1 """Access to Python's configuration information."""
2
3 import os
4 import sys
5 import threading
6 from os.path import realpath
7
8 __all__ = [
9 'get_config_h_filename',
10 'get_config_var',
11 'get_config_vars',
12 'get_makefile_filename',
13 'get_path',
14 'get_path_names',
15 'get_paths',
16 'get_platform',
17 'get_python_version',
18 'get_scheme_names',
19 'parse_config_h',
20 ]
21
22 # Keys for get_config_var() that are never converted to Python integers.
23 _ALWAYS_STR = {
24 'MACOSX_DEPLOYMENT_TARGET',
25 }
26
27 _INSTALL_SCHEMES = {
28 'posix_prefix': {
29 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
30 'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
31 'purelib': '{base}/lib/python{py_version_short}/site-packages',
32 'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
33 'include':
34 '{installed_base}/include/python{py_version_short}{abiflags}',
35 'platinclude':
36 '{installed_platbase}/include/python{py_version_short}{abiflags}',
37 'scripts': '{base}/bin',
38 'data': '{base}',
39 },
40 'posix_home': {
41 'stdlib': '{installed_base}/lib/python',
42 'platstdlib': '{base}/lib/python',
43 'purelib': '{base}/lib/python',
44 'platlib': '{base}/lib/python',
45 'include': '{installed_base}/include/python',
46 'platinclude': '{installed_base}/include/python',
47 'scripts': '{base}/bin',
48 'data': '{base}',
49 },
50 'nt': {
51 'stdlib': '{installed_base}/Lib',
52 'platstdlib': '{base}/Lib',
53 'purelib': '{base}/Lib/site-packages',
54 'platlib': '{base}/Lib/site-packages',
55 'include': '{installed_base}/Include',
56 'platinclude': '{installed_base}/Include',
57 'scripts': '{base}/Scripts',
58 'data': '{base}',
59 },
60 # Downstream distributors can overwrite the default install scheme.
61 # This is done to support downstream modifications where distributors change
62 # the installation layout (eg. different site-packages directory).
63 # So, distributors will change the default scheme to one that correctly
64 # represents their layout.
65 # This presents an issue for projects/people that need to bootstrap virtual
66 # environments, like virtualenv. As distributors might now be customizing
67 # the default install scheme, there is no guarantee that the information
68 # returned by sysconfig.get_default_scheme/get_paths is correct for
69 # a virtual environment, the only guarantee we have is that it is correct
70 # for the *current* environment. When bootstrapping a virtual environment,
71 # we need to know its layout, so that we can place the files in the
72 # correct locations.
73 # The "*_venv" install scheme is a scheme to bootstrap virtual environments,
74 # essentially identical to the default posix_prefix/nt schemes.
75 # Downstream distributors who patch posix_prefix/nt scheme are encouraged to
76 # leave the following schemes unchanged
77 'posix_venv': {
78 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
79 'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
80 'purelib': '{base}/lib/python{py_version_short}/site-packages',
81 'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
82 'include':
83 '{installed_base}/include/python{py_version_short}{abiflags}',
84 'platinclude':
85 '{installed_platbase}/include/python{py_version_short}{abiflags}',
86 'scripts': '{base}/bin',
87 'data': '{base}',
88 },
89 'nt_venv': {
90 'stdlib': '{installed_base}/Lib',
91 'platstdlib': '{base}/Lib',
92 'purelib': '{base}/Lib/site-packages',
93 'platlib': '{base}/Lib/site-packages',
94 'include': '{installed_base}/Include',
95 'platinclude': '{installed_base}/Include',
96 'scripts': '{base}/Scripts',
97 'data': '{base}',
98 },
99 }
100
101 # For the OS-native venv scheme, we essentially provide an alias:
102 if os.name == 'nt':
103 _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv']
104 else:
105 _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv']
106
107
108 # NOTE: site.py has copy of this function.
109 # Sync it when modify this function.
110 def _getuserbase():
111 env_base = os.environ.get("PYTHONUSERBASE", None)
112 if env_base:
113 return env_base
114
115 # Emscripten, VxWorks, and WASI have no home directories
116 if sys.platform in {"emscripten", "vxworks", "wasi"}:
117 return None
118
119 def joinuser(*args):
120 return os.path.expanduser(os.path.join(*args))
121
122 if os.name == "nt":
123 base = os.environ.get("APPDATA") or "~"
124 return joinuser(base, "Python")
125
126 if sys.platform == "darwin" and sys._framework:
127 return joinuser("~", "Library", sys._framework,
128 f"{sys.version_info[0]}.{sys.version_info[1]}")
129
130 return joinuser("~", ".local")
131
132 _HAS_USER_BASE = (_getuserbase() is not None)
133
134 if _HAS_USER_BASE:
135 _INSTALL_SCHEMES |= {
136 # NOTE: When modifying "purelib" scheme, update site._get_path() too.
137 'nt_user': {
138 'stdlib': '{userbase}/Python{py_version_nodot_plat}',
139 'platstdlib': '{userbase}/Python{py_version_nodot_plat}',
140 'purelib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
141 'platlib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
142 'include': '{userbase}/Python{py_version_nodot_plat}/Include',
143 'scripts': '{userbase}/Python{py_version_nodot_plat}/Scripts',
144 'data': '{userbase}',
145 },
146 'posix_user': {
147 'stdlib': '{userbase}/{platlibdir}/python{py_version_short}',
148 'platstdlib': '{userbase}/{platlibdir}/python{py_version_short}',
149 'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
150 'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
151 'include': '{userbase}/include/python{py_version_short}',
152 'scripts': '{userbase}/bin',
153 'data': '{userbase}',
154 },
155 'osx_framework_user': {
156 'stdlib': '{userbase}/lib/python',
157 'platstdlib': '{userbase}/lib/python',
158 'purelib': '{userbase}/lib/python/site-packages',
159 'platlib': '{userbase}/lib/python/site-packages',
160 'include': '{userbase}/include/python{py_version_short}',
161 'scripts': '{userbase}/bin',
162 'data': '{userbase}',
163 },
164 }
165
166 _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
167 'scripts', 'data')
168
169 _PY_VERSION = sys.version.split()[0]
170 _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
171 _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
172 _PREFIX = os.path.normpath(sys.prefix)
173 _BASE_PREFIX = os.path.normpath(sys.base_prefix)
174 _EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
175 _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
176 # Mutex guarding initialization of _CONFIG_VARS.
177 _CONFIG_VARS_LOCK = threading.RLock()
178 _CONFIG_VARS = None
179 # True iff _CONFIG_VARS has been fully initialized.
180 _CONFIG_VARS_INITIALIZED = False
181 _USER_BASE = None
182
183 # Regexes needed for parsing Makefile (and similar syntaxes,
184 # like old-style Setup files).
185 _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)"
186 _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
187 _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
188
189
190 def _safe_realpath(path):
191 try:
192 return realpath(path)
193 except OSError:
194 return path
195
196 if sys.executable:
197 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))
198 else:
199 # sys.executable can be empty if argv[0] has been changed and Python is
200 # unable to retrieve the real program name
201 _PROJECT_BASE = _safe_realpath(os.getcwd())
202
203 # In a virtual environment, `sys._home` gives us the target directory
204 # `_PROJECT_BASE` for the executable that created it when the virtual
205 # python is an actual executable ('venv --copies' or Windows).
206 _sys_home = getattr(sys, '_home', None)
207 if _sys_home:
208 _PROJECT_BASE = _sys_home
209
210 if os.name == 'nt':
211 # In a source build, the executable is in a subdirectory of the root
212 # that we want (<root>\PCbuild\<platname>).
213 # `_BASE_PREFIX` is used as the base installation is where the source
214 # will be. The realpath is needed to prevent mount point confusion
215 # that can occur with just string comparisons.
216 if _safe_realpath(_PROJECT_BASE).startswith(
217 _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')):
218 _PROJECT_BASE = _BASE_PREFIX
219
220 # set for cross builds
221 if "_PYTHON_PROJECT_BASE" in os.environ:
222 _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"])
223
224 def is_python_build(check_home=None):
225 if check_home is not None:
226 import warnings
227 warnings.warn("check_home argument is deprecated and ignored.",
228 DeprecationWarning, stacklevel=2)
229 for fn in ("Setup", "Setup.local"):
230 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
231 return True
232 return False
233
234 _PYTHON_BUILD = is_python_build()
235
236 if _PYTHON_BUILD:
237 for scheme in ('posix_prefix', 'posix_home'):
238 # On POSIX-y platforms, Python will:
239 # - Build from .h files in 'headers' (which is only added to the
240 # scheme when building CPython)
241 # - Install .h files to 'include'
242 scheme = _INSTALL_SCHEMES[scheme]
243 scheme['headers'] = scheme['include']
244 scheme['include'] = '{srcdir}/Include'
245 scheme['platinclude'] = '{projectbase}/.'
246 del scheme
247
248
249 def _subst_vars(s, local_vars):
250 try:
251 return s.format(**local_vars)
252 except KeyError as var:
253 try:
254 return s.format(**os.environ)
255 except KeyError:
256 raise AttributeError(f'{var}') from None
257
258 def _extend_dict(target_dict, other_dict):
259 target_keys = target_dict.keys()
260 for key, value in other_dict.items():
261 if key in target_keys:
262 continue
263 target_dict[key] = value
264
265
266 def _expand_vars(scheme, vars):
267 res = {}
268 if vars is None:
269 vars = {}
270 _extend_dict(vars, get_config_vars())
271 if os.name == 'nt':
272 # On Windows we want to substitute 'lib' for schemes rather
273 # than the native value (without modifying vars, in case it
274 # was passed in)
275 vars = vars | {'platlibdir': 'lib'}
276
277 for key, value in _INSTALL_SCHEMES[scheme].items():
278 if os.name in ('posix', 'nt'):
279 value = os.path.expanduser(value)
280 res[key] = os.path.normpath(_subst_vars(value, vars))
281 return res
282
283
284 def _get_preferred_schemes():
285 if os.name == 'nt':
286 return {
287 'prefix': 'nt',
288 'home': 'posix_home',
289 'user': 'nt_user',
290 }
291 if sys.platform == 'darwin' and sys._framework:
292 return {
293 'prefix': 'posix_prefix',
294 'home': 'posix_home',
295 'user': 'osx_framework_user',
296 }
297 return {
298 'prefix': 'posix_prefix',
299 'home': 'posix_home',
300 'user': 'posix_user',
301 }
302
303
304 def get_preferred_scheme(key):
305 if key == 'prefix' and sys.prefix != sys.base_prefix:
306 return 'venv'
307 scheme = _get_preferred_schemes()[key]
308 if scheme not in _INSTALL_SCHEMES:
309 raise ValueError(
310 f"{key!r} returned {scheme!r}, which is not a valid scheme "
311 f"on this platform"
312 )
313 return scheme
314
315
316 def get_default_scheme():
317 return get_preferred_scheme('prefix')
318
319
320 def _parse_makefile(filename, vars=None, keep_unresolved=True):
321 """Parse a Makefile-style file.
322
323 A dictionary containing name/value pairs is returned. If an
324 optional dictionary is passed in as the second argument, it is
325 used instead of a new dictionary.
326 """
327 import re
328
329 if vars is None:
330 vars = {}
331 done = {}
332 notdone = {}
333
334 with open(filename, encoding=sys.getfilesystemencoding(),
335 errors="surrogateescape") as f:
336 lines = f.readlines()
337
338 for line in lines:
339 if line.startswith('#') or line.strip() == '':
340 continue
341 m = re.match(_variable_rx, line)
342 if m:
343 n, v = m.group(1, 2)
344 v = v.strip()
345 # `$$' is a literal `$' in make
346 tmpv = v.replace('$$', '')
347
348 if "$" in tmpv:
349 notdone[n] = v
350 else:
351 try:
352 if n in _ALWAYS_STR:
353 raise ValueError
354
355 v = int(v)
356 except ValueError:
357 # insert literal `$'
358 done[n] = v.replace('$$', '$')
359 else:
360 done[n] = v
361
362 # do variable interpolation here
363 variables = list(notdone.keys())
364
365 # Variables with a 'PY_' prefix in the makefile. These need to
366 # be made available without that prefix through sysconfig.
367 # Special care is needed to ensure that variable expansion works, even
368 # if the expansion uses the name without a prefix.
369 renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
370
371 while len(variables) > 0:
372 for name in tuple(variables):
373 value = notdone[name]
374 m1 = re.search(_findvar1_rx, value)
375 m2 = re.search(_findvar2_rx, value)
376 if m1 and m2:
377 m = m1 if m1.start() < m2.start() else m2
378 else:
379 m = m1 if m1 else m2
380 if m is not None:
381 n = m.group(1)
382 found = True
383 if n in done:
384 item = str(done[n])
385 elif n in notdone:
386 # get it on a subsequent round
387 found = False
388 elif n in os.environ:
389 # do it like make: fall back to environment
390 item = os.environ[n]
391
392 elif n in renamed_variables:
393 if (name.startswith('PY_') and
394 name[3:] in renamed_variables):
395 item = ""
396
397 elif 'PY_' + n in notdone:
398 found = False
399
400 else:
401 item = str(done['PY_' + n])
402
403 else:
404 done[n] = item = ""
405
406 if found:
407 after = value[m.end():]
408 value = value[:m.start()] + item + after
409 if "$" in after:
410 notdone[name] = value
411 else:
412 try:
413 if name in _ALWAYS_STR:
414 raise ValueError
415 value = int(value)
416 except ValueError:
417 done[name] = value.strip()
418 else:
419 done[name] = value
420 variables.remove(name)
421
422 if name.startswith('PY_') \
423 and name[3:] in renamed_variables:
424
425 name = name[3:]
426 if name not in done:
427 done[name] = value
428
429 else:
430 # Adds unresolved variables to the done dict.
431 # This is disabled when called from distutils.sysconfig
432 if keep_unresolved:
433 done[name] = value
434 # bogus variable reference (e.g. "prefix=$/opt/python");
435 # just drop it since we can't deal
436 variables.remove(name)
437
438 # strip spurious spaces
439 for k, v in done.items():
440 if isinstance(v, str):
441 done[k] = v.strip()
442
443 # save the results in the global dictionary
444 vars.update(done)
445 return vars
446
447
448 def get_makefile_filename():
449 """Return the path of the Makefile."""
450 if _PYTHON_BUILD:
451 return os.path.join(_PROJECT_BASE, "Makefile")
452 if hasattr(sys, 'abiflags'):
453 config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}'
454 else:
455 config_dir_name = 'config'
456 if hasattr(sys.implementation, '_multiarch'):
457 config_dir_name += f'-{sys.implementation._multiarch}'
458 return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')
459
460
461 def _get_sysconfigdata_name():
462 multiarch = getattr(sys.implementation, '_multiarch', '')
463 return os.environ.get(
464 '_PYTHON_SYSCONFIGDATA_NAME',
465 f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
466 )
467
468
469 def _generate_posix_vars():
470 """Generate the Python module containing build-time variables."""
471 import pprint
472 vars = {}
473 # load the installed Makefile:
474 makefile = get_makefile_filename()
475 try:
476 _parse_makefile(makefile, vars)
477 except OSError as e:
478 msg = f"invalid Python installation: unable to open {makefile}"
479 if hasattr(e, "strerror"):
480 msg = f"{msg} ({e.strerror})"
481 raise OSError(msg)
482 # load the installed pyconfig.h:
483 config_h = get_config_h_filename()
484 try:
485 with open(config_h, encoding="utf-8") as f:
486 parse_config_h(f, vars)
487 except OSError as e:
488 msg = f"invalid Python installation: unable to open {config_h}"
489 if hasattr(e, "strerror"):
490 msg = f"{msg} ({e.strerror})"
491 raise OSError(msg)
492 # On AIX, there are wrong paths to the linker scripts in the Makefile
493 # -- these paths are relative to the Python source, but when installed
494 # the scripts are in another directory.
495 if _PYTHON_BUILD:
496 vars['BLDSHARED'] = vars['LDSHARED']
497
498 # There's a chicken-and-egg situation on OS X with regards to the
499 # _sysconfigdata module after the changes introduced by #15298:
500 # get_config_vars() is called by get_platform() as part of the
501 # `make pybuilddir.txt` target -- which is a precursor to the
502 # _sysconfigdata.py module being constructed. Unfortunately,
503 # get_config_vars() eventually calls _init_posix(), which attempts
504 # to import _sysconfigdata, which we won't have built yet. In order
505 # for _init_posix() to work, if we're on Darwin, just mock up the
506 # _sysconfigdata module manually and populate it with the build vars.
507 # This is more than sufficient for ensuring the subsequent call to
508 # get_platform() succeeds.
509 name = _get_sysconfigdata_name()
510 if 'darwin' in sys.platform:
511 import types
512 module = types.ModuleType(name)
513 module.build_time_vars = vars
514 sys.modules[name] = module
515
516 pybuilddir = f'build/lib.{get_platform()}-{_PY_VERSION_SHORT}'
517 if hasattr(sys, "gettotalrefcount"):
518 pybuilddir += '-pydebug'
519 os.makedirs(pybuilddir, exist_ok=True)
520 destfile = os.path.join(pybuilddir, name + '.py')
521
522 with open(destfile, 'w', encoding='utf8') as f:
523 f.write('# system configuration generated and used by'
524 ' the sysconfig module\n')
525 f.write('build_time_vars = ')
526 pprint.pprint(vars, stream=f)
527
528 # Create file used for sys.path fixup -- see Modules/getpath.c
529 with open('pybuilddir.txt', 'w', encoding='utf8') as f:
530 f.write(pybuilddir)
531
532 def _init_posix(vars):
533 """Initialize the module as appropriate for POSIX systems."""
534 # _sysconfigdata is generated at build time, see _generate_posix_vars()
535 name = _get_sysconfigdata_name()
536 _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
537 build_time_vars = _temp.build_time_vars
538 vars.update(build_time_vars)
539
540 def _init_non_posix(vars):
541 """Initialize the module as appropriate for NT"""
542 # set basic install directories
543 import _imp
544 vars['LIBDEST'] = get_path('stdlib')
545 vars['BINLIBDEST'] = get_path('platstdlib')
546 vars['INCLUDEPY'] = get_path('include')
547 try:
548 # GH-99201: _imp.extension_suffixes may be empty when
549 # HAVE_DYNAMIC_LOADING is not set. In this case, don't set EXT_SUFFIX.
550 vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
551 except IndexError:
552 pass
553 vars['EXE'] = '.exe'
554 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
555 vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
556 vars['TZPATH'] = ''
557
558 #
559 # public APIs
560 #
561
562
563 def parse_config_h(fp, vars=None):
564 """Parse a config.h-style file.
565
566 A dictionary containing name/value pairs is returned. If an
567 optional dictionary is passed in as the second argument, it is
568 used instead of a new dictionary.
569 """
570 if vars is None:
571 vars = {}
572 import re
573 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
574 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
575
576 while True:
577 line = fp.readline()
578 if not line:
579 break
580 m = define_rx.match(line)
581 if m:
582 n, v = m.group(1, 2)
583 try:
584 if n in _ALWAYS_STR:
585 raise ValueError
586 v = int(v)
587 except ValueError:
588 pass
589 vars[n] = v
590 else:
591 m = undef_rx.match(line)
592 if m:
593 vars[m.group(1)] = 0
594 return vars
595
596
597 def get_config_h_filename():
598 """Return the path of pyconfig.h."""
599 if _PYTHON_BUILD:
600 if os.name == "nt":
601 inc_dir = os.path.join(_PROJECT_BASE, "PC")
602 else:
603 inc_dir = _PROJECT_BASE
604 else:
605 inc_dir = get_path('platinclude')
606 return os.path.join(inc_dir, 'pyconfig.h')
607
608
609 def get_scheme_names():
610 """Return a tuple containing the schemes names."""
611 return tuple(sorted(_INSTALL_SCHEMES))
612
613
614 def get_path_names():
615 """Return a tuple containing the paths names."""
616 return _SCHEME_KEYS
617
618
619 def get_paths(scheme=get_default_scheme(), vars=None, expand=True):
620 """Return a mapping containing an install scheme.
621
622 ``scheme`` is the install scheme name. If not provided, it will
623 return the default scheme for the current platform.
624 """
625 if expand:
626 return _expand_vars(scheme, vars)
627 else:
628 return _INSTALL_SCHEMES[scheme]
629
630
631 def get_path(name, scheme=get_default_scheme(), vars=None, expand=True):
632 """Return a path corresponding to the scheme.
633
634 ``scheme`` is the install scheme name.
635 """
636 return get_paths(scheme, vars, expand)[name]
637
638
639 def _init_config_vars():
640 global _CONFIG_VARS
641 _CONFIG_VARS = {}
642 # Normalized versions of prefix and exec_prefix are handy to have;
643 # in fact, these are the standard versions used most places in the
644 # Distutils.
645 _CONFIG_VARS['prefix'] = _PREFIX
646 _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
647 _CONFIG_VARS['py_version'] = _PY_VERSION
648 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
649 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT
650 _CONFIG_VARS['installed_base'] = _BASE_PREFIX
651 _CONFIG_VARS['base'] = _PREFIX
652 _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX
653 _CONFIG_VARS['platbase'] = _EXEC_PREFIX
654 _CONFIG_VARS['projectbase'] = _PROJECT_BASE
655 _CONFIG_VARS['platlibdir'] = sys.platlibdir
656 try:
657 _CONFIG_VARS['abiflags'] = sys.abiflags
658 except AttributeError:
659 # sys.abiflags may not be defined on all platforms.
660 _CONFIG_VARS['abiflags'] = ''
661 try:
662 _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '')
663 except AttributeError:
664 _CONFIG_VARS['py_version_nodot_plat'] = ''
665
666 if os.name == 'nt':
667 _init_non_posix(_CONFIG_VARS)
668 _CONFIG_VARS['VPATH'] = sys._vpath
669 if os.name == 'posix':
670 _init_posix(_CONFIG_VARS)
671 if _HAS_USER_BASE:
672 # Setting 'userbase' is done below the call to the
673 # init function to enable using 'get_config_var' in
674 # the init-function.
675 _CONFIG_VARS['userbase'] = _getuserbase()
676
677 # Always convert srcdir to an absolute path
678 srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE)
679 if os.name == 'posix':
680 if _PYTHON_BUILD:
681 # If srcdir is a relative path (typically '.' or '..')
682 # then it should be interpreted relative to the directory
683 # containing Makefile.
684 base = os.path.dirname(get_makefile_filename())
685 srcdir = os.path.join(base, srcdir)
686 else:
687 # srcdir is not meaningful since the installation is
688 # spread about the filesystem. We choose the
689 # directory containing the Makefile since we know it
690 # exists.
691 srcdir = os.path.dirname(get_makefile_filename())
692 _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir)
693
694 # OS X platforms require special customization to handle
695 # multi-architecture, multi-os-version installers
696 if sys.platform == 'darwin':
697 import _osx_support
698 _osx_support.customize_config_vars(_CONFIG_VARS)
699
700 global _CONFIG_VARS_INITIALIZED
701 _CONFIG_VARS_INITIALIZED = True
702
703
704 def get_config_vars(*args):
705 """With no arguments, return a dictionary of all configuration
706 variables relevant for the current platform.
707
708 On Unix, this means every variable defined in Python's installed Makefile;
709 On Windows it's a much smaller set.
710
711 With arguments, return a list of values that result from looking up
712 each argument in the configuration variable dictionary.
713 """
714
715 # Avoid claiming the lock once initialization is complete.
716 if not _CONFIG_VARS_INITIALIZED:
717 with _CONFIG_VARS_LOCK:
718 # Test again with the lock held to avoid races. Note that
719 # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,
720 # to ensure that recursive calls to get_config_vars()
721 # don't re-enter init_config_vars().
722 if _CONFIG_VARS is None:
723 _init_config_vars()
724
725 if args:
726 vals = []
727 for name in args:
728 vals.append(_CONFIG_VARS.get(name))
729 return vals
730 else:
731 return _CONFIG_VARS
732
733
734 def get_config_var(name):
735 """Return the value of a single variable using the dictionary returned by
736 'get_config_vars()'.
737
738 Equivalent to get_config_vars().get(name)
739 """
740 return get_config_vars().get(name)
741
742
743 def get_platform():
744 """Return a string that identifies the current platform.
745
746 This is used mainly to distinguish platform-specific build directories and
747 platform-specific built distributions. Typically includes the OS name and
748 version and the architecture (as supplied by 'os.uname()'), although the
749 exact information included depends on the OS; on Linux, the kernel version
750 isn't particularly important.
751
752 Examples of returned values:
753 linux-i586
754 linux-alpha (?)
755 solaris-2.6-sun4u
756
757 Windows will return one of:
758 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
759 win32 (all others - specifically, sys.platform is returned)
760
761 For other non-POSIX platforms, currently just returns 'sys.platform'.
762
763 """
764 if os.name == 'nt':
765 if 'amd64' in sys.version.lower():
766 return 'win-amd64'
767 if '(arm)' in sys.version.lower():
768 return 'win-arm32'
769 if '(arm64)' in sys.version.lower():
770 return 'win-arm64'
771 return sys.platform
772
773 if os.name != "posix" or not hasattr(os, 'uname'):
774 # XXX what about the architecture? NT is Intel or Alpha
775 return sys.platform
776
777 # Set for cross builds explicitly
778 if "_PYTHON_HOST_PLATFORM" in os.environ:
779 return os.environ["_PYTHON_HOST_PLATFORM"]
780
781 # Try to distinguish various flavours of Unix
782 osname, host, release, version, machine = os.uname()
783
784 # Convert the OS name to lowercase, remove '/' characters, and translate
785 # spaces (for "Power Macintosh")
786 osname = osname.lower().replace('/', '')
787 machine = machine.replace(' ', '_')
788 machine = machine.replace('/', '-')
789
790 if osname[:5] == "linux":
791 # At least on Linux/Intel, 'machine' is the processor --
792 # i386, etc.
793 # XXX what about Alpha, SPARC, etc?
794 return f"{osname}-{machine}"
795 elif osname[:5] == "sunos":
796 if release[0] >= "5": # SunOS 5 == Solaris 2
797 osname = "solaris"
798 release = f"{int(release[0]) - 3}.{release[2:]}"
799 # We can't use "platform.architecture()[0]" because a
800 # bootstrap problem. We use a dict to get an error
801 # if some suspicious happens.
802 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
803 machine += f".{bitness[sys.maxsize]}"
804 # fall through to standard osname-release-machine representation
805 elif osname[:3] == "aix":
806 from _aix_support import aix_platform
807 return aix_platform()
808 elif osname[:6] == "cygwin":
809 osname = "cygwin"
810 import re
811 rel_re = re.compile(r'[\d.]+')
812 m = rel_re.match(release)
813 if m:
814 release = m.group()
815 elif osname[:6] == "darwin":
816 import _osx_support
817 osname, release, machine = _osx_support.get_platform_osx(
818 get_config_vars(),
819 osname, release, machine)
820
821 return f"{osname}-{release}-{machine}"
822
823
824 def get_python_version():
825 return _PY_VERSION_SHORT
826
827
828 def expand_makefile_vars(s, vars):
829 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
830 'string' according to 'vars' (a dictionary mapping variable names to
831 values). Variables not present in 'vars' are silently expanded to the
832 empty string. The variable values in 'vars' should not contain further
833 variable expansions; if 'vars' is the output of 'parse_makefile()',
834 you're fine. Returns a variable-expanded version of 's'.
835 """
836 import re
837
838 # This algorithm does multiple expansion, so if vars['foo'] contains
839 # "${bar}", it will expand ${foo} to ${bar}, and then expand
840 # ${bar}... and so forth. This is fine as long as 'vars' comes from
841 # 'parse_makefile()', which takes care of such expansions eagerly,
842 # according to make's variable expansion semantics.
843
844 while True:
845 m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s)
846 if m:
847 (beg, end) = m.span()
848 s = s[0:beg] + vars.get(m.group(1)) + s[end:]
849 else:
850 break
851 return s
852
853
854 def _print_dict(title, data):
855 for index, (key, value) in enumerate(sorted(data.items())):
856 if index == 0:
857 print(f'{title}: ')
858 print(f'\t{key} = "{value}"')
859
860
861 def _main():
862 """Display all information sysconfig detains."""
863 if '--generate-posix-vars' in sys.argv:
864 _generate_posix_vars()
865 return
866 print(f'Platform: "{get_platform()}"')
867 print(f'Python version: "{get_python_version()}"')
868 print(f'Current installation scheme: "{get_default_scheme()}"')
869 print()
870 _print_dict('Paths', get_paths())
871 print()
872 _print_dict('Variables', get_config_vars())
873
874
875 if __name__ == '__main__':
876 _main()