快轉到主要內容
  1. 教學文章/

Python sysconfig 實戰:安裝路徑、編譯資訊與環境診斷

·6 分鐘· loading · loading · ·
Python Sysconfig Standard-Library Packaging Virtualenv Developer-Tools
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
Python 學習 - 本文屬於一個選集。
§ 99: 本文

featured

一. 前言:你的 Python 到底住在哪裡?
#

「我明明安裝了套件,為什麼 import 不到?」

「同一支腳本在 macOS 可以跑,進 Docker 就找不到 header?」

「CI 裡執行的 python,真的是剛剛安裝依賴的那一個嗎?」

這些問題看起來不一樣,底層常常都指向同一件事:你對目前執行中的 Python 環境不夠了解。

硬寫 /usr/local/lib/python3.12/site-packages 當然很快,但 Windows、虛擬環境、Homebrew Python、系統 Python 和自編譯 Python 的目錄布局都可能不同。

這時候,標準庫的 sysconfig 就像 Python 自己附的環境 X 光機。

它可以告訴你:

  • 目前採用哪一種 installation scheme;
  • 標準庫、第三方套件、scripts 和 C headers 在哪裡;
  • Python 是用哪些編譯參數建出來的;
  • 當前作業系統與 CPU 平台識別字;
  • 你是否正在 Python 原始碼 build tree 裡執行。

這篇會把這些資訊整理成實際可用的查詢方式,最後再做一個可以貼進 issue 的環境診斷報告。

二. 安裝:標準庫直接用
#

2.1 不需要 pip install
#

sysconfig 從 Python 3.2 起就是標準庫的一部分:

import sysconfig

print(sysconfig.get_python_version())
print(sysconfig.get_platform())

所以請不要執行:

pip install sysconfig

你需要確認的,是目前到底呼叫了哪個 Python:

python --version
python -c "import sys; print(sys.executable)"

2.2 先試內建 CLI
#

還不想寫程式時,可以先跑:

python -m sysconfig

它會列出 platform、Python 版本、current scheme、安裝路徑與 configuration variables。輸出很長,除錯時可用 python -m sysconfig > sysconfig-report.txt 保存;issue 裡多一份環境報告,通常能少來回問三輪。

三. Installation scheme:路徑不是憑空出現的
#

3.1 什麼是 scheme?
#

Python 不會只保存一個「套件安裝目錄」。

它用 installation scheme 描述一整組互相關聯的路徑,例如:

  • stdlib:不依賴平台的標準庫;
  • platstdlib:平台相關的標準庫;
  • purelib:純 Python 第三方套件;
  • platlib:包含平台相關內容的第三方套件;
  • include:Python C API headers;
  • platinclude:平台相關 headers;
  • scripts:命令列 entry-point scripts;
  • data:其他資料檔的基準目錄。

先看看目前支援哪些名稱:

import sysconfig

print("schemes:")
for name in sysconfig.get_scheme_names():
    print(" -", name)

print("path names:")
for name in sysconfig.get_path_names():
    print(" -", name)

不同平台的 scheme 清單可能不同,所以不要拿自己電腦的結果當成宇宙常數。

3.2 查詢目前預設 scheme
#

Python 3.10 以上可以使用:

import sysconfig

scheme = sysconfig.get_default_scheme()
print(scheme)

常見結果可能是:

posix_prefix

或在虛擬環境裡看到:

venv

從 Python 3.11 起,在 virtual environment 裡,get_default_scheme() 會回傳 venv scheme。

實務上不要用 os.name == "posix" 自己猜 scheme;讓正在執行的 interpreter 回答,才會尊重發行版與環境的配置。

3.3 一次取得整組路徑
#

最常用的入口是 get_paths()

from pprint import pprint
import sysconfig

paths = sysconfig.get_paths()
pprint(paths)

你自己的輸出會依平台、發行方式與 virtual environment 改變,這正是使用查詢 API 而不寫死路徑的理由。

四. 精準取得 stdlib、site-packages 與 scripts
#

4.1 用 get_path() 查單一路徑
#

如果只需要一個值,不必把整張表搬出來:

import sysconfig

