1"""Provide access to Python's configuration information. 2 3""" 4import sys 5import os 6from os.path import pardir, realpath 7 8_INSTALL_SCHEMES = { 9 'posix_prefix': { 10 'stdlib': '{base}/lib/python{py_version_short}', 11 'platstdlib': '{platbase}/lib/python{py_version_short}', 12 'purelib': '{base}/lib/python{py_version_short}/site-packages', 13 'platlib': '{platbase}/lib/python{py_version_short}/site-packages', 14 'include': '{base}/include/python{py_version_short}', 15 'platinclude': '{platbase}/include/python{py_version_short}', 16 'scripts': '{base}/bin', 17 'data': '{base}', 18 }, 19 'posix_home': { 20 'stdlib': '{base}/lib/python', 21 'platstdlib': '{base}/lib/python', 22 'purelib': '{base}/lib/python', 23 'platlib': '{base}/lib/python', 24 'include': '{base}/include/python', 25 'platinclude': '{base}/include/python', 26 'scripts': '{base}/bin', 27 'data' : '{base}', 28 }, 29 'nt': { 30 'stdlib': '{base}/Lib', 31 'platstdlib': '{base}/Lib', 32 'purelib': '{base}/Lib/site-packages', 33 'platlib': '{base}/Lib/site-packages', 34 'include': '{base}/Include', 35 'platinclude': '{base}/Include', 36 'scripts': '{base}/Scripts', 37 'data' : '{base}', 38 }, 39 'os2': { 40 'stdlib': '{base}/Lib', 41 'platstdlib': '{base}/Lib', 42 'purelib': '{base}/Lib/site-packages', 43 'platlib': '{base}/Lib/site-packages', 44 'include': '{base}/Include', 45 'platinclude': '{base}/Include', 46 'scripts': '{base}/Scripts', 47 'data' : '{base}', 48 }, 49 'os2_home': { 50 'stdlib': '{userbase}/lib/python{py_version_short}', 51 'platstdlib': '{userbase}/lib/python{py_version_short}', 52 'purelib': '{userbase}/lib/python{py_version_short}/site-packages', 53 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', 54 'include': '{userbase}/include/python{py_version_short}', 55 'scripts': '{userbase}/bin', 56 'data' : '{userbase}', 57 }, 58 'nt_user': { 59 'stdlib': '{userbase}/Python{py_version_nodot}', 60 'platstdlib': '{userbase}/Python{py_version_nodot}', 61 'purelib': '{userbase}/Python{py_version_nodot}/site-packages', 62 'platlib': '{userbase}/Python{py_version_nodot}/site-packages', 63 'include': '{userbase}/Python{py_version_nodot}/Include', 64 'scripts': '{userbase}/Scripts', 65 'data' : '{userbase}', 66 }, 67 'posix_user': { 68 'stdlib': '{userbase}/lib/python{py_version_short}', 69 'platstdlib': '{userbase}/lib/python{py_version_short}', 70 'purelib': '{userbase}/lib/python{py_version_short}/site-packages', 71 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', 72 'include': '{userbase}/include/python{py_version_short}', 73 'scripts': '{userbase}/bin', 74 'data' : '{userbase}', 75 }, 76 'osx_framework_user': { 77 'stdlib': '{userbase}/lib/python', 78 'platstdlib': '{userbase}/lib/python', 79 'purelib': '{userbase}/lib/python/site-packages', 80 'platlib': '{userbase}/lib/python/site-packages', 81 'include': '{userbase}/include', 82 'scripts': '{userbase}/bin', 83 'data' : '{userbase}', 84 }, 85 } 86 87_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', 88 'scripts', 'data') 89_PY_VERSION = sys.version.split()[0] 90_PY_VERSION_SHORT = sys.version[:3] 91_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] 92_PREFIX = os.path.normpath(sys.prefix) 93_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) 94_CONFIG_VARS = None 95_USER_BASE = None 96 97def _safe_realpath(path): 98 try: 99 return realpath(path) 100 except OSError: 101 return path 102 103if sys.executable: 104 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) 105else: 106 # sys.executable can be empty if argv[0] has been changed and Python is 107 # unable to retrieve the real program name 108 _PROJECT_BASE = _safe_realpath(os.getcwd()) 109 110if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): 111 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) 112# PC/VS7.1 113if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): 114 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) 115# PC/AMD64 116if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): 117 _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) 118 119# set for cross builds 120if "_PYTHON_PROJECT_BASE" in os.environ: 121 # the build directory for posix builds 122 _PROJECT_BASE = os.path.normpath(os.path.abspath(".")) 123def is_python_build(): 124 for fn in ("Setup.dist", "Setup.local"): 125 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): 126 return True 127 return False 128 129_PYTHON_BUILD = is_python_build() 130 131if _PYTHON_BUILD: 132 for scheme in ('posix_prefix', 'posix_home'): 133 _INSTALL_SCHEMES[scheme]['include'] = '{projectbase}/Include' 134 _INSTALL_SCHEMES[scheme]['platinclude'] = '{srcdir}' 135 136def _subst_vars(s, local_vars): 137 try: 138 return s.format(**local_vars) 139 except KeyError: 140 try: 141 return s.format(**os.environ) 142 except KeyError, var: 143 raise AttributeError('{%s}' % var) 144 145def _extend_dict(target_dict, other_dict): 146 target_keys = target_dict.keys() 147 for key, value in other_dict.items(): 148 if key in target_keys: 149 continue 150 target_dict[key] = value 151 152def _expand_vars(scheme, vars): 153 res = {} 154 if vars is None: 155 vars = {} 156 _extend_dict(vars, get_config_vars()) 157 158 for key, value in _INSTALL_SCHEMES[scheme].items(): 159 if os.name in ('posix', 'nt'): 160 value = os.path.expanduser(value) 161 res[key] = os.path.normpath(_subst_vars(value, vars)) 162 return res 163 164def _get_default_scheme(): 165 if os.name == 'posix': 166 # the default scheme for posix is posix_prefix 167 return 'posix_prefix' 168 return os.name 169 170def _getuserbase(): 171 env_base = os.environ.get("PYTHONUSERBASE", None) 172 def joinuser(*args): 173 return os.path.expanduser(os.path.join(*args)) 174 175 # what about 'os2emx', 'riscos' ? 176 if os.name == "nt": 177 base = os.environ.get("APPDATA") or "~" 178 return env_base if env_base else joinuser(base, "Python") 179 180 if sys.platform == "darwin": 181 framework = get_config_var("PYTHONFRAMEWORK") 182 if framework: 183 return env_base if env_base else \ 184 joinuser("~", "Library", framework, "%d.%d" 185 % (sys.version_info[:2])) 186 187 return env_base if env_base else joinuser("~", ".local") 188 189 190def _parse_makefile(filename, vars=None): 191 """Parse a Makefile-style file. 192 193 A dictionary containing name/value pairs is returned. If an 194 optional dictionary is passed in as the second argument, it is 195 used instead of a new dictionary. 196 """ 197 import re 198 # Regexes needed for parsing Makefile (and similar syntaxes, 199 # like old-style Setup files). 200 _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") 201 _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") 202 _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") 203 204 if vars is None: 205 vars = {} 206 done = {} 207 notdone = {} 208 209 with open(filename) as f: 210 lines = f.readlines() 211 212 for line in lines: 213 if line.startswith('#') or line.strip() == '': 214 continue 215 m = _variable_rx.match(line) 216 if m: 217 n, v = m.group(1, 2) 218 v = v.strip() 219 # `$$' is a literal `$' in make 220 tmpv = v.replace('$$', '') 221 222 if "$" in tmpv: 223 notdone[n] = v 224 else: 225 try: 226 v = int(v) 227 except ValueError: 228 # insert literal `$' 229 done[n] = v.replace('$$', '$') 230 else: 231 done[n] = v 232 233 # do variable interpolation here 234 while notdone: 235 for name in notdone.keys(): 236 value = notdone[name] 237 m = _findvar1_rx.search(value) or _findvar2_rx.search(value) 238 if m: 239 n = m.group(1) 240 found = True 241 if n in done: 242 item = str(done[n]) 243 elif n in notdone: 244 # get it on a subsequent round 245 found = False 246 elif n in os.environ: 247 # do it like make: fall back to environment 248 item = os.environ[n] 249 else: 250 done[n] = item = "" 251 if found: 252 after = value[m.end():] 253 value = value[:m.start()] + item + after 254 if "$" in after: 255 notdone[name] = value 256 else: 257 try: value = int(value) 258 except ValueError: 259 done[name] = value.strip() 260 else: 261 done[name] = value 262 del notdone[name] 263 else: 264 # bogus variable reference; just drop it since we can't deal 265 del notdone[name] 266 # strip spurious spaces 267 for k, v in done.items(): 268 if isinstance(v, str): 269 done[k] = v.strip() 270 271 # save the results in the global dictionary 272 vars.update(done) 273 return vars 274 275 276def get_makefile_filename(): 277 """Return the path of the Makefile.""" 278 if _PYTHON_BUILD: 279 return os.path.join(_PROJECT_BASE, "Makefile") 280 return os.path.join(get_path('platstdlib'), "config", "Makefile") 281 282# Issue #22199: retain undocumented private name for compatibility 283_get_makefile_filename = get_makefile_filename 284 285def _generate_posix_vars(): 286 """Generate the Python module containing build-time variables.""" 287 import pprint 288 vars = {} 289 # load the installed Makefile: 290 makefile = get_makefile_filename() 291 try: 292 _parse_makefile(makefile, vars) 293 except IOError, e: 294 msg = "invalid Python installation: unable to open %s" % makefile 295 if hasattr(e, "strerror"): 296 msg = msg + " (%s)" % e.strerror 297 raise IOError(msg) 298 299 # load the installed pyconfig.h: 300 config_h = get_config_h_filename() 301 try: 302 with open(config_h) as f: 303 parse_config_h(f, vars) 304 except IOError, e: 305 msg = "invalid Python installation: unable to open %s" % config_h 306 if hasattr(e, "strerror"): 307 msg = msg + " (%s)" % e.strerror 308 raise IOError(msg) 309 310 # On AIX, there are wrong paths to the linker scripts in the Makefile 311 # -- these paths are relative to the Python source, but when installed 312 # the scripts are in another directory. 313 if _PYTHON_BUILD: 314 vars['LDSHARED'] = vars['BLDSHARED'] 315 316 # There's a chicken-and-egg situation on OS X with regards to the 317 # _sysconfigdata module after the changes introduced by #15298: 318 # get_config_vars() is called by get_platform() as part of the 319 # `make pybuilddir.txt` target -- which is a precursor to the 320 # _sysconfigdata.py module being constructed. Unfortunately, 321 # get_config_vars() eventually calls _init_posix(), which attempts 322 # to import _sysconfigdata, which we won't have built yet. In order 323 # for _init_posix() to work, if we're on Darwin, just mock up the 324 # _sysconfigdata module manually and populate it with the build vars. 325 # This is more than sufficient for ensuring the subsequent call to 326 # get_platform() succeeds. 327 name = '_sysconfigdata' 328 if 'darwin' in sys.platform: 329 import imp 330 module = imp.new_module(name) 331 module.build_time_vars = vars 332 sys.modules[name] = module 333 334 pybuilddir = 'build/lib.%s-%s' % (get_platform(), sys.version[:3]) 335 if hasattr(sys, "gettotalrefcount"): 336 pybuilddir += '-pydebug' 337 try: 338 os.makedirs(pybuilddir) 339 except OSError: 340 pass 341 destfile = os.path.join(pybuilddir, name + '.py') 342 343 with open(destfile, 'wb') as f: 344 f.write('# system configuration generated and used by' 345 ' the sysconfig module\n') 346 f.write('build_time_vars = ') 347 pprint.pprint(vars, stream=f) 348 349 # Create file used for sys.path fixup -- see Modules/getpath.c 350 with open('pybuilddir.txt', 'w') as f: 351 f.write(pybuilddir) 352 353def _init_posix(vars): 354 """Initialize the module as appropriate for POSIX systems.""" 355 # _sysconfigdata is generated at build time, see _generate_posix_vars() 356 from _sysconfigdata import build_time_vars 357 vars.update(build_time_vars) 358 359def _init_non_posix(vars): 360 """Initialize the module as appropriate for NT""" 361 # set basic install directories 362 vars['LIBDEST'] = get_path('stdlib') 363 vars['BINLIBDEST'] = get_path('platstdlib') 364 vars['INCLUDEPY'] = get_path('include') 365 vars['SO'] = '.pyd' 366 vars['EXE'] = '.exe' 367 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT 368 vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) 369 370# 371# public APIs 372# 373 374 375def parse_config_h(fp, vars=None): 376 """Parse a config.h-style file. 377 378 A dictionary containing name/value pairs is returned. If an 379 optional dictionary is passed in as the second argument, it is 380 used instead of a new dictionary. 381 """ 382 import re 383 if vars is None: 384 vars = {} 385 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") 386 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") 387 388 while True: 389 line = fp.readline() 390 if not line: 391 break 392 m = define_rx.match(line) 393 if m: 394 n, v = m.group(1, 2) 395 try: v = int(v) 396 except ValueError: pass 397 vars[n] = v 398 else: 399 m = undef_rx.match(line) 400 if m: 401 vars[m.group(1)] = 0 402 return vars 403 404def get_config_h_filename(): 405 """Returns the path of pyconfig.h.""" 406 if _PYTHON_BUILD: 407 if os.name == "nt": 408 inc_dir = os.path.join(_PROJECT_BASE, "PC") 409 else: 410 inc_dir = _PROJECT_BASE 411 else: 412 inc_dir = get_path('platinclude') 413 return os.path.join(inc_dir, 'pyconfig.h') 414 415def get_scheme_names(): 416 """Returns a tuple containing the schemes names.""" 417 schemes = _INSTALL_SCHEMES.keys() 418 schemes.sort() 419 return tuple(schemes) 420 421def get_path_names(): 422 """Returns a tuple containing the paths names.""" 423 return _SCHEME_KEYS 424 425def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): 426 """Returns a mapping containing an install scheme. 427 428 ``scheme`` is the install scheme name. If not provided, it will 429 return the default scheme for the current platform. 430 """ 431 if expand: 432 return _expand_vars(scheme, vars) 433 else: 434 return _INSTALL_SCHEMES[scheme] 435 436def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): 437 """Returns a path corresponding to the scheme. 438 439 ``scheme`` is the install scheme name. 440 """ 441 return get_paths(scheme, vars, expand)[name] 442 443def get_config_vars(*args): 444 """With no arguments, return a dictionary of all configuration 445 variables relevant for the current platform. 446 447 On Unix, this means every variable defined in Python's installed Makefile; 448 On Windows and Mac OS it's a much smaller set. 449 450 With arguments, return a list of values that result from looking up 451 each argument in the configuration variable dictionary. 452 """ 453 import re 454 global _CONFIG_VARS 455 if _CONFIG_VARS is None: 456 _CONFIG_VARS = {} 457 # Normalized versions of prefix and exec_prefix are handy to have; 458 # in fact, these are the standard versions used most places in the 459 # Distutils. 460 _CONFIG_VARS['prefix'] = _PREFIX 461 _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX 462 _CONFIG_VARS['py_version'] = _PY_VERSION 463 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT 464 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] 465 _CONFIG_VARS['base'] = _PREFIX 466 _CONFIG_VARS['platbase'] = _EXEC_PREFIX 467 _CONFIG_VARS['projectbase'] = _PROJECT_BASE 468 469 if os.name in ('nt', 'os2'): 470 _init_non_posix(_CONFIG_VARS) 471 if os.name == 'posix': 472 _init_posix(_CONFIG_VARS) 473 474 # Setting 'userbase' is done below the call to the 475 # init function to enable using 'get_config_var' in 476 # the init-function. 477 _CONFIG_VARS['userbase'] = _getuserbase() 478 479 if 'srcdir' not in _CONFIG_VARS: 480 _CONFIG_VARS['srcdir'] = _PROJECT_BASE 481 482 # Convert srcdir into an absolute path if it appears necessary. 483 # Normally it is relative to the build directory. However, during 484 # testing, for example, we might be running a non-installed python 485 # from a different directory. 486 if _PYTHON_BUILD and os.name == "posix": 487 base = _PROJECT_BASE 488 try: 489 cwd = os.getcwd() 490 except OSError: 491 cwd = None 492 if (not os.path.isabs(_CONFIG_VARS['srcdir']) and 493 base != cwd): 494 # srcdir is relative and we are not in the same directory 495 # as the executable. Assume executable is in the build 496 # directory and make srcdir absolute. 497 srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) 498 _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) 499 500 # OS X platforms require special customization to handle 501 # multi-architecture, multi-os-version installers 502 if sys.platform == 'darwin': 503 import _osx_support 504 _osx_support.customize_config_vars(_CONFIG_VARS) 505 506 if args: 507 vals = [] 508 for name in args: 509 vals.append(_CONFIG_VARS.get(name)) 510 return vals 511 else: 512 return _CONFIG_VARS 513 514def get_config_var(name): 515 """Return the value of a single variable using the dictionary returned by 516 'get_config_vars()'. 517 518 Equivalent to get_config_vars().get(name) 519 """ 520 return get_config_vars().get(name) 521 522def get_platform(): 523 """Return a string that identifies the current platform. 524 525 This is used mainly to distinguish platform-specific build directories and 526 platform-specific built distributions. Typically includes the OS name 527 and version and the architecture (as supplied by 'os.uname()'), 528 although the exact information included depends on the OS; eg. for IRIX 529 the architecture isn't particularly important (IRIX only runs on SGI 530 hardware), but for Linux the kernel version isn't particularly 531 important. 532 533 Examples of returned values: 534 linux-i586 535 linux-alpha (?) 536 solaris-2.6-sun4u 537 irix-5.3 538 irix64-6.2 539 540 Windows will return one of: 541 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) 542 win-ia64 (64bit Windows on Itanium) 543 win32 (all others - specifically, sys.platform is returned) 544 545 For other non-POSIX platforms, currently just returns 'sys.platform'. 546 """ 547 import re 548 if os.name == 'nt': 549 # sniff sys.version for architecture. 550 prefix = " bit (" 551 i = sys.version.find(prefix) 552 if i == -1: 553 return sys.platform 554 j = sys.version.find(")", i) 555 look = sys.version[i+len(prefix):j].lower() 556 if look == 'amd64': 557 return 'win-amd64' 558 if look == 'itanium': 559 return 'win-ia64' 560 return sys.platform 561 562 # Set for cross builds explicitly 563 if "_PYTHON_HOST_PLATFORM" in os.environ: 564 return os.environ["_PYTHON_HOST_PLATFORM"] 565 566 if os.name != "posix" or not hasattr(os, 'uname'): 567 # XXX what about the architecture? NT is Intel or Alpha, 568 # Mac OS is M68k or PPC, etc. 569 return sys.platform 570 571 # Try to distinguish various flavours of Unix 572 osname, host, release, version, machine = os.uname() 573 574 # Convert the OS name to lowercase, remove '/' characters 575 # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") 576 osname = osname.lower().replace('/', '') 577 machine = machine.replace(' ', '_') 578 machine = machine.replace('/', '-') 579 580 if osname[:5] == "linux": 581 # At least on Linux/Intel, 'machine' is the processor -- 582 # i386, etc. 583 # XXX what about Alpha, SPARC, etc? 584 return "%s-%s" % (osname, machine) 585 elif osname[:5] == "sunos": 586 if release[0] >= "5": # SunOS 5 == Solaris 2 587 osname = "solaris" 588 release = "%d.%s" % (int(release[0]) - 3, release[2:]) 589 # We can't use "platform.architecture()[0]" because a 590 # bootstrap problem. We use a dict to get an error 591 # if some suspicious happens. 592 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} 593 machine += ".%s" % bitness[sys.maxint] 594 # fall through to standard osname-release-machine representation 595 elif osname[:4] == "irix": # could be "irix64"! 596 return "%s-%s" % (osname, release) 597 elif osname[:3] == "aix": 598 return "%s-%s.%s" % (osname, version, release) 599 elif osname[:6] == "cygwin": 600 osname = "cygwin" 601 rel_re = re.compile (r'[\d.]+') 602 m = rel_re.match(release) 603 if m: 604 release = m.group() 605 elif osname[:6] == "darwin": 606 import _osx_support 607 osname, release, machine = _osx_support.get_platform_osx( 608 get_config_vars(), 609 osname, release, machine) 610 611 return "%s-%s-%s" % (osname, release, machine) 612 613 614def get_python_version(): 615 return _PY_VERSION_SHORT 616 617 618def _print_dict(title, data): 619 for index, (key, value) in enumerate(sorted(data.items())): 620 if index == 0: 621 print '%s: ' % (title) 622 print '\t%s = "%s"' % (key, value) 623 624 625def _main(): 626 """Display all information sysconfig detains.""" 627 if '--generate-posix-vars' in sys.argv: 628 _generate_posix_vars() 629 return 630 print 'Platform: "%s"' % get_platform() 631 print 'Python version: "%s"' % get_python_version() 632 print 'Current installation scheme: "%s"' % _get_default_scheme() 633 print 634 _print_dict('Paths', get_paths()) 635 print 636 _print_dict('Variables', get_config_vars()) 637 638 639if __name__ == '__main__': 640 _main() 641