-
-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy path_pythoninfo.py
More file actions
160 lines (142 loc) · 5.19 KB
/
_pythoninfo.py
File metadata and controls
160 lines (142 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# A utility library for getting information about a Python executable.
#
# This may be used as a script.
import importlib.util
import json
import os
import os.path
import sys
import sysconfig
INFO = {
# sys
"executable (sys)": "sys.executable",
"executable (sys;realpath)": "executable_realpath",
"prefix (sys)": "sys.prefix",
"exec_prefix (sys)": "sys.exec_prefix",
"stdlib_dir (sys)": "sys._stdlib_dir",
"base_executable (sys)": "sys._base_executable",
"base_prefix (sys)": "sys.base_prefix",
"base_exec_prefix (sys)": "sys.base_exec_prefix",
"version_str (sys)": "sys.version",
"version_info (sys)": "sys.version_info",
"hexversion (sys)": "sys.hexversion",
"api_version (sys)": "sys.api_version",
"implementation_name (sys)": "sys.implementation.name",
"implementation_version (sys)": "sys.implementation.version",
"platform (sys)": "sys.platform",
# sysconfig
"stdlib_dir (sysconfig)": "sysconfig.paths.stdlib",
"is_dev (sysconfig)": "sysconfig.is_python_build",
# other
"base_executable": "base_executable",
"stdlib_dir": "stdlib_dir",
"pyc_magic_number": "pyc_magic_number",
"is_venv": "is_venv",
}
def get_info(python=sys.executable):
"""Return an object with details about the given Python executable.
Most of the details are grouped by their source.
By default the current Python is used.
"""
if python and python != sys.executable:
# Run _pythoninfo.py to get the raw info.
import subprocess
argv = [python, __file__]
try:
text = subprocess.check_output(argv, encoding="utf-8")
except subprocess.CalledProcessError:
raise Exception(f"could not get info for {python or sys.executable}")
data = _unjsonify_info(text)
else:
data = _get_current_info()
return _build_info(data)
def _build_info(data):
# Map the data into a new types.SimpleNamespace object.
info = type(sys.implementation)()
for key, value in data.items():
try:
field = INFO[key]
except KeyError:
raise NotImplementedError(repr(key))
parent = info
while "." in field:
pname, _, field = field.partition(".")
try:
parent = getattr(parent, pname)
except AttributeError:
setattr(parent, pname, type(sys.implementation)())
parent = getattr(parent, pname)
setattr(parent, field, value)
return info
def _get_current_info():
is_venv = sys.prefix != sys.base_prefix
base_executable = getattr(sys, "_base_executable", None)
if is_venv:
# XXX There is probably a bug related to venv, since
# sys._base_executable should be different.
if base_executable == sys.executable:
# Indicate that we don't know.
base_executable = None
elif not base_executable:
base_executable = sys.executable
info = {
# locations
"executable (sys)": sys.executable,
"executable (sys;realpath)": os.path.realpath(sys.executable),
"prefix (sys)": sys.prefix,
"exec_prefix (sys)": sys.exec_prefix,
"stdlib_dir": os.path.dirname(os.__file__),
"stdlib_dir (sys)": getattr(sys, "_stdlib_dir", None),
"stdlib_dir (sysconfig)": (
sysconfig.get_path("stdlib")
if "stdlib" in sysconfig.get_path_names()
else None
),
# base locations
"base_executable": base_executable,
"base_executable (sys)": getattr(sys, "_base_executable", None),
"base_prefix (sys)": sys.base_prefix,
"base_exec_prefix (sys)": sys.base_exec_prefix,
# version
"version_str (sys)": sys.version,
"version_info (sys)": sys.version_info,
"hexversion (sys)": sys.hexversion,
"api_version (sys)": sys.api_version,
# implementation
"implementation_name (sys)": sys.implementation.name,
"implementation_version (sys)": sys.implementation.version,
# build
"is_dev (sysconfig)": sysconfig.is_python_build(),
# host
"platform (sys)": sys.platform,
# virtual envs
"is_venv": is_venv,
# import system
# importlib.util.MAGIC_NUMBER has been around since 3.5.
"pyc_magic_number": importlib.util.MAGIC_NUMBER,
}
return info
def _jsonify_info(info):
data = dict(info)
if isinstance(data["pyc_magic_number"], bytes):
data["pyc_magic_number"] = data["pyc_magic_number"].hex()
return data
def _unjsonify_info(data):
if isinstance(data, str):
data = json.loads(data)
info = dict(data)
for key in ("version_info (sys)", "implementation_version (sys)"):
if isinstance(info[key], list):
# We would use type(sys.version_info) if it allowed it.
info[key] = tuple(info[key])
for key in ("pyc_magic_number",):
if isinstance(info[key], str):
info[key] = bytes.fromhex(data[key])
return info
#######################################
# use as a script
if __name__ == "__main__":
info = _get_current_info()
data = _jsonify_info(info)
json.dump(data, sys.stdout, indent=4)
print()