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