import site
import sys
import json
import os
import platform
import struct
import sysconfig
def format_full_version(info):
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
version += kind[0] + str(info.serial)
return version
if sys.version_info[0] < 3:
print(
json.dumps(
{
"result": "error",
"kind": "unsupported_python_version",
"python_version": format_full_version(sys.version_info),
}
)
)
sys.exit(0)
if hasattr(sys, "implementation"):
implementation_name = sys.implementation.name
if implementation_name == "graalpy":
import re
implementation_version = re.sub(
r"graalpy(\d)(\d+)(?:dev[\da-f]+)?-\d+",
r"\1.\2",
sys.implementation.cache_tag,
)
elif implementation_name == "pyston":
import re
implementation_version = re.sub(
r"pyston-(\d)(\d+)",
r"\1.\2",
sys.implementation.cache_tag,
)
else:
implementation_version = format_full_version(sys.implementation.version)
else:
implementation_version = "0"
implementation_name = ""
python_full_version = platform.python_version()
if len(python_full_version) > 0 and python_full_version[-1] == "+":
python_full_version = python_full_version[:-1]
def _running_under_venv() -> bool:
return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
def _running_under_legacy_virtualenv() -> bool:
return hasattr(sys, "real_prefix")
def running_under_virtualenv() -> bool:
return _running_under_venv() or _running_under_legacy_virtualenv()
def get_major_minor_version() -> str:
return "{}.{}".format(*sys.version_info)
def get_virtualenv():
scheme_names = sysconfig.get_scheme_names()
if "venv" in scheme_names:
sysconfig_scheme = "venv"
elif sys.version_info[:2] == (3, 10) and "deb_system" in scheme_names:
sysconfig_scheme = "posix_prefix"
else:
sysconfig_scheme = None
if sysconfig_scheme:
import re
sysconfig_paths = {
i: sysconfig.get_path(i, expand=False, scheme=sysconfig_scheme)
for i in sysconfig.get_path_names()
}
config_var_keys = set()
conf_var_re = re.compile(r"\{\w+}")
for element in sysconfig_paths.values():
for k in conf_var_re.findall(element):
config_var_keys.add(k[1:-1])
config_var_keys.add("PYTHONFRAMEWORK")
sysconfig_vars = {i: sysconfig.get_config_var(i or "") for i in config_var_keys}
prefix = os.path.abspath(sys.prefix)
base_prefix = os.path.abspath(sys.base_prefix)
base_exec_prefix = os.path.abspath(sys.base_exec_prefix)
exec_prefix = os.path.abspath(sys.exec_prefix)
prefixes = prefix, exec_prefix, base_prefix, base_exec_prefix
sysconfig_vars.update(
{k: "" if v in prefixes else v for k, v in sysconfig_vars.items()}
)
def expand_path(path: str) -> str:
return path.format(**sysconfig_vars).replace("/", os.sep).lstrip(os.sep)
return {
"purelib": expand_path(sysconfig_paths["purelib"]),
"platlib": expand_path(sysconfig_paths["platlib"]),
"include": os.path.join(
"include", "site", f"python{get_major_minor_version()}"
),
"scripts": expand_path(sysconfig_paths["scripts"]),
"data": expand_path(sysconfig_paths["data"]),
}
else:
try:
__import__("_distutils_hack").remove_shim()
except (ImportError, AttributeError):
pass
import warnings
with warnings.catch_warnings(): warnings.simplefilter("ignore")
from distutils import dist
from distutils.command.install import SCHEME_KEYS
d = dist.Distribution({"script_args": "--no-user-cfg"})
if hasattr(sys, "_framework"):
sys._framework = None
with warnings.catch_warnings():
warnings.simplefilter("ignore")
i = d.get_command_obj("install", create=True)
i.prefix = os.sep
i.finalize_options()
distutils_paths = {
key: (getattr(i, f"install_{key}")[1:]).lstrip(os.sep)
for key in SCHEME_KEYS
}
return {
"purelib": distutils_paths["purelib"],
"platlib": distutils_paths["platlib"],
"include": os.path.join(
"include", "site", f"python{get_major_minor_version()}"
),
"scripts": distutils_paths["scripts"],
"data": distutils_paths["data"],
}
def get_scheme(use_sysconfig_scheme: bool):
def get_sysconfig_scheme():
def is_osx_framework() -> bool:
return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))
_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names())
_PREFERRED_SCHEME_API = getattr(sysconfig, "get_preferred_scheme", None)
def _should_use_osx_framework_prefix() -> bool:
return (
"osx_framework_library" in _AVAILABLE_SCHEMES
and not running_under_virtualenv()
and is_osx_framework()
)
def _infer_prefix() -> str:
if _PREFERRED_SCHEME_API:
return _PREFERRED_SCHEME_API("prefix")
if _should_use_osx_framework_prefix():
return "osx_framework_library"
implementation_suffixed = f"{sys.implementation.name}_{os.name}"
if implementation_suffixed in _AVAILABLE_SCHEMES:
return implementation_suffixed
if sys.implementation.name in _AVAILABLE_SCHEMES:
return sys.implementation.name
suffixed = f"{os.name}_prefix"
if suffixed in _AVAILABLE_SCHEMES:
return suffixed
if os.name in _AVAILABLE_SCHEMES: return os.name
return "posix_prefix"
scheme_name = _infer_prefix()
paths = sysconfig.get_paths(scheme=scheme_name)
if running_under_virtualenv():
python_xy = f"python{get_major_minor_version()}"
paths["include"] = os.path.join(sys.prefix, "include", "site", python_xy)
return {
"platlib": paths["platlib"],
"purelib": paths["purelib"],
"include": paths["include"],
"scripts": paths["scripts"],
"data": paths["data"],
}
def get_distutils_scheme():
import warnings
with warnings.catch_warnings(): warnings.simplefilter("ignore")
from distutils.dist import Distribution
dist_args = {}
d = Distribution(dist_args)
try:
d.parse_config_files()
except UnicodeDecodeError:
pass
with warnings.catch_warnings():
warnings.simplefilter("ignore")
i = d.get_command_obj("install", create=True)
i.finalize_options()
scheme = {}
for key in ("purelib", "platlib", "headers", "scripts", "data"):
scheme[key] = getattr(i, "install_" + key)
if "install_lib" in d.get_option_dict("install"):
scheme.update({"purelib": i.install_lib, "platlib": i.install_lib})
if running_under_virtualenv():
scheme["headers"] = os.path.join(
i.prefix,
"include",
"site",
f"python{get_major_minor_version()}",
"UNKNOWN",
)
return {
"platlib": scheme["platlib"],
"purelib": scheme["purelib"],
"include": os.path.dirname(scheme["headers"]),
"scripts": scheme["scripts"],
"data": scheme["data"],
}
if use_sysconfig_scheme:
return get_sysconfig_scheme()
else:
return get_distutils_scheme()
def get_operating_system_and_architecture():
platform_info = sysconfig.get_platform().split("-", 1)
if len(platform_info) == 1:
if platform_info[0] == "win32":
operating_system, version_arch = "win", "i386"
else:
operating_system, version_arch = platform_info[0], ""
else:
[operating_system, version_arch] = platform_info
if "-" in version_arch:
version, architecture = version_arch.rsplit("-", 1)
else:
version = None
architecture = version_arch
if sys.version_info < (3, 6):
print(
json.dumps(
{
"result": "error",
"kind": "unsupported_python_version",
"python_version": format_full_version(sys.version_info),
}
)
)
sys.exit(0)
if operating_system == "linux":
from .packaging._manylinux import _get_glibc_version
from .packaging._musllinux import _get_musl_version
if struct.calcsize("P") == 4:
if architecture == "x86_64":
architecture = "i686"
elif architecture == "aarch64":
architecture = "armv8l"
musl_version = _get_musl_version(sys.executable)
glibc_version = _get_glibc_version()
if musl_version:
operating_system = {
"name": "musllinux",
"major": musl_version[0],
"minor": musl_version[1],
}
elif glibc_version != (-1, -1):
operating_system = {
"name": "manylinux",
"major": glibc_version[0],
"minor": glibc_version[1],
}
elif hasattr(sys, "getandroidapilevel"):
operating_system = {
"name": "android",
"api_level": sys.getandroidapilevel(),
}
else:
print(json.dumps({"result": "error", "kind": "libc_not_found"}))
sys.exit(0)
elif operating_system == "win":
operating_system = {
"name": "windows",
}
elif operating_system == "macosx":
version, _, architecture = platform.mac_ver()
if not version or not architecture:
print(json.dumps({"result": "error", "kind": "broken_mac_ver"}))
sys.exit(0)
is_32bit = struct.calcsize("P") == 4
if is_32bit:
if architecture.startswith("ppc"):
architecture = "ppc"
else:
architecture = "i386"
version = version.split(".")
operating_system = {
"name": "macos",
"major": int(version[0]),
"minor": int(version[1]),
}
elif operating_system == "ios":
ios_ver = platform.ios_ver()
version = ios_ver.release.split(".")
operating_system = {
"name": "ios",
"major": int(version[0]),
"minor": int(version[1]),
"simulator": ios_ver.is_simulator,
}
[_version, architecture, _platform] = version_arch.split("-")
elif operating_system == "emscripten":
pyodide_abi_version = sysconfig.get_config_var("PYODIDE_ABI_VERSION")
if not pyodide_abi_version:
print(
json.dumps(
{
"result": "error",
"kind": "emscripten_not_pyodide",
}
)
)
sys.exit(0)
version = pyodide_abi_version.split("_")
operating_system = {
"name": "pyodide",
"major": int(version[0]),
"minor": int(version[1]),
}
elif operating_system == "android":
android_abi_to_arch = {
"arm64_v8a": "aarch64",
"armeabi_v7a": "armv7l",
"x86": "i686",
"x86_64": "x86_64",
}
architecture = android_abi_to_arch.get(architecture, architecture)
operating_system = {
"name": "android",
"api_level": sys.getandroidapilevel(),
}
elif operating_system in [
"freebsd",
"netbsd",
"openbsd",
"dragonfly",
"illumos",
"haiku",
]:
operating_system = {
"name": operating_system,
"release": version,
}
else:
print(
json.dumps(
{
"result": "error",
"kind": "unknown_operating_system",
"operating_system": operating_system,
}
)
)
sys.exit(0)
return {"os": operating_system, "arch": architecture}
def main() -> None:
markers = {
"implementation_name": implementation_name,
"implementation_version": implementation_version,
"os_name": os.name,
"platform_machine": platform.machine(),
"platform_python_implementation": platform.python_implementation(),
"platform_release": platform.release(),
"platform_system": platform.system(),
"platform_version": platform.version(),
"python_full_version": python_full_version,
"python_version": ".".join(platform.python_version_tuple()[:2]),
"sys_platform": sys.platform,
}
os_and_arch = get_operating_system_and_architecture()
manylinux_compatible = False
if os_and_arch["os"]["name"] == "manylinux":
from .packaging._manylinux import _get_glibc_version, _is_compatible
manylinux_compatible = _is_compatible(
arch=os_and_arch["arch"], version=_get_glibc_version()
)
elif os_and_arch["os"]["name"] == "musllinux":
manylinux_compatible = True
use_sysconfig_scheme = bool(
getattr(sysconfig, "_PIP_USE_SYSCONFIG", sys.version_info >= (3, 10))
)
if not use_sysconfig_scheme:
try:
try:
__import__("_distutils_hack").remove_shim()
except (ImportError, AttributeError):
pass
import distutils.dist except ImportError:
print(
json.dumps(
{
"result": "error",
"kind": "missing_required_distutils",
"python_major": sys.version_info[0],
"python_minor": sys.version_info[1],
}
)
)
sys.exit(0)
interpreter_info = {
"result": "success",
"markers": markers,
"sys_base_prefix": sys.base_prefix,
"sys_base_exec_prefix": sys.base_exec_prefix,
"sys_prefix": sys.prefix,
"sys_base_executable": getattr(sys, "_base_executable", None),
"sys_executable": sys.executable,
"sys_path": sys.path[1:],
"site_packages": site.getsitepackages(),
"stdlib": sysconfig.get_path("stdlib"),
"standalone": (
sysconfig.get_config_var("prefix") == "/install"
or bool(sysconfig.get_config_var("PYTHON_BUILD_STANDALONE"))
),
"scheme": get_scheme(use_sysconfig_scheme),
"virtualenv": get_virtualenv(),
"platform": os_and_arch,
"manylinux_compatible": manylinux_compatible,
"gil_disabled": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
"debug_enabled": bool(sysconfig.get_config_var("Py_DEBUG")),
"pointer_size": "64" if sys.maxsize > 2**32 else "32",
}
print(json.dumps(interpreter_info))
if __name__ == "__main__":
main()