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