Home | History | Annotate | Download | only in Lib
      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',
     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 
     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 def is_python_build():
    120     for fn in ("Setup.dist", "Setup.local"):
    121         if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
    122             return True
    123     return False
    124 
    125 _PYTHON_BUILD = is_python_build()
    126 
    127 if _PYTHON_BUILD:
    128     for scheme in ('posix_prefix', 'posix_home'):
    129         _INSTALL_SCHEMES[scheme]['include'] = '{projectbase}/Include'
    130         _INSTALL_SCHEMES[scheme]['platinclude'] = '{srcdir}'
    131 
    132 def _subst_vars(s, local_vars):
    133     try:
    134         return s.format(**local_vars)
    135     except KeyError:
    136         try:
    137             return s.format(**os.environ)
    138         except KeyError, var:
    139             raise AttributeError('{%s}' % var)
    140 
    141 def _extend_dict(target_dict, other_dict):
    142     target_keys = target_dict.keys()
    143     for key, value in other_dict.items():
    144         if key in target_keys:
    145             continue
    146         target_dict[key] = value
    147 
    148 def _expand_vars(scheme, vars):
    149     res = {}
    150     if vars is None:
    151         vars = {}
    152     _extend_dict(vars, get_config_vars())
    153 
    154     for key, value in _INSTALL_SCHEMES[scheme].items():
    155         if os.name in ('posix', 'nt'):
    156             value = os.path.expanduser(value)
    157         res[key] = os.path.normpath(_subst_vars(value, vars))
    158     return res
    159 
    160 def _get_default_scheme():
    161     if os.name == 'posix':
    162         # the default scheme for posix is posix_prefix

    163         return 'posix_prefix'
    164     return os.name
    165 
    166 def _getuserbase():
    167     env_base = os.environ.get("PYTHONUSERBASE", None)
    168     def joinuser(*args):
    169         return os.path.expanduser(os.path.join(*args))
    170 
    171     # what about 'os2emx', 'riscos' ?

    172     if os.name == "nt":
    173         base = os.environ.get("APPDATA") or "~"
    174         return env_base if env_base else joinuser(base, "Python")
    175 
    176     if sys.platform == "darwin":
    177         framework = get_config_var("PYTHONFRAMEWORK")
    178         if framework:
    179             return joinuser("~", "Library", framework, "%d.%d"%(
    180                 sys.version_info[:2]))
    181 
    182     return env_base if env_base else joinuser("~", ".local")
    183 
    184 
    185 def _parse_makefile(filename, vars=None):
    186     """Parse a Makefile-style file.
    187 
    188     A dictionary containing name/value pairs is returned.  If an
    189     optional dictionary is passed in as the second argument, it is
    190     used instead of a new dictionary.
    191     """
    192     import re
    193     # Regexes needed for parsing Makefile (and similar syntaxes,

    194     # like old-style Setup files).

    195     _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
    196     _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
    197     _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
    198 
    199     if vars is None:
    200         vars = {}
    201     done = {}
    202     notdone = {}
    203 
    204     with open(filename) as f:
    205         lines = f.readlines()
    206 
    207     for line in lines:
    208         if line.startswith('#') or line.strip() == '':
    209             continue
    210         m = _variable_rx.match(line)
    211         if m:
    212             n, v = m.group(1, 2)
    213             v = v.strip()
    214             # `$$' is a literal `$' in make

    215             tmpv = v.replace('$$', '')
    216 
    217             if "$" in tmpv:
    218                 notdone[n] = v
    219             else:
    220                 try:
    221                     v = int(v)
    222                 except ValueError:
    223                     # insert literal `$'

    224                     done[n] = v.replace('$$', '$')
    225                 else:
    226                     done[n] = v
    227 
    228     # do variable interpolation here

    229     while notdone:
    230         for name in notdone.keys():
    231             value = notdone[name]
    232             m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
    233             if m:
    234                 n = m.group(1)
    235                 found = True
    236                 if n in done:
    237                     item = str(done[n])
    238                 elif n in notdone:
    239                     # get it on a subsequent round

    240                     found = False
    241                 elif n in os.environ:
    242                     # do it like make: fall back to environment

    243                     item = os.environ[n]
    244                 else:
    245                     done[n] = item = ""
    246                 if found:
    247                     after = value[m.end():]
    248                     value = value[:m.start()] + item + after
    249                     if "$" in after:
    250                         notdone[name] = value
    251                     else:
    252                         try: value = int(value)
    253                         except ValueError:
    254                             done[name] = value.strip()
    255                         else:
    256                             done[name] = value
    257                         del notdone[name]
    258             else:
    259                 # bogus variable reference; just drop it since we can't deal

    260                 del notdone[name]
    261     # strip spurious spaces

    262     for k, v in done.items():
    263         if isinstance(v, str):
    264             done[k] = v.strip()
    265 
    266     # save the results in the global dictionary

    267     vars.update(done)
    268     return vars
    269 
    270 
    271 def _get_makefile_filename():
    272     if _PYTHON_BUILD:
    273         return os.path.join(_PROJECT_BASE, "Makefile")
    274     return os.path.join(get_path('platstdlib'), "config", "Makefile")
    275 
    276 
    277 def _init_posix(vars):
    278     """Initialize the module as appropriate for POSIX systems."""
    279     # load the installed Makefile:

    280     makefile = _get_makefile_filename()
    281     try:
    282         _parse_makefile(makefile, vars)
    283     except IOError, e:
    284         msg = "invalid Python installation: unable to open %s" % makefile
    285         if hasattr(e, "strerror"):
    286             msg = msg + " (%s)" % e.strerror
    287         raise IOError(msg)
    288 
    289     # load the installed pyconfig.h:

    290     config_h = get_config_h_filename()
    291     try:
    292         with open(config_h) as f:
    293             parse_config_h(f, vars)
    294     except IOError, e:
    295         msg = "invalid Python installation: unable to open %s" % config_h
    296         if hasattr(e, "strerror"):
    297             msg = msg + " (%s)" % e.strerror
    298         raise IOError(msg)
    299 
    300     # On AIX, there are wrong paths to the linker scripts in the Makefile

    301     # -- these paths are relative to the Python source, but when installed

    302     # the scripts are in another directory.

    303     if _PYTHON_BUILD:
    304         vars['LDSHARED'] = vars['BLDSHARED']
    305 
    306 def _init_non_posix(vars):
    307     """Initialize the module as appropriate for NT"""
    308     # set basic install directories

    309     vars['LIBDEST'] = get_path('stdlib')
    310     vars['BINLIBDEST'] = get_path('platstdlib')
    311     vars['INCLUDEPY'] = get_path('include')
    312     vars['SO'] = '.pyd'
    313     vars['EXE'] = '.exe'
    314     vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
    315     vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
    316 
    317 #

    318 # public APIs

    319 #

    320 
    321 
    322 def parse_config_h(fp, vars=None):
    323     """Parse a config.h-style file.
    324 
    325     A dictionary containing name/value pairs is returned.  If an
    326     optional dictionary is passed in as the second argument, it is
    327     used instead of a new dictionary.
    328     """
    329     import re
    330     if vars is None:
    331         vars = {}
    332     define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
    333     undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
    334 
    335     while True:
    336         line = fp.readline()
    337         if not line:
    338             break
    339         m = define_rx.match(line)
    340         if m:
    341             n, v = m.group(1, 2)
    342             try: v = int(v)
    343             except ValueError: pass
    344             vars[n] = v
    345         else:
    346             m = undef_rx.match(line)
    347             if m:
    348                 vars[m.group(1)] = 0
    349     return vars
    350 
    351 def get_config_h_filename():
    352     """Returns the path of pyconfig.h."""
    353     if _PYTHON_BUILD:
    354         if os.name == "nt":
    355             inc_dir = os.path.join(_PROJECT_BASE, "PC")
    356         else:
    357             inc_dir = _PROJECT_BASE
    358     else:
    359         inc_dir = get_path('platinclude')
    360     return os.path.join(inc_dir, 'pyconfig.h')
    361 
    362 def get_scheme_names():
    363     """Returns a tuple containing the schemes names."""
    364     schemes = _INSTALL_SCHEMES.keys()
    365     schemes.sort()
    366     return tuple(schemes)
    367 
    368 def get_path_names():
    369     """Returns a tuple containing the paths names."""
    370     return _SCHEME_KEYS
    371 
    372 def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
    373     """Returns a mapping containing an install scheme.
    374 
    375     ``scheme`` is the install scheme name. If not provided, it will
    376     return the default scheme for the current platform.
    377     """
    378     if expand:
    379         return _expand_vars(scheme, vars)
    380     else:
    381         return _INSTALL_SCHEMES[scheme]
    382 
    383 def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True):
    384     """Returns a path corresponding to the scheme.
    385 
    386     ``scheme`` is the install scheme name.
    387     """
    388     return get_paths(scheme, vars, expand)[name]
    389 
    390 def get_config_vars(*args):
    391     """With no arguments, return a dictionary of all configuration
    392     variables relevant for the current platform.
    393 
    394     On Unix, this means every variable defined in Python's installed Makefile;
    395     On Windows and Mac OS it's a much smaller set.
    396 
    397     With arguments, return a list of values that result from looking up
    398     each argument in the configuration variable dictionary.
    399     """
    400     import re
    401     global _CONFIG_VARS
    402     if _CONFIG_VARS is None:
    403         _CONFIG_VARS = {}
    404         # Normalized versions of prefix and exec_prefix are handy to have;

    405         # in fact, these are the standard versions used most places in the

    406         # Distutils.

    407         _CONFIG_VARS['prefix'] = _PREFIX
    408         _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
    409         _CONFIG_VARS['py_version'] = _PY_VERSION
    410         _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
    411         _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2]
    412         _CONFIG_VARS['base'] = _PREFIX
    413         _CONFIG_VARS['platbase'] = _EXEC_PREFIX
    414         _CONFIG_VARS['projectbase'] = _PROJECT_BASE
    415 
    416         if os.name in ('nt', 'os2'):
    417             _init_non_posix(_CONFIG_VARS)
    418         if os.name == 'posix':
    419             _init_posix(_CONFIG_VARS)
    420 
    421         # Setting 'userbase' is done below the call to the

    422         # init function to enable using 'get_config_var' in

    423         # the init-function.

    424         _CONFIG_VARS['userbase'] = _getuserbase()
    425 
    426         if 'srcdir' not in _CONFIG_VARS:
    427             _CONFIG_VARS['srcdir'] = _PROJECT_BASE
    428 
    429         # Convert srcdir into an absolute path if it appears necessary.

    430         # Normally it is relative to the build directory.  However, during

    431         # testing, for example, we might be running a non-installed python

    432         # from a different directory.

    433         if _PYTHON_BUILD and os.name == "posix":
    434             base = _PROJECT_BASE
    435             try:
    436                 cwd = os.getcwd()
    437             except OSError:
    438                 cwd = None
    439             if (not os.path.isabs(_CONFIG_VARS['srcdir']) and
    440                 base != cwd):
    441                 # srcdir is relative and we are not in the same directory

    442                 # as the executable. Assume executable is in the build

    443                 # directory and make srcdir absolute.

    444                 srcdir = os.path.join(base, _CONFIG_VARS['srcdir'])
    445                 _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir)
    446 
    447         if sys.platform == 'darwin':
    448             kernel_version = os.uname()[2] # Kernel version (8.4.3)

    449             major_version = int(kernel_version.split('.')[0])
    450 
    451             if major_version < 8:
    452                 # On Mac OS X before 10.4, check if -arch and -isysroot

    453                 # are in CFLAGS or LDFLAGS and remove them if they are.

    454                 # This is needed when building extensions on a 10.3 system

    455                 # using a universal build of python.

    456                 for key in ('LDFLAGS', 'BASECFLAGS',
    457                         # a number of derived variables. These need to be

    458                         # patched up as well.

    459                         'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    460                     flags = _CONFIG_VARS[key]
    461                     flags = re.sub('-arch\s+\w+\s', ' ', flags)
    462                     flags = re.sub('-isysroot [^ \t]*', ' ', flags)
    463                     _CONFIG_VARS[key] = flags
    464             else:
    465                 # Allow the user to override the architecture flags using

    466                 # an environment variable.

    467                 # NOTE: This name was introduced by Apple in OSX 10.5 and

    468                 # is used by several scripting languages distributed with

    469                 # that OS release.

    470                 if 'ARCHFLAGS' in os.environ:
    471                     arch = os.environ['ARCHFLAGS']
    472                     for key in ('LDFLAGS', 'BASECFLAGS',
    473                         # a number of derived variables. These need to be

    474                         # patched up as well.

    475                         'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    476 
    477                         flags = _CONFIG_VARS[key]
    478                         flags = re.sub('-arch\s+\w+\s', ' ', flags)
    479                         flags = flags + ' ' + arch
    480                         _CONFIG_VARS[key] = flags
    481 
    482                 # If we're on OSX 10.5 or later and the user tries to

    483                 # compiles an extension using an SDK that is not present

    484                 # on the current machine it is better to not use an SDK

    485                 # than to fail.

    486                 #

    487                 # The major usecase for this is users using a Python.org

    488                 # binary installer  on OSX 10.6: that installer uses

    489                 # the 10.4u SDK, but that SDK is not installed by default

    490                 # when you install Xcode.

    491                 #

    492                 CFLAGS = _CONFIG_VARS.get('CFLAGS', '')
    493                 m = re.search('-isysroot\s+(\S+)', CFLAGS)
    494                 if m is not None:
    495                     sdk = m.group(1)
    496                     if not os.path.exists(sdk):
    497                         for key in ('LDFLAGS', 'BASECFLAGS',
    498                              # a number of derived variables. These need to be

    499                              # patched up as well.

    500                             'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
    501 
    502                             flags = _CONFIG_VARS[key]
    503                             flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags)
    504                             _CONFIG_VARS[key] = flags
    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 
    514 def 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 
    522 def 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     if os.name != "posix" or not hasattr(os, 'uname'):
    563         # XXX what about the architecture? NT is Intel or Alpha,

    564         # Mac OS is M68k or PPC, etc.

    565         return sys.platform
    566 
    567     # Try to distinguish various flavours of Unix

    568     osname, host, release, version, machine = os.uname()
    569 
    570     # Convert the OS name to lowercase, remove '/' characters

    571     # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")

    572     osname = osname.lower().replace('/', '')
    573     machine = machine.replace(' ', '_')
    574     machine = machine.replace('/', '-')
    575 
    576     if osname[:5] == "linux":
    577         # At least on Linux/Intel, 'machine' is the processor --

    578         # i386, etc.

    579         # XXX what about Alpha, SPARC, etc?

    580         return  "%s-%s" % (osname, machine)
    581     elif osname[:5] == "sunos":
    582         if release[0] >= "5":           # SunOS 5 == Solaris 2

    583             osname = "solaris"
    584             release = "%d.%s" % (int(release[0]) - 3, release[2:])
    585         # fall through to standard osname-release-machine representation

    586     elif osname[:4] == "irix":              # could be "irix64"!

    587         return "%s-%s" % (osname, release)
    588     elif osname[:3] == "aix":
    589         return "%s-%s.%s" % (osname, version, release)
    590     elif osname[:6] == "cygwin":
    591         osname = "cygwin"
    592         rel_re = re.compile (r'[\d.]+')
    593         m = rel_re.match(release)
    594         if m:
    595             release = m.group()
    596     elif osname[:6] == "darwin":
    597         #

    598         # For our purposes, we'll assume that the system version from

    599         # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set

    600         # to. This makes the compatibility story a bit more sane because the

    601         # machine is going to compile and link as if it were

    602         # MACOSX_DEPLOYMENT_TARGET.

    603         cfgvars = get_config_vars()
    604         macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET')
    605 
    606         if 1:
    607             # Always calculate the release of the running machine,

    608             # needed to determine if we can build fat binaries or not.

    609 
    610             macrelease = macver
    611             # Get the system version. Reading this plist is a documented

    612             # way to get the system version (see the documentation for

    613             # the Gestalt Manager)

    614             try:
    615                 f = open('/System/Library/CoreServices/SystemVersion.plist')
    616             except IOError:
    617                 # We're on a plain darwin box, fall back to the default

    618                 # behaviour.

    619                 pass
    620             else:
    621                 try:
    622                     m = re.search(
    623                             r'<key>ProductUserVisibleVersion</key>\s*' +
    624                             r'<string>(.*?)</string>', f.read())
    625                     if m is not None:
    626                         macrelease = '.'.join(m.group(1).split('.')[:2])
    627                     # else: fall back to the default behaviour

    628                 finally:
    629                     f.close()
    630 
    631         if not macver:
    632             macver = macrelease
    633 
    634         if macver:
    635             release = macver
    636             osname = "macosx"
    637 
    638             if (macrelease + '.') >= '10.4.' and \
    639                     '-arch' in get_config_vars().get('CFLAGS', '').strip():
    640                 # The universal build will build fat binaries, but not on

    641                 # systems before 10.4

    642                 #

    643                 # Try to detect 4-way universal builds, those have machine-type

    644                 # 'universal' instead of 'fat'.

    645 
    646                 machine = 'fat'
    647                 cflags = get_config_vars().get('CFLAGS')
    648 
    649                 archs = re.findall('-arch\s+(\S+)', cflags)
    650                 archs = tuple(sorted(set(archs)))
    651 
    652                 if len(archs) == 1:
    653                     machine = archs[0]
    654                 elif archs == ('i386', 'ppc'):
    655                     machine = 'fat'
    656                 elif archs == ('i386', 'x86_64'):
    657                     machine = 'intel'
    658                 elif archs == ('i386', 'ppc', 'x86_64'):
    659                     machine = 'fat3'
    660                 elif archs == ('ppc64', 'x86_64'):
    661                     machine = 'fat64'
    662                 elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'):
    663                     machine = 'universal'
    664                 else:
    665                     raise ValueError(
    666                        "Don't know machine value for archs=%r"%(archs,))
    667 
    668             elif machine == 'i386':
    669                 # On OSX the machine type returned by uname is always the

    670                 # 32-bit variant, even if the executable architecture is

    671                 # the 64-bit variant

    672                 if sys.maxint >= 2**32:
    673                     machine = 'x86_64'
    674 
    675             elif machine in ('PowerPC', 'Power_Macintosh'):
    676                 # Pick a sane name for the PPC architecture.

    677                 # See 'i386' case

    678                 if sys.maxint >= 2**32:
    679                     machine = 'ppc64'
    680                 else:
    681                     machine = 'ppc'
    682 
    683     return "%s-%s-%s" % (osname, release, machine)
    684 
    685 
    686 def get_python_version():
    687     return _PY_VERSION_SHORT
    688