stdlib = sysconfig.get_path("stdlib")
purelib = sysconfig.get_path("purelib")
platlib = sysconfig.get_path("platlib")
scripts = sysconfig.get_path("scripts")

print("stdlib:", stdlib)
print("purelib:", purelib)
print("platlib:", platlib)
print("scripts:", scripts)

purelibplatlib 在許多環境會相同,但前者代表純 Python 套件,後者代表 platform-specific 套件;工具仍應保留兩者語意。

你也可以明確指定 scheme=;可攜式程式要先用 get_scheme_names() 檢查名稱是否存在。若要管理 app config、cache、data 與 logs,請改看 platformdirs 實戰,因為那是不同層次的路徑問題。

五. Configuration variables:看看 Python 怎麼被建出來
#

5.1 查單一變數
#

get_config_var() 會讀取目前 Python build 的設定:

import sysconfig

names = [
    "CC",
    "CFLAGS",
    "EXT_SUFFIX",
    "SOABI",
    "Py_DEBUG",
    "Py_ENABLE_SHARED",
]

for name in names:
    print(f"{name:>16} = {sysconfig.get_config_var(name)!r}")

這些欄位特別適合診斷 C extension build:

  • CC:預設 C compiler;
  • CFLAGS:編譯 Python 時的部分 flags;
  • EXT_SUFFIX:extension module 的檔名後綴;
  • SOABI:shared object 的 ABI 標記;
  • Py_DEBUG:是否為 debug build;
  • Py_ENABLE_SHARED:是否啟用 shared Python library。

某些變數在某個平台可能不存在,此時會得到 None。所以請寫:

value = sysconfig.get_config_var("SOME_OPTION")
if value is None:
    print("SOME_OPTION is unavailable on this Python build")

不要把 None 當成字串直接塞進 shell command。

get_config_vars("CC", "CXX") 可以依序查多個值;不帶參數則取得完整 dictionary。完整設定很多,正式 log 最好只挑需要的欄位,避免報告又長又難 diff。

六. 平台與虛擬環境診斷
#

6.1 get_platform() 不是 sys.platform
#

sysconfig.get_platform() 會產生較適合區分 build 與 platform-specific distribution 的識別字:

import platform
import sys
import sysconfig

print("sys.platform:", sys.platform)
print("sysconfig platform:", sysconfig.get_platform())
print("machine:", platform.machine())

結果可能像 linux-x86_64macosx-15.5-arm64win-amd64,但它不是完整的 wheel compatibility tag。判斷 wheel 能否安裝還要考慮 Python implementation 與 ABI,應交給 packaging 工具鏈,不要只比較一個字串。

6.2 判斷是否在 virtual environment
#

sysconfig 負責 layout;搭配 sys.prefix 可以看出目前環境關係:

import sys
import sysconfig

in_venv = sys.prefix != sys.base_prefix

print("executable:", sys.executable)
print("prefix:", sys.prefix)
print("base_prefix:", sys.base_prefix)
print("in_venv:", in_venv)
print("scheme:", sysconfig.get_default_scheme())
print("purelib:", sysconfig.get_path("purelib"))

in_venvTruesys.prefix 通常指向虛擬環境,而 sys.base_prefix 指向建立它的 base Python。

這段輸出非常適合回答:「pip 到底裝去哪裡?」

再搭配:

python -m pip --version

你就能同時看到 pip module 的位置與它綁定的 Python。

請優先使用 python -m pip,不要只打裸的 pip。後者可能來自 PATH 裡另一個環境。

七. 實戰:產生一份可分享的環境報告
#

7.1 報告內容要精簡而穩定
#

把幾個核心 API 組起來:

from __future__ import annotations

import json
import platform
import sys
import sysconfig
from typing import Any


def build_report() -> dict[str, Any]:
    config_names = (
        "CC",
        "CFLAGS",
        "EXT_SUFFIX",
        "SOABI",
        "Py_DEBUG",
        "Py_ENABLE_SHARED",
    )

    return {
        "python": {
            "version": platform.python_version(),
            "implementation": platform.python_implementation(),
            "executable": sys.executable,
            "prefix": sys.prefix,
            "base_prefix": sys.base_prefix,
            "in_venv": sys.prefix != sys.base_prefix,
        },
        "sysconfig": {
            "python_version": sysconfig.get_python_version(),
            "platform": sysconfig.get_platform(),
            "default_scheme": sysconfig.get_default_scheme(),
            "paths": sysconfig.get_paths(),
            "variables": {
                name: sysconfig.get_config_var(name)
                for name in config_names
            },
        },
    }


