一. 前言:你的 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)
purelib 和 platlib 在許多環境會相同,但前者代表純 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_64、macosx-15.5-arm64 或 win-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_venv 是 True,sys.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;
purelib與platlib差異。
應改成:
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 環境時非常可靠的標準庫工具。
今天最值得帶走的幾件事:
- 用
get_default_scheme()理解目前 installation layout; - 用
get_path()或get_paths()查 stdlib、purelib、platlib、scripts 與 include; - 用
get_config_var()檢查 compiler、ABI 與 extension 設定; - 用
get_platform()描述 build platform,但不要把它誤當完整 wheel tag; - 搭配
sys.prefix與sys.base_prefix診斷 virtual environment; - 遇到跨環境 bug 時,產生結構化報告,不要靠猜。
下次有人說「它在我電腦上可以跑」,先別急著翻白眼。
請他跑一次 python -m sysconfig。證據比氣氛可靠,拍拍君蓋章。