PageRenderTime 143ms CodeModel.GetById 21ms app.highlight 103ms RepoModel.GetById 1ms app.codeStats 1ms

/Lib/platform.py

http://unladen-swallow.googlecode.com/
Python | 1566 lines | 1418 code | 19 blank | 129 comment | 30 complexity | 35699798a9af8bac12f5c207c6248085 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1#!/usr/bin/env python
   2
   3""" This module tries to retrieve as much platform-identifying data as
   4    possible. It makes this information available via function APIs.
   5
   6    If called from the command line, it prints the platform
   7    information concatenated as single string to stdout. The output
   8    format is useable as part of a filename.
   9
  10"""
  11#    This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
  12#    If you find problems, please submit bug reports/patches via the
  13#    Python SourceForge Project Page and assign them to "lemburg".
  14#
  15#    Note: Please keep this module compatible to Python 1.5.2.
  16#
  17#    Still needed:
  18#    * more support for WinCE
  19#    * support for MS-DOS (PythonDX ?)
  20#    * support for Amiga and other still unsupported platforms running Python
  21#    * support for additional Linux distributions
  22#
  23#    Many thanks to all those who helped adding platform-specific
  24#    checks (in no particular order):
  25#
  26#      Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
  27#      Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
  28#      Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
  29#      Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
  30#      Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
  31#      Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
  32#
  33#    History:
  34#
  35#    <see CVS and SVN checkin messages for history>
  36#
  37#    1.0.6 - added linux_distribution()
  38#    1.0.5 - fixed Java support to allow running the module on Jython
  39#    1.0.4 - added IronPython support
  40#    1.0.3 - added normalization of Windows system name
  41#    1.0.2 - added more Windows support
  42#    1.0.1 - reformatted to make doc.py happy
  43#    1.0.0 - reformatted a bit and checked into Python CVS
  44#    0.8.0 - added sys.version parser and various new access
  45#            APIs (python_version(), python_compiler(), etc.)
  46#    0.7.2 - fixed architecture() to use sizeof(pointer) where available
  47#    0.7.1 - added support for Caldera OpenLinux
  48#    0.7.0 - some fixes for WinCE; untabified the source file
  49#    0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
  50#            vms_lib.getsyi() configured
  51#    0.6.1 - added code to prevent 'uname -p' on platforms which are
  52#            known not to support it
  53#    0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
  54#            did some cleanup of the interfaces - some APIs have changed
  55#    0.5.5 - fixed another type in the MacOS code... should have
  56#            used more coffee today ;-)
  57#    0.5.4 - fixed a few typos in the MacOS code
  58#    0.5.3 - added experimental MacOS support; added better popen()
  59#            workarounds in _syscmd_ver() -- still not 100% elegant
  60#            though
  61#    0.5.2 - fixed uname() to return '' instead of 'unknown' in all
  62#            return values (the system uname command tends to return
  63#            'unknown' instead of just leaving the field emtpy)
  64#    0.5.1 - included code for slackware dist; added exception handlers
  65#            to cover up situations where platforms don't have os.popen
  66#            (e.g. Mac) or fail on socket.gethostname(); fixed libc
  67#            detection RE
  68#    0.5.0 - changed the API names referring to system commands to *syscmd*;
  69#            added java_ver(); made syscmd_ver() a private
  70#            API (was system_ver() in previous versions) -- use uname()
  71#            instead; extended the win32_ver() to also return processor
  72#            type information
  73#    0.4.0 - added win32_ver() and modified the platform() output for WinXX
  74#    0.3.4 - fixed a bug in _follow_symlinks()
  75#    0.3.3 - fixed popen() and "file" command invokation bugs
  76#    0.3.2 - added architecture() API and support for it in platform()
  77#    0.3.1 - fixed syscmd_ver() RE to support Windows NT
  78#    0.3.0 - added system alias support
  79#    0.2.3 - removed 'wince' again... oh well.
  80#    0.2.2 - added 'wince' to syscmd_ver() supported platforms
  81#    0.2.1 - added cache logic and changed the platform string format
  82#    0.2.0 - changed the API to use functions instead of module globals
  83#            since some action take too long to be run on module import
  84#    0.1.0 - first release
  85#
  86#    You can always get the latest version of this module at:
  87#
  88#             http://www.egenix.com/files/python/platform.py
  89#
  90#    If that URL should fail, try contacting the author.
  91
  92__copyright__ = """
  93    Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
  94    Copyright (c) 2000-2008, eGenix.com Software GmbH; mailto:info@egenix.com
  95
  96    Permission to use, copy, modify, and distribute this software and its
  97    documentation for any purpose and without fee or royalty is hereby granted,
  98    provided that the above copyright notice appear in all copies and that
  99    both that copyright notice and this permission notice appear in
 100    supporting documentation or portions thereof, including modifications,
 101    that you make.
 102
 103    EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
 104    THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 105    FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
 106    INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 107    FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 108    NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 109    WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
 110
 111"""
 112
 113__version__ = '1.0.6'
 114
 115import sys,string,os,re
 116
 117### Platform specific APIs
 118
 119_libc_search = re.compile(r'(__libc_init)'
 120                          '|'
 121                          '(GLIBC_([0-9.]+))'
 122                          '|'
 123                          '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)')
 124
 125def libc_ver(executable=sys.executable,lib='',version='',
 126
 127             chunksize=2048):
 128
 129    """ Tries to determine the libc version that the file executable
 130        (which defaults to the Python interpreter) is linked against.
 131
 132        Returns a tuple of strings (lib,version) which default to the
 133        given parameters in case the lookup fails.
 134
 135        Note that the function has intimate knowledge of how different
 136        libc versions add symbols to the executable and thus is probably
 137        only useable for executables compiled using gcc.
 138
 139        The file is read and scanned in chunks of chunksize bytes.
 140
 141    """
 142    if hasattr(os.path, 'realpath'):
 143        # Python 2.2 introduced os.path.realpath(); it is used
 144        # here to work around problems with Cygwin not being
 145        # able to open symlinks for reading
 146        executable = os.path.realpath(executable)
 147    f = open(executable,'rb')
 148    binary = f.read(chunksize)
 149    pos = 0
 150    while 1:
 151        m = _libc_search.search(binary,pos)
 152        if not m:
 153            binary = f.read(chunksize)
 154            if not binary:
 155                break
 156            pos = 0
 157            continue
 158        libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
 159        if libcinit and not lib:
 160            lib = 'libc'
 161        elif glibc:
 162            if lib != 'glibc':
 163                lib = 'glibc'
 164                version = glibcversion
 165            elif glibcversion > version:
 166                version = glibcversion
 167        elif so:
 168            if lib != 'glibc':
 169                lib = 'libc'
 170                if soversion > version:
 171                    version = soversion
 172                if threads and version[-len(threads):] != threads:
 173                    version = version + threads
 174        pos = m.end()
 175    f.close()
 176    return lib,version
 177
 178def _dist_try_harder(distname,version,id):
 179
 180    """ Tries some special tricks to get the distribution
 181        information in case the default method fails.
 182
 183        Currently supports older SuSE Linux, Caldera OpenLinux and
 184        Slackware Linux distributions.
 185
 186    """
 187    if os.path.exists('/var/adm/inst-log/info'):
 188        # SuSE Linux stores distribution information in that file
 189        info = open('/var/adm/inst-log/info').readlines()
 190        distname = 'SuSE'
 191        for line in info:
 192            tv = string.split(line)
 193            if len(tv) == 2:
 194                tag,value = tv
 195            else:
 196                continue
 197            if tag == 'MIN_DIST_VERSION':
 198                version = string.strip(value)
 199            elif tag == 'DIST_IDENT':
 200                values = string.split(value,'-')
 201                id = values[2]
 202        return distname,version,id
 203
 204    if os.path.exists('/etc/.installed'):
 205        # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
 206        info = open('/etc/.installed').readlines()
 207        for line in info:
 208            pkg = string.split(line,'-')
 209            if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
 210                # XXX does Caldera support non Intel platforms ? If yes,
 211                #     where can we find the needed id ?
 212                return 'OpenLinux',pkg[1],id
 213
 214    if os.path.isdir('/usr/lib/setup'):
 215        # Check for slackware verson tag file (thanks to Greg Andruk)
 216        verfiles = os.listdir('/usr/lib/setup')
 217        for n in range(len(verfiles)-1, -1, -1):
 218            if verfiles[n][:14] != 'slack-version-':
 219                del verfiles[n]
 220        if verfiles:
 221            verfiles.sort()
 222            distname = 'slackware'
 223            version = verfiles[-1][14:]
 224            return distname,version,id
 225
 226    return distname,version,id
 227
 228_release_filename = re.compile(r'(\w+)[-_](release|version)')
 229_lsb_release_version = re.compile(r'(.+)'
 230                                   ' release '
 231                                   '([\d.]+)'
 232                                   '[^(]*(?:\((.+)\))?')
 233_release_version = re.compile(r'([^0-9]+)'
 234                               '(?: release )?'
 235                               '([\d.]+)'
 236                               '[^(]*(?:\((.+)\))?')
 237
 238# See also http://www.novell.com/coolsolutions/feature/11251.html
 239# and http://linuxmafia.com/faq/Admin/release-files.html
 240# and http://data.linux-ntfs.org/rpm/whichrpm
 241# and http://www.die.net/doc/linux/man/man1/lsb_release.1.html
 242
 243_supported_dists = (
 244    'SuSE', 'debian', 'fedora', 'redhat', 'centos',
 245    'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',
 246    'UnitedLinux', 'turbolinux')
 247
 248def _parse_release_file(firstline):
 249
 250    # Parse the first line
 251    m = _lsb_release_version.match(firstline)
 252    if m is not None:
 253        # LSB format: "distro release x.x (codename)"
 254        return tuple(m.groups())
 255
 256    # Pre-LSB format: "distro x.x (codename)"
 257    m = _release_version.match(firstline)
 258    if m is not None:
 259        return tuple(m.groups())
 260
 261    # Unkown format... take the first two words
 262    l = string.split(string.strip(firstline))
 263    if l:
 264        version = l[0]
 265        if len(l) > 1:
 266            id = l[1]
 267        else:
 268            id = ''
 269    return '', version, id
 270
 271def _test_parse_release_file():
 272
 273    for input, output in (
 274        # Examples of release file contents:
 275        ('SuSE Linux 9.3 (x86-64)', ('SuSE Linux ', '9.3', 'x86-64'))
 276        ('SUSE LINUX 10.1 (X86-64)', ('SUSE LINUX ', '10.1', 'X86-64'))
 277        ('SUSE LINUX 10.1 (i586)', ('SUSE LINUX ', '10.1', 'i586'))
 278        ('Fedora Core release 5 (Bordeaux)', ('Fedora Core', '5', 'Bordeaux'))
 279        ('Red Hat Linux release 8.0 (Psyche)', ('Red Hat Linux', '8.0', 'Psyche'))
 280        ('Red Hat Linux release 9 (Shrike)', ('Red Hat Linux', '9', 'Shrike'))
 281        ('Red Hat Enterprise Linux release 4 (Nahant)', ('Red Hat Enterprise Linux', '4', 'Nahant'))
 282        ('CentOS release 4', ('CentOS', '4', None))
 283        ('Rocks release 4.2.1 (Cydonia)', ('Rocks', '4.2.1', 'Cydonia'))
 284        ):
 285        parsed = _parse_release_file(input)
 286        if parsed != output:
 287            print (input, parsed)
 288
 289def linux_distribution(distname='', version='', id='',
 290
 291                       supported_dists=_supported_dists,
 292                       full_distribution_name=1):
 293
 294    """ Tries to determine the name of the Linux OS distribution name.
 295
 296        The function first looks for a distribution release file in
 297        /etc and then reverts to _dist_try_harder() in case no
 298        suitable files are found.
 299
 300        supported_dists may be given to define the set of Linux
 301        distributions to look for. It defaults to a list of currently
 302        supported Linux distributions identified by their release file
 303        name.
 304
 305        If full_distribution_name is true (default), the full
 306        distribution read from the OS is returned. Otherwise the short
 307        name taken from supported_dists is used.
 308
 309        Returns a tuple (distname,version,id) which default to the
 310        args given as parameters.
 311
 312    """
 313    try:
 314        etc = os.listdir('/etc')
 315    except os.error:
 316        # Probably not a Unix system
 317        return distname,version,id
 318    etc.sort()
 319    for file in etc:
 320        m = _release_filename.match(file)
 321        if m is not None:
 322            _distname,dummy = m.groups()
 323            if _distname in supported_dists:
 324                distname = _distname
 325                break
 326    else:
 327        return _dist_try_harder(distname,version,id)
 328
 329    # Read the first line
 330    f = open('/etc/'+file, 'r')
 331    firstline = f.readline()
 332    f.close()
 333    _distname, _version, _id = _parse_release_file(firstline)
 334
 335    if _distname and full_distribution_name:
 336        distname = _distname
 337    if _version:
 338        version = _version
 339    if _id:
 340        id = _id
 341    return distname, version, id
 342
 343# To maintain backwards compatibility:
 344
 345def dist(distname='',version='',id='',
 346
 347         supported_dists=_supported_dists):
 348
 349    """ Tries to determine the name of the Linux OS distribution name.
 350
 351        The function first looks for a distribution release file in
 352        /etc and then reverts to _dist_try_harder() in case no
 353        suitable files are found.
 354
 355        Returns a tuple (distname,version,id) which default to the
 356        args given as parameters.
 357
 358    """
 359    return linux_distribution(distname, version, id,
 360                              supported_dists=supported_dists,
 361                              full_distribution_name=0)
 362
 363class _popen:
 364
 365    """ Fairly portable (alternative) popen implementation.
 366
 367        This is mostly needed in case os.popen() is not available, or
 368        doesn't work as advertised, e.g. in Win9X GUI programs like
 369        PythonWin or IDLE.
 370
 371        Writing to the pipe is currently not supported.
 372
 373    """
 374    tmpfile = ''
 375    pipe = None
 376    bufsize = None
 377    mode = 'r'
 378
 379    def __init__(self,cmd,mode='r',bufsize=None):
 380
 381        if mode != 'r':
 382            raise ValueError,'popen()-emulation only supports read mode'
 383        import tempfile
 384        self.tmpfile = tmpfile = tempfile.mktemp()
 385        os.system(cmd + ' > %s' % tmpfile)
 386        self.pipe = open(tmpfile,'rb')
 387        self.bufsize = bufsize
 388        self.mode = mode
 389
 390    def read(self):
 391
 392        return self.pipe.read()
 393
 394    def readlines(self):
 395
 396        if self.bufsize is not None:
 397            return self.pipe.readlines()
 398
 399    def close(self,
 400
 401              remove=os.unlink,error=os.error):
 402
 403        if self.pipe:
 404            rc = self.pipe.close()
 405        else:
 406            rc = 255
 407        if self.tmpfile:
 408            try:
 409                remove(self.tmpfile)
 410            except error:
 411                pass
 412        return rc
 413
 414    # Alias
 415    __del__ = close
 416
 417def popen(cmd, mode='r', bufsize=None):
 418
 419    """ Portable popen() interface.
 420    """
 421    # Find a working popen implementation preferring win32pipe.popen
 422    # over os.popen over _popen
 423    popen = None
 424    if os.environ.get('OS','') == 'Windows_NT':
 425        # On NT win32pipe should work; on Win9x it hangs due to bugs
 426        # in the MS C lib (see MS KnowledgeBase article Q150956)
 427        try:
 428            import win32pipe
 429        except ImportError:
 430            pass
 431        else:
 432            popen = win32pipe.popen
 433    if popen is None:
 434        if hasattr(os,'popen'):
 435            popen = os.popen
 436            # Check whether it works... it doesn't in GUI programs
 437            # on Windows platforms
 438            if sys.platform == 'win32': # XXX Others too ?
 439                try:
 440                    popen('')
 441                except os.error:
 442                    popen = _popen
 443        else:
 444            popen = _popen
 445    if bufsize is None:
 446        return popen(cmd,mode)
 447    else:
 448        return popen(cmd,mode,bufsize)
 449
 450def _norm_version(version, build=''):
 451
 452    """ Normalize the version and build strings and return a single
 453        version string using the format major.minor.build (or patchlevel).
 454    """
 455    l = string.split(version,'.')
 456    if build:
 457        l.append(build)
 458    try:
 459        ints = map(int,l)
 460    except ValueError:
 461        strings = l
 462    else:
 463        strings = map(str,ints)
 464    version = string.join(strings[:3],'.')
 465    return version
 466
 467_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
 468                         '.*'
 469                         'Version ([\d.]+))')
 470
 471def _syscmd_ver(system='', release='', version='',
 472
 473               supported_platforms=('win32','win16','dos','os2')):
 474
 475    """ Tries to figure out the OS version used and returns
 476        a tuple (system,release,version).
 477
 478        It uses the "ver" shell command for this which is known
 479        to exists on Windows, DOS and OS/2. XXX Others too ?
 480
 481        In case this fails, the given parameters are used as
 482        defaults.
 483
 484    """
 485    if sys.platform not in supported_platforms:
 486        return system,release,version
 487
 488    # Try some common cmd strings
 489    for cmd in ('ver','command /c ver','cmd /c ver'):
 490        try:
 491            pipe = popen(cmd)
 492            info = pipe.read()
 493            if pipe.close():
 494                raise os.error,'command failed'
 495            # XXX How can I supress shell errors from being written
 496            #     to stderr ?
 497        except os.error,why:
 498            #print 'Command %s failed: %s' % (cmd,why)
 499            continue
 500        except IOError,why:
 501            #print 'Command %s failed: %s' % (cmd,why)
 502            continue
 503        else:
 504            break
 505    else:
 506        return system,release,version
 507
 508    # Parse the output
 509    info = string.strip(info)
 510    m = _ver_output.match(info)
 511    if m is not None:
 512        system,release,version = m.groups()
 513        # Strip trailing dots from version and release
 514        if release[-1] == '.':
 515            release = release[:-1]
 516        if version[-1] == '.':
 517            version = version[:-1]
 518        # Normalize the version and build strings (eliminating additional
 519        # zeros)
 520        version = _norm_version(version)
 521    return system,release,version
 522
 523def _win32_getvalue(key,name,default=''):
 524
 525    """ Read a value for name from the registry key.
 526
 527        In case this fails, default is returned.
 528
 529    """
 530    try:
 531        # Use win32api if available
 532        from win32api import RegQueryValueEx
 533    except ImportError:
 534        # On Python 2.0 and later, emulate using _winreg
 535        import _winreg
 536        RegQueryValueEx = _winreg.QueryValueEx
 537    try:
 538        return RegQueryValueEx(key,name)
 539    except:
 540        return default
 541
 542def win32_ver(release='',version='',csd='',ptype=''):
 543
 544    """ Get additional version information from the Windows Registry
 545        and return a tuple (version,csd,ptype) referring to version
 546        number, CSD level and OS type (multi/single
 547        processor).
 548
 549        As a hint: ptype returns 'Uniprocessor Free' on single
 550        processor NT machines and 'Multiprocessor Free' on multi
 551        processor machines. The 'Free' refers to the OS version being
 552        free of debugging code. It could also state 'Checked' which
 553        means the OS version uses debugging code, i.e. code that
 554        checks arguments, ranges, etc. (Thomas Heller).
 555
 556        Note: this function works best with Mark Hammond's win32
 557        package installed, but also on Python 2.3 and later. It
 558        obviously only runs on Win32 compatible platforms.
 559
 560    """
 561    # XXX Is there any way to find out the processor type on WinXX ?
 562    # XXX Is win32 available on Windows CE ?
 563    #
 564    # Adapted from code posted by Karl Putland to comp.lang.python.
 565    #
 566    # The mappings between reg. values and release names can be found
 567    # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
 568
 569    # Import the needed APIs
 570    try:
 571        import win32api
 572        from win32api import RegQueryValueEx, RegOpenKeyEx, \
 573             RegCloseKey, GetVersionEx
 574        from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
 575             VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
 576    except ImportError:
 577        # Emulate the win32api module using Python APIs
 578        try:
 579            sys.getwindowsversion
 580        except AttributeError:
 581            # No emulation possible, so return the defaults...
 582            return release,version,csd,ptype
 583        else:
 584            # Emulation using _winreg (added in Python 2.0) and
 585            # sys.getwindowsversion() (added in Python 2.3)
 586            import _winreg
 587            GetVersionEx = sys.getwindowsversion
 588            RegQueryValueEx = _winreg.QueryValueEx
 589            RegOpenKeyEx = _winreg.OpenKeyEx
 590            RegCloseKey = _winreg.CloseKey
 591            HKEY_LOCAL_MACHINE = _winreg.HKEY_LOCAL_MACHINE
 592            VER_PLATFORM_WIN32_WINDOWS = 1
 593            VER_PLATFORM_WIN32_NT = 2
 594            VER_NT_WORKSTATION = 1
 595
 596    # Find out the registry key and some general version infos
 597    maj,min,buildno,plat,csd = GetVersionEx()
 598    version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
 599    if csd[:13] == 'Service Pack ':
 600        csd = 'SP' + csd[13:]
 601    if plat == VER_PLATFORM_WIN32_WINDOWS:
 602        regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
 603        # Try to guess the release name
 604        if maj == 4:
 605            if min == 0:
 606                release = '95'
 607            elif min == 10:
 608                release = '98'
 609            elif min == 90:
 610                release = 'Me'
 611            else:
 612                release = 'postMe'
 613        elif maj == 5:
 614            release = '2000'
 615    elif plat == VER_PLATFORM_WIN32_NT:
 616        regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
 617        if maj <= 4:
 618            release = 'NT'
 619        elif maj == 5:
 620            if min == 0:
 621                release = '2000'
 622            elif min == 1:
 623                release = 'XP'
 624            elif min == 2:
 625                release = '2003Server'
 626            else:
 627                release = 'post2003'
 628        elif maj == 6:
 629            if min == 0:
 630                # Per http://msdn2.microsoft.com/en-us/library/ms724429.aspx
 631                try:
 632                    productType = GetVersionEx(1)[8]
 633                except TypeError:
 634                    # sys.getwindowsversion() doesn't take any arguments, so
 635                    # we cannot detect 2008 Server that way.
 636                    # XXX Add some other means of detecting 2008 Server ?!
 637                    release = 'Vista'
 638                else:
 639                    if productType == VER_NT_WORKSTATION:
 640                        release = 'Vista'
 641                    else:
 642                        release = '2008Server'
 643            else:
 644                release = 'post2008Server'
 645    else:
 646        if not release:
 647            # E.g. Win3.1 with win32s
 648            release = '%i.%i' % (maj,min)
 649        return release,version,csd,ptype
 650
 651    # Open the registry key
 652    try:
 653        keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
 654        # Get a value to make sure the key exists...
 655        RegQueryValueEx(keyCurVer, 'SystemRoot')
 656    except:
 657        return release,version,csd,ptype
 658
 659    # Parse values
 660    #subversion = _win32_getvalue(keyCurVer,
 661    #                            'SubVersionNumber',
 662    #                            ('',1))[0]
 663    #if subversion:
 664    #   release = release + subversion # 95a, 95b, etc.
 665    build = _win32_getvalue(keyCurVer,
 666                            'CurrentBuildNumber',
 667                            ('',1))[0]
 668    ptype = _win32_getvalue(keyCurVer,
 669                           'CurrentType',
 670                           (ptype,1))[0]
 671
 672    # Normalize version
 673    version = _norm_version(version,build)
 674
 675    # Close key
 676    RegCloseKey(keyCurVer)
 677    return release,version,csd,ptype
 678
 679def _mac_ver_lookup(selectors,default=None):
 680
 681    from gestalt import gestalt
 682    import MacOS
 683    l = []
 684    append = l.append
 685    for selector in selectors:
 686        try:
 687            append(gestalt(selector))
 688        except (RuntimeError, MacOS.Error):
 689            append(default)
 690    return l
 691
 692def _bcd2str(bcd):
 693
 694    return hex(bcd)[2:]
 695
 696def mac_ver(release='',versioninfo=('','',''),machine=''):
 697
 698    """ Get MacOS version information and return it as tuple (release,
 699        versioninfo, machine) with versioninfo being a tuple (version,
 700        dev_stage, non_release_version).
 701
 702        Entries which cannot be determined are set to the paramter values
 703        which default to ''. All tuple entries are strings.
 704
 705        Thanks to Mark R. Levinson for mailing documentation links and
 706        code examples for this function. Documentation for the
 707        gestalt() API is available online at:
 708
 709           http://www.rgaros.nl/gestalt/
 710
 711    """
 712    # Check whether the version info module is available
 713    try:
 714        import gestalt
 715        import MacOS
 716    except ImportError:
 717        return release,versioninfo,machine
 718    # Get the infos
 719    sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa'))
 720    # Decode the infos
 721    if sysv:
 722        major = (sysv & 0xFF00) >> 8
 723        minor = (sysv & 0x00F0) >> 4
 724        patch = (sysv & 0x000F)
 725
 726        if (major, minor) >= (10, 4):
 727            # the 'sysv' gestald cannot return patchlevels
 728            # higher than 9. Apple introduced 3 new
 729            # gestalt codes in 10.4 to deal with this
 730            # issue (needed because patch levels can
 731            # run higher than 9, such as 10.4.11)
 732            major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
 733            release = '%i.%i.%i' %(major, minor, patch)
 734        else:
 735            release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
 736
 737    if sysu:
 738        # NOTE: this block is left as documentation of the
 739        # intention of this function, the 'sysu' gestalt is no
 740        # longer available and there are no alternatives.
 741        major =  int((sysu & 0xFF000000L) >> 24)
 742        minor =  (sysu & 0x00F00000) >> 20
 743        bugfix = (sysu & 0x000F0000) >> 16
 744        stage =  (sysu & 0x0000FF00) >> 8
 745        nonrel = (sysu & 0x000000FF)
 746        version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix)
 747        nonrel = _bcd2str(nonrel)
 748        stage = {0x20:'development',
 749                 0x40:'alpha',
 750                 0x60:'beta',
 751                 0x80:'final'}.get(stage,'')
 752        versioninfo = (version,stage,nonrel)
 753
 754
 755    if sysa:
 756        machine = {0x1: '68k',
 757                   0x2: 'PowerPC',
 758                   0xa: 'i386'}.get(sysa,'')
 759    return release,versioninfo,machine
 760
 761def _java_getprop(name,default):
 762
 763    from java.lang import System
 764    try:
 765        value = System.getProperty(name)
 766        if value is None:
 767            return default
 768        return value
 769    except AttributeError:
 770        return default
 771
 772def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
 773
 774    """ Version interface for Jython.
 775
 776        Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
 777        a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
 778        tuple (os_name,os_version,os_arch).
 779
 780        Values which cannot be determined are set to the defaults
 781        given as parameters (which all default to '').
 782
 783    """
 784    # Import the needed APIs
 785    try:
 786        import java.lang
 787    except ImportError:
 788        return release,vendor,vminfo,osinfo
 789
 790    vendor = _java_getprop('java.vendor', vendor)
 791    release = _java_getprop('java.version', release)
 792    vm_name, vm_release, vm_vendor = vminfo
 793    vm_name = _java_getprop('java.vm.name', vm_name)
 794    vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
 795    vm_release = _java_getprop('java.vm.version', vm_release)
 796    vminfo = vm_name, vm_release, vm_vendor
 797    os_name, os_version, os_arch = osinfo
 798    os_arch = _java_getprop('java.os.arch', os_arch)
 799    os_name = _java_getprop('java.os.name', os_name)
 800    os_version = _java_getprop('java.os.version', os_version)
 801    osinfo = os_name, os_version, os_arch
 802
 803    return release, vendor, vminfo, osinfo
 804
 805### System name aliasing
 806
 807def system_alias(system,release,version):
 808
 809    """ Returns (system,release,version) aliased to common
 810        marketing names used for some systems.
 811
 812        It also does some reordering of the information in some cases
 813        where it would otherwise cause confusion.
 814
 815    """
 816    if system == 'Rhapsody':
 817        # Apple's BSD derivative
 818        # XXX How can we determine the marketing release number ?
 819        return 'MacOS X Server',system+release,version
 820
 821    elif system == 'SunOS':
 822        # Sun's OS
 823        if release < '5':
 824            # These releases use the old name SunOS
 825            return system,release,version
 826        # Modify release (marketing release = SunOS release - 3)
 827        l = string.split(release,'.')
 828        if l:
 829            try:
 830                major = int(l[0])
 831            except ValueError:
 832                pass
 833            else:
 834                major = major - 3
 835                l[0] = str(major)
 836                release = string.join(l,'.')
 837        if release < '6':
 838            system = 'Solaris'
 839        else:
 840            # XXX Whatever the new SunOS marketing name is...
 841            system = 'Solaris'
 842
 843    elif system == 'IRIX64':
 844        # IRIX reports IRIX64 on platforms with 64-bit support; yet it
 845        # is really a version and not a different platform, since 32-bit
 846        # apps are also supported..
 847        system = 'IRIX'
 848        if version:
 849            version = version + ' (64bit)'
 850        else:
 851            version = '64bit'
 852
 853    elif system in ('win32','win16'):
 854        # In case one of the other tricks
 855        system = 'Windows'
 856
 857    return system,release,version
 858
 859### Various internal helpers
 860
 861def _platform(*args):
 862
 863    """ Helper to format the platform string in a filename
 864        compatible format e.g. "system-version-machine".
 865    """
 866    # Format the platform string
 867    platform = string.join(
 868        map(string.strip,
 869            filter(len, args)),
 870        '-')
 871
 872    # Cleanup some possible filename obstacles...
 873    replace = string.replace
 874    platform = replace(platform,' ','_')
 875    platform = replace(platform,'/','-')
 876    platform = replace(platform,'\\','-')
 877    platform = replace(platform,':','-')
 878    platform = replace(platform,';','-')
 879    platform = replace(platform,'"','-')
 880    platform = replace(platform,'(','-')
 881    platform = replace(platform,')','-')
 882
 883    # No need to report 'unknown' information...
 884    platform = replace(platform,'unknown','')
 885
 886    # Fold '--'s and remove trailing '-'
 887    while 1:
 888        cleaned = replace(platform,'--','-')
 889        if cleaned == platform:
 890            break
 891        platform = cleaned
 892    while platform[-1] == '-':
 893        platform = platform[:-1]
 894
 895    return platform
 896
 897def _node(default=''):
 898
 899    """ Helper to determine the node name of this machine.
 900    """
 901    try:
 902        import socket
 903    except ImportError:
 904        # No sockets...
 905        return default
 906    try:
 907        return socket.gethostname()
 908    except socket.error:
 909        # Still not working...
 910        return default
 911
 912# os.path.abspath is new in Python 1.5.2:
 913if not hasattr(os.path,'abspath'):
 914
 915    def _abspath(path,
 916
 917                 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
 918                 normpath=os.path.normpath):
 919
 920        if not isabs(path):
 921            path = join(getcwd(), path)
 922        return normpath(path)
 923
 924else:
 925
 926    _abspath = os.path.abspath
 927
 928def _follow_symlinks(filepath):
 929
 930    """ In case filepath is a symlink, follow it until a
 931        real file is reached.
 932    """
 933    filepath = _abspath(filepath)
 934    while os.path.islink(filepath):
 935        filepath = os.path.normpath(
 936            os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
 937    return filepath
 938
 939def _syscmd_uname(option,default=''):
 940
 941    """ Interface to the system's uname command.
 942    """
 943    if sys.platform in ('dos','win32','win16','os2'):
 944        # XXX Others too ?
 945        return default
 946    try:
 947        f = os.popen('uname %s 2> /dev/null' % option)
 948    except (AttributeError,os.error):
 949        return default
 950    output = string.strip(f.read())
 951    rc = f.close()
 952    if not output or rc:
 953        return default
 954    else:
 955        return output
 956
 957def _syscmd_file(target,default=''):
 958
 959    """ Interface to the system's file command.
 960
 961        The function uses the -b option of the file command to have it
 962        ommit the filename in its output and if possible the -L option
 963        to have the command follow symlinks. It returns default in
 964        case the command should fail.
 965
 966    """
 967    if sys.platform in ('dos','win32','win16','os2'):
 968        # XXX Others too ?
 969        return default
 970    target = _follow_symlinks(target)
 971    try:
 972        f = os.popen('file "%s" 2> /dev/null' % target)
 973    except (AttributeError,os.error):
 974        return default
 975    output = string.strip(f.read())
 976    rc = f.close()
 977    if not output or rc:
 978        return default
 979    else:
 980        return output
 981
 982### Information about the used architecture
 983
 984# Default values for architecture; non-empty strings override the
 985# defaults given as parameters
 986_default_architecture = {
 987    'win32': ('','WindowsPE'),
 988    'win16': ('','Windows'),
 989    'dos': ('','MSDOS'),
 990}
 991
 992_architecture_split = re.compile(r'[\s,]').split
 993
 994def architecture(executable=sys.executable,bits='',linkage=''):
 995
 996    """ Queries the given executable (defaults to the Python interpreter
 997        binary) for various architecture information.
 998
 999        Returns a tuple (bits,linkage) which contains information about
1000        the bit architecture and the linkage format used for the
1001        executable. Both values are returned as strings.
1002
1003        Values that cannot be determined are returned as given by the
1004        parameter presets. If bits is given as '', the sizeof(pointer)
1005        (or sizeof(long) on Python version < 1.5.2) is used as
1006        indicator for the supported pointer size.
1007
1008        The function relies on the system's "file" command to do the
1009        actual work. This is available on most if not all Unix
1010        platforms. On some non-Unix platforms where the "file" command
1011        does not exist and the executable is set to the Python interpreter
1012        binary defaults from _default_architecture are used.
1013
1014    """
1015    # Use the sizeof(pointer) as default number of bits if nothing
1016    # else is given as default.
1017    if not bits:
1018        import struct
1019        try:
1020            size = struct.calcsize('P')
1021        except struct.error:
1022            # Older installations can only query longs
1023            size = struct.calcsize('l')
1024        bits = str(size*8) + 'bit'
1025
1026    # Get data from the 'file' system command
1027    if executable:
1028        output = _syscmd_file(executable, '')
1029    else:
1030        output = ''
1031
1032    if not output and \
1033       executable == sys.executable:
1034        # "file" command did not return anything; we'll try to provide
1035        # some sensible defaults then...
1036        if _default_architecture.has_key(sys.platform):
1037            b,l = _default_architecture[sys.platform]
1038            if b:
1039                bits = b
1040            if l:
1041                linkage = l
1042        return bits,linkage
1043
1044    # Split the output into a list of strings omitting the filename
1045    fileout = _architecture_split(output)[1:]
1046
1047    if 'executable' not in fileout:
1048        # Format not supported
1049        return bits,linkage
1050
1051    # Bits
1052    if '32-bit' in fileout:
1053        bits = '32bit'
1054    elif 'N32' in fileout:
1055        # On Irix only
1056        bits = 'n32bit'
1057    elif '64-bit' in fileout:
1058        bits = '64bit'
1059
1060    # Linkage
1061    if 'ELF' in fileout:
1062        linkage = 'ELF'
1063    elif 'PE' in fileout:
1064        # E.g. Windows uses this format
1065        if 'Windows' in fileout:
1066            linkage = 'WindowsPE'
1067        else:
1068            linkage = 'PE'
1069    elif 'COFF' in fileout:
1070        linkage = 'COFF'
1071    elif 'MS-DOS' in fileout:
1072        linkage = 'MSDOS'
1073    else:
1074        # XXX the A.OUT format also falls under this class...
1075        pass
1076
1077    return bits,linkage
1078
1079### Portable uname() interface
1080
1081_uname_cache = None
1082
1083def uname():
1084
1085    """ Fairly portable uname interface. Returns a tuple
1086        of strings (system,node,release,version,machine,processor)
1087        identifying the underlying platform.
1088
1089        Note that unlike the os.uname function this also returns
1090        possible processor information as an additional tuple entry.
1091
1092        Entries which cannot be determined are set to ''.
1093
1094    """
1095    global _uname_cache
1096    no_os_uname = 0
1097
1098    if _uname_cache is not None:
1099        return _uname_cache
1100
1101    processor = ''
1102
1103    # Get some infos from the builtin os.uname API...
1104    try:
1105        system,node,release,version,machine = os.uname()
1106    except AttributeError:
1107        no_os_uname = 1
1108
1109    if no_os_uname or not filter(None, (system, node, release, version, machine)):
1110        # Hmm, no there is either no uname or uname has returned
1111        #'unknowns'... we'll have to poke around the system then.
1112        if no_os_uname:
1113            system = sys.platform
1114            release = ''
1115            version = ''
1116            node = _node()
1117            machine = ''
1118
1119        use_syscmd_ver = 01
1120
1121        # Try win32_ver() on win32 platforms
1122        if system == 'win32':
1123            release,version,csd,ptype = win32_ver()
1124            if release and version:
1125                use_syscmd_ver = 0
1126            # Try to use the PROCESSOR_* environment variables
1127            # available on Win XP and later; see
1128            # http://support.microsoft.com/kb/888731 and
1129            # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
1130            if not machine:
1131                machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
1132            if not processor:
1133                processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
1134
1135        # Try the 'ver' system command available on some
1136        # platforms
1137        if use_syscmd_ver:
1138            system,release,version = _syscmd_ver(system)
1139            # Normalize system to what win32_ver() normally returns
1140            # (_syscmd_ver() tends to return the vendor name as well)
1141            if system == 'Microsoft Windows':
1142                system = 'Windows'
1143            elif system == 'Microsoft' and release == 'Windows':
1144                # Under Windows Vista and Windows Server 2008,
1145                # Microsoft changed the output of the ver command. The
1146                # release is no longer printed.  This causes the
1147                # system and release to be misidentified.
1148                system = 'Windows'
1149                if '6.0' == version[:3]:
1150                    release = 'Vista'
1151                else:
1152                    release = ''
1153
1154        # In case we still don't know anything useful, we'll try to
1155        # help ourselves
1156        if system in ('win32','win16'):
1157            if not version:
1158                if system == 'win32':
1159                    version = '32bit'
1160                else:
1161                    version = '16bit'
1162            system = 'Windows'
1163
1164        elif system[:4] == 'java':
1165            release,vendor,vminfo,osinfo = java_ver()
1166            system = 'Java'
1167            version = string.join(vminfo,', ')
1168            if not version:
1169                version = vendor
1170
1171        elif os.name == 'mac':
1172            release,(version,stage,nonrel),machine = mac_ver()
1173            system = 'MacOS'
1174
1175    # System specific extensions
1176    if system == 'OpenVMS':
1177        # OpenVMS seems to have release and version mixed up
1178        if not release or release == '0':
1179            release = version
1180            version = ''
1181        # Get processor information
1182        try:
1183            import vms_lib
1184        except ImportError:
1185            pass
1186        else:
1187            csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
1188            if (cpu_number >= 128):
1189                processor = 'Alpha'
1190            else:
1191                processor = 'VAX'
1192    if not processor:
1193        # Get processor information from the uname system command
1194        processor = _syscmd_uname('-p','')
1195
1196    #If any unknowns still exist, replace them with ''s, which are more portable
1197    if system == 'unknown':
1198        system = ''
1199    if node == 'unknown':
1200        node = ''
1201    if release == 'unknown':
1202        release = ''
1203    if version == 'unknown':
1204        version = ''
1205    if machine == 'unknown':
1206        machine = ''
1207    if processor == 'unknown':
1208        processor = ''
1209
1210    #  normalize name
1211    if system == 'Microsoft' and release == 'Windows':
1212        system = 'Windows'
1213        release = 'Vista'
1214
1215    _uname_cache = system,node,release,version,machine,processor
1216    return _uname_cache
1217
1218### Direct interfaces to some of the uname() return values
1219
1220def system():
1221
1222    """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
1223
1224        An empty string is returned if the value cannot be determined.
1225
1226    """
1227    return uname()[0]
1228
1229def node():
1230
1231    """ Returns the computer's network name (which may not be fully
1232        qualified)
1233
1234        An empty string is returned if the value cannot be determined.
1235
1236    """
1237    return uname()[1]
1238
1239def release():
1240
1241    """ Returns the system's release, e.g. '2.2.0' or 'NT'
1242
1243        An empty string is returned if the value cannot be determined.
1244
1245    """
1246    return uname()[2]
1247
1248def version():
1249
1250    """ Returns the system's release version, e.g. '#3 on degas'
1251
1252        An empty string is returned if the value cannot be determined.
1253
1254    """
1255    return uname()[3]
1256
1257def machine():
1258
1259    """ Returns the machine type, e.g. 'i386'
1260
1261        An empty string is returned if the value cannot be determined.
1262
1263    """
1264    return uname()[4]
1265
1266def processor():
1267
1268    """ Returns the (true) processor name, e.g. 'amdk6'
1269
1270        An empty string is returned if the value cannot be
1271        determined. Note that many platforms do not provide this
1272        information or simply return the same value as for machine(),
1273        e.g.  NetBSD does this.
1274
1275    """
1276    return uname()[5]
1277
1278### Various APIs for extracting information from sys.version
1279
1280_sys_version_parser = re.compile(
1281    r'([\w.+]+)\s*'
1282    '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1283    '\[([^\]]+)\]?')
1284
1285_jython_sys_version_parser = re.compile(
1286    r'([\d\.]+)')
1287
1288_ironpython_sys_version_parser = re.compile(
1289    r'IronPython\s*'
1290    '([\d\.]+)'
1291    '(?: \(([\d\.]+)\))?'
1292    ' on (.NET [\d\.]+)')
1293
1294_sys_version_cache = {}
1295
1296def _sys_version(sys_version=None):
1297
1298    """ Returns a parsed version of Python's sys.version as tuple
1299        (name, version, branch, revision, buildno, builddate, compiler)
1300        referring to the Python implementation name, version, branch,
1301        revision, build number, build date/time as string and the compiler
1302        identification string.
1303
1304        Note that unlike the Python sys.version, the returned value
1305        for the Python version will always include the patchlevel (it
1306        defaults to '.0').
1307
1308        The function returns empty strings for tuple entries that
1309        cannot be determined.
1310
1311        sys_version may be given to parse an alternative version
1312        string, e.g. if the version was read from a different Python
1313        interpreter.
1314
1315    """
1316    # Get the Python version
1317    if sys_version is None:
1318        sys_version = sys.version
1319
1320    # Try the cache first
1321    result = _sys_version_cache.get(sys_version, None)
1322    if result is not None:
1323        return result
1324
1325    # Parse it
1326    if sys_version[:10] == 'IronPython':
1327        # IronPython
1328        name = 'IronPython'
1329        match = _ironpython_sys_version_parser.match(sys_version)
1330        if match is None:
1331            raise ValueError(
1332                'failed to parse IronPython sys.version: %s' %
1333                repr(sys_version))
1334        version, alt_version, compiler = match.groups()
1335        branch = ''
1336        revision = ''
1337        buildno = ''
1338        builddate = ''
1339
1340    elif sys.platform[:4] == 'java':
1341        # Jython
1342        name = 'Jython'
1343        match = _jython_sys_version_parser.match(sys_version)
1344        if match is None:
1345            raise ValueError(
1346                'failed to parse Jython sys.version: %s' %
1347                repr(sys_version))
1348        version, = match.groups()
1349        branch = ''
1350        revision = ''
1351        compiler = sys.platform
1352        buildno = ''
1353        builddate = ''
1354
1355    else:
1356        # CPython
1357        match = _sys_version_parser.match(sys_version)
1358        if match is None:
1359            raise ValueError(
1360                'failed to parse CPython sys.version: %s' %
1361                repr(sys_version))
1362        version, buildno, builddate, buildtime, compiler = \
1363              match.groups()
1364        if hasattr(sys, 'subversion'):
1365            # sys.subversion was added in Python 2.5
1366            name, branch, revision = sys.subversion
1367        else:
1368            name = 'CPython'
1369            branch = ''
1370            revision = ''
1371        builddate = builddate + ' ' + buildtime
1372
1373    # Add the patchlevel version if missing
1374    l = string.split(version, '.')
1375    if len(l) == 2:
1376        l.append('0')
1377        version = string.join(l, '.')
1378
1379    # Build and cache the result
1380    result = (name, version, branch, revision, buildno, builddate, compiler)
1381    _sys_version_cache[sys_version] = result
1382    return result
1383
1384def _test_sys_version():
1385
1386    _sys_version_cache.clear()
1387    for input, output in (
1388        ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
1389         ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
1390        ('IronPython 1.0.60816 on .NET 2.0.50727.42',
1391         ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
1392        ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
1393         ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
1394        ):
1395        parsed = _sys_version(input)
1396        if parsed != output:
1397            print (input, parsed)
1398
1399def python_implementation():
1400
1401    """ Returns a string identifying the Python implementation.
1402
1403        Currently, the following implementations are identified:
1404        'CPython' (C implementation of Python),
1405        'IronPython' (.NET implementation of Python),
1406        'Jython' (Java implementation of Python).
1407
1408    """
1409    return _sys_version()[0]
1410
1411def python_version():
1412
1413    """ Returns the Python version as string 'major.minor.patchlevel'
1414
1415        Note that unlike the Python sys.version, the returned value
1416        will always include the patchlevel (it defaults to 0).
1417
1418    """
1419    return _sys_version()[1]
1420
1421def python_version_tuple():
1422
1423    """ Returns the Python version as tuple (major, minor, patchlevel)
1424        of strings.
1425
1426        Note that unlike the Python sys.version, the returned value
1427        will always include the patchlevel (it defaults to 0).
1428
1429    """
1430    return tuple(string.split(_sys_version()[1], '.'))
1431
1432def python_branch():
1433
1434    """ Returns a string identifying the Python implementation
1435        branch.
1436
1437        For CPython this is the Subversion branch from which the
1438        Python binary was built.
1439
1440        If not available, an empty string is returned.
1441
1442    """
1443
1444    return _sys_version()[2]
1445
1446def python_revision():
1447
1448    """ Returns a string identifying the Python implementation
1449        revision.
1450
1451        For CPython this is the Subversion revision from which the
1452        Python binary was built.
1453
1454        If not available, an empty string is returned.
1455
1456    """
1457    return _sys_version()[3]
1458
1459def python_build():
1460
1461    """ Returns a tuple (buildno, builddate) stating the Python
1462        build number and date as strings.
1463
1464    """
1465    return _sys_version()[4:6]
1466
1467def python_compiler():
1468
1469    """ Returns a string identifying the compiler used for compiling
1470        Python.
1471
1472    """
1473    return _sys_version()[6]
1474
1475### The Opus Magnum of platform strings :-)
1476
1477_platform_cache = {}
1478
1479def platform(aliased=0, terse=0):
1480
1481    """ Returns a single string identifying the underlying platform
1482        with as much useful information as possible (but no more :).
1483
1484        The output is intended to be human readable rather than
1485        machine parseable. It may look different on different
1486        platforms and this is intended.
1487
1488        If "aliased" is true, the function will use aliases for
1489        various platforms that report system names which differ from
1490        their common names, e.g. SunOS will be reported as
1491        Solaris. The system_alias() function is used to implement
1492        this.
1493
1494        Setting terse to true causes the function to return only the
1495        absolute minimum information needed to identify the platform.
1496
1497    """
1498    result = _platform_cache.get((aliased, terse), None)
1499    if result is not None:
1500        return result
1501
1502    # Get uname information and then apply platform specific cosmetics
1503    # to it...
1504    system,node,release,version,machine,processor = uname()
1505    if machine == processor:
1506        processor = ''
1507    if aliased:
1508        system,release,version = system_alias(system,release,version)
1509
1510    if system == 'Windows':
1511        # MS platforms
1512        rel,vers,csd,ptype = win32_ver(version)
1513        if terse:
1514            platform = _platform(system,release)
1515        else:
1516            platform = _platform(system,release,version,csd)
1517
1518    elif system in ('Linux',):
1519        # Linux based systems
1520        distname,distversion,distid = dist('')
1521        if distname and not terse:
1522            platform = _platform(system,release,machine,processor,
1523                                 'with',
1524                                 distname,distversion,distid)
1525        else:
1526            # If the distribution name is unknown check for libc vs. glibc
1527            l

Large files files are truncated, but you can click here to view the full file