if __name__ == "__main__":
    print(json.dumps(build_report(), indent=2, ensure_ascii=False))

存成 python_env_report.py 後執行:

python python_env_report.py > python-env.json

JSON 的好處是:

  • 人可以讀;
  • issue bot 可以解析;
  • CI artifacts 可以保存;
  • 兩個環境可以直接 diff。

在本機與 CI 各產生一份 JSON,再用 diff -u local.json ci.json 比較,常能立刻發現 executable、scheme、purelib、SOABI 或 CPU architecture 不一致。公開報告前要先檢查路徑裡的使用者名稱與內部掛載點,也不要額外蒐集可能含有 token 的 environment variables。

八. 常見誤區與設計建議
#

8.1 不要手動拼 site-packages
#

這段看似合理:

# 不建議
site_packages = f"/usr/local/lib/python{sys.version_info.major}.{sys.version_info.minor}/site-packages"

但它忽略了:

  • Windows layout;
  • virtual environment prefix;
  • framework build;
  • distributor 自訂 scheme;
  • purelibplatlib 差異。

應改成:

import sysconfig

site_packages = sysconfig.get_path("purelib")

8.2 不要用 sysconfig 找 package data
#

sysconfig 回答的是 Python installation layout,不是某個套件內資源的實際位置。

套件模板、預設 JSON、圖示等 package data,應使用 importlib.resources,不要從 purelib 後面手動拼 package 名稱。

8.3 選對工具
#

可以把分工記成:

  • sysconfig:這個 Python 怎麼配置、路徑怎麼布局;
  • importlib.metadata:這個環境安裝了哪些 distributions;
  • importlib.resources:某個 importable package 帶了哪些資源;
  • platformdirs:你的 app config/cache/data 應該放哪裡。

九. 結語
#

sysconfig 平常很低調,卻是診斷 Python 環境時非常可靠的標準庫工具。

今天最值得帶走的幾件事:

  1. get_default_scheme() 理解目前 installation layout;
  2. get_path()get_paths() 查 stdlib、purelib、platlib、scripts 與 include;
  3. get_config_var() 檢查 compiler、ABI 與 extension 設定;
  4. get_platform() 描述 build platform,但不要把它誤當完整 wheel tag;
  5. 搭配 sys.prefixsys.base_prefix 診斷 virtual environment;
  6. 遇到跨環境 bug 時,產生結構化報告,不要靠猜。

下次有人說「它在我電腦上可以跑」,先別急著翻白眼。

請他跑一次 python -m sysconfig。證據比氣氛可靠,拍拍君蓋章。

延伸閱讀
#

Python 學習 - 本文屬於一個選集。
§ 99: 本文

相關文章

Python uv build/publish 實戰:從 wheel 到 private package workflow
·11 分鐘· loading · loading
Python Uv Packaging Wheel PyPI Private-Package Developer-Tools
Python socket 實戰:TCP client/server、timeout 與簡易通訊協定
·8 分鐘· loading · loading
Python Socket TCP Networking Standard-Library Developer-Tools
Python shutil 實戰:檔案複製、搬移、壓縮與安全清理
·7 分鐘· loading · loading
Python Shutil Filesystem Automation Standard-Library Developer-Tools
Python inspect 實戰:看懂函式簽名、物件結構與開發工具自動化
·6 分鐘· loading · loading
Python Inspect Introspection Standard-Library Developer-Tools
Python tempfile 實戰:安全建立暫存檔案、目錄與測試資料
·9 分鐘· loading · loading
Python Tempfile Filesystem Testing Standard-Library Developer-Tools
Python importlib.metadata 實戰:套件版本、依賴資訊與外掛探索
·7 分鐘· loading · loading
Python Importlib.metadata Packaging Metadata Entry-Points Plugins