Home | History | Annotate | Download | only in Lib
      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 (at] egenix.com>.

     12 #    If you find problems, please submit bug reports/patches via the

     13 #    Python bug tracker (http://bugs.python.org) 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.7 - added DEV_NULL

     38 #    1.0.6 - added linux_distribution()

     39 #    1.0.5 - fixed Java support to allow running the module on Jython

     40 #    1.0.4 - added IronPython support

     41 #    1.0.3 - added normalization of Windows system name

     42 #    1.0.2 - added more Windows support

     43 #    1.0.1 - reformatted to make doc.py happy

     44 #    1.0.0 - reformatted a bit and checked into Python CVS

     45 #    0.8.0 - added sys.version parser and various new access

     46 #            APIs (python_version(), python_compiler(), etc.)

     47 #    0.7.2 - fixed architecture() to use sizeof(pointer) where available

     48 #    0.7.1 - added support for Caldera OpenLinux

     49 #    0.7.0 - some fixes for WinCE; untabified the source file

     50 #    0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and

     51 #            vms_lib.getsyi() configured

     52 #    0.6.1 - added code to prevent 'uname -p' on platforms which are

     53 #            known not to support it

     54 #    0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;

     55 #            did some cleanup of the interfaces - some APIs have changed

     56 #    0.5.5 - fixed another type in the MacOS code... should have

     57 #            used more coffee today ;-)

     58 #    0.5.4 - fixed a few typos in the MacOS code

     59 #    0.5.3 - added experimental MacOS support; added better popen()

     60 #            workarounds in _syscmd_ver() -- still not 100% elegant

     61 #            though

     62 #    0.5.2 - fixed uname() to return '' instead of 'unknown' in all

     63 #            return values (the system uname command tends to return

     64 #            'unknown' instead of just leaving the field emtpy)

     65 #    0.5.1 - included code for slackware dist; added exception handlers

     66 #            to cover up situations where platforms don't have os.popen

     67 #            (e.g. Mac) or fail on socket.gethostname(); fixed libc

     68 #            detection RE

     69 #    0.5.0 - changed the API names referring to system commands to *syscmd*;

     70 #            added java_ver(); made syscmd_ver() a private

     71 #            API (was system_ver() in previous versions) -- use uname()

     72 #            instead; extended the win32_ver() to also return processor

     73 #            type information

     74 #    0.4.0 - added win32_ver() and modified the platform() output for WinXX

     75 #    0.3.4 - fixed a bug in _follow_symlinks()

     76 #    0.3.3 - fixed popen() and "file" command invokation bugs

     77 #    0.3.2 - added architecture() API and support for it in platform()

     78 #    0.3.1 - fixed syscmd_ver() RE to support Windows NT

     79 #    0.3.0 - added system alias support

     80 #    0.2.3 - removed 'wince' again... oh well.

     81 #    0.2.2 - added 'wince' to syscmd_ver() supported platforms

     82 #    0.2.1 - added cache logic and changed the platform string format

     83 #    0.2.0 - changed the API to use functions instead of module globals

     84 #            since some action take too long to be run on module import

     85 #    0.1.0 - first release

     86 #

     87 #    You can always get the latest version of this module at:

     88 #

     89 #             http://www.egenix.com/files/python/platform.py

     90 #

     91 #    If that URL should fail, try contacting the author.

     92 
     93 __copyright__ = """
     94     Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal (at] lemburg.com
     95     Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info (at] egenix.com
     96 
     97     Permission to use, copy, modify, and distribute this software and its
     98     documentation for any purpose and without fee or royalty is hereby granted,
     99     provided that the above copyright notice appear in all copies and that
    100     both that copyright notice and this permission notice appear in
    101     supporting documentation or portions thereof, including modifications,
    102     that you make.
    103 
    104     EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
    105     THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
    106     FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
    107     INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
    108     FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
    109     NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
    110     WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
    111 
    112 """
    113 
    114 __version__ = '1.0.7'
    115 
    116 import sys,string,os,re
    117 
    118 ### Globals & Constants

    119 
    120 # Determine the platform's /dev/null device

    121 try:
    122     DEV_NULL = os.devnull
    123 except AttributeError:
    124     # os.devnull was added in Python 2.4, so emulate it for earlier

    125     # Python versions

    126     if sys.platform in ('dos','win32','win16','os2'):
    127         # Use the old CP/M NUL as device name

    128         DEV_NULL = 'NUL'
    129     else:
    130         # Standard Unix uses /dev/null

    131         DEV_NULL = '/dev/null'
    132 
    133 ### Platform specific APIs

    134 
    135 _libc_search = re.compile(r'(__libc_init)'
    136                           '|'
    137                           '(GLIBC_([0-9.]+))'
    138                           '|'
    139                           '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)')
    140 
    141 def libc_ver(executable=sys.executable,lib='',version='',
    142 
    143              chunksize=2048):
    144 
    145     """ Tries to determine the libc version that the file executable
    146         (which defaults to the Python interpreter) is linked against.
    147 
    148         Returns a tuple of strings (lib,version) which default to the
    149         given parameters in case the lookup fails.
    150 
    151         Note that the function has intimate knowledge of how different
    152         libc versions add symbols to the executable and thus is probably
    153         only useable for executables compiled using gcc.
    154 
    155         The file is read and scanned in chunks of chunksize bytes.
    156 
    157     """
    158     if hasattr(os.path, 'realpath'):
    159         # Python 2.2 introduced os.path.realpath(); it is used

    160         # here to work around problems with Cygwin not being

    161         # able to open symlinks for reading

    162         executable = os.path.realpath(executable)
    163     f = open(executable,'rb')
    164     binary = f.read(chunksize)
    165     pos = 0
    166     while 1:
    167         m = _libc_search.search(binary,pos)
    168         if not m:
    169             binary = f.read(chunksize)
    170             if not binary:
    171                 break
    172             pos = 0
    173             continue
    174         libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
    175         if libcinit and not lib:
    176             lib = 'libc'
    177         elif glibc:
    178             if lib != 'glibc':
    179                 lib = 'glibc'
    180                 version = glibcversion
    181             elif glibcversion > version:
    182                 version = glibcversion
    183         elif so:
    184             if lib != 'glibc':
    185                 lib = 'libc'
    186                 if soversion > version:
    187                     version = soversion
    188                 if threads and version[-len(threads):] != threads:
    189                     version = version + threads
    190         pos = m.end()
    191     f.close()
    192     return lib,version
    193 
    194 def _dist_try_harder(distname,version,id):
    195 
    196     """ Tries some special tricks to get the distribution
    197         information in case the default method fails.
    198 
    199         Currently supports older SuSE Linux, Caldera OpenLinux and
    200         Slackware Linux distributions.
    201 
    202     """
    203     if os.path.exists('/var/adm/inst-log/info'):
    204         # SuSE Linux stores distribution information in that file

    205         info = open('/var/adm/inst-log/info').readlines()
    206         distname = 'SuSE'
    207         for line in info:
    208             tv = string.split(line)
    209             if len(tv) == 2:
    210                 tag,value = tv
    211             else:
    212                 continue
    213             if tag == 'MIN_DIST_VERSION':
    214                 version = string.strip(value)
    215             elif tag == 'DIST_IDENT':
    216                 values = string.split(value,'-')
    217                 id = values[2]
    218         return distname,version,id
    219 
    220     if os.path.exists('/etc/.installed'):
    221         # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)

    222         info = open('/etc/.installed').readlines()
    223         for line in info:
    224             pkg = string.split(line,'-')
    225             if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
    226                 # XXX does Caldera support non Intel platforms ? If yes,

    227                 #     where can we find the needed id ?

    228                 return 'OpenLinux',pkg[1],id
    229 
    230     if os.path.isdir('/usr/lib/setup'):
    231         # Check for slackware verson tag file (thanks to Greg Andruk)

    232         verfiles = os.listdir('/usr/lib/setup')
    233         for n in range(len(verfiles)-1, -1, -1):
    234             if verfiles[n][:14] != 'slack-version-':
    235                 del verfiles[n]
    236         if verfiles:
    237             verfiles.sort()
    238             distname = 'slackware'
    239             version = verfiles[-1][14:]
    240             return distname,version,id
    241 
    242     return distname,version,id
    243 
    244 _release_filename = re.compile(r'(\w+)[-_](release|version)')
    245 _lsb_release_version = re.compile(r'(.+)'
    246                                    ' release '
    247                                    '([\d.]+)'
    248                                    '[^(]*(?:\((.+)\))?')
    249 _release_version = re.compile(r'([^0-9]+)'
    250                                '(?: release )?'
    251                                '([\d.]+)'
    252                                '[^(]*(?:\((.+)\))?')
    253 
    254 # See also http://www.novell.com/coolsolutions/feature/11251.html

    255 # and http://linuxmafia.com/faq/Admin/release-files.html

    256 # and http://data.linux-ntfs.org/rpm/whichrpm

    257 # and http://www.die.net/doc/linux/man/man1/lsb_release.1.html

    258 
    259 _supported_dists = (
    260     'SuSE', 'debian', 'fedora', 'redhat', 'centos',
    261     'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',
    262     'UnitedLinux', 'turbolinux')
    263 
    264 def _parse_release_file(firstline):
    265 
    266     # Default to empty 'version' and 'id' strings.  Both defaults are used

    267     # when 'firstline' is empty.  'id' defaults to empty when an id can not

    268     # be deduced.

    269     version = ''
    270     id = ''
    271 
    272     # Parse the first line

    273     m = _lsb_release_version.match(firstline)
    274     if m is not None:
    275         # LSB format: "distro release x.x (codename)"

    276         return tuple(m.groups())
    277 
    278     # Pre-LSB format: "distro x.x (codename)"

    279     m = _release_version.match(firstline)
    280     if m is not None:
    281         return tuple(m.groups())
    282 
    283     # Unkown format... take the first two words

    284     l = string.split(string.strip(firstline))
    285     if l:
    286         version = l[0]
    287         if len(l) > 1:
    288             id = l[1]
    289     return '', version, id
    290 
    291 def linux_distribution(distname='', version='', id='',
    292 
    293                        supported_dists=_supported_dists,
    294                        full_distribution_name=1):
    295 
    296     """ Tries to determine the name of the Linux OS distribution name.
    297 
    298         The function first looks for a distribution release file in
    299         /etc and then reverts to _dist_try_harder() in case no
    300         suitable files are found.
    301 
    302         supported_dists may be given to define the set of Linux
    303         distributions to look for. It defaults to a list of currently
    304         supported Linux distributions identified by their release file
    305         name.
    306 
    307         If full_distribution_name is true (default), the full
    308         distribution read from the OS is returned. Otherwise the short
    309         name taken from supported_dists is used.
    310 
    311         Returns a tuple (distname,version,id) which default to the
    312         args given as parameters.
    313 
    314     """
    315     try:
    316         etc = os.listdir('/etc')
    317     except os.error:
    318         # Probably not a Unix system

    319         return distname,version,id
    320     etc.sort()
    321     for file in etc:
    322         m = _release_filename.match(file)
    323         if m is not None:
    324             _distname,dummy = m.groups()
    325             if _distname in supported_dists:
    326                 distname = _distname
    327                 break
    328     else:
    329         return _dist_try_harder(distname,version,id)
    330 
    331     # Read the first line

    332     f = open('/etc/'+file, 'r')
    333     firstline = f.readline()
    334     f.close()
    335     _distname, _version, _id = _parse_release_file(firstline)
    336 
    337     if _distname and full_distribution_name:
    338         distname = _distname
    339     if _version:
    340         version = _version
    341     if _id:
    342         id = _id
    343     return distname, version, id
    344 
    345 # To maintain backwards compatibility:

    346 
    347 def dist(distname='',version='',id='',
    348 
    349          supported_dists=_supported_dists):
    350 
    351     """ Tries to determine the name of the Linux OS distribution name.
    352 
    353         The function first looks for a distribution release file in
    354         /etc and then reverts to _dist_try_harder() in case no
    355         suitable files are found.
    356 
    357         Returns a tuple (distname,version,id) which default to the
    358         args given as parameters.
    359 
    360     """
    361     return linux_distribution(distname, version, id,
    362                               supported_dists=supported_dists,
    363                               full_distribution_name=0)
    364 
    365 class _popen:
    366 
    367     """ Fairly portable (alternative) popen implementation.
    368 
    369         This is mostly needed in case os.popen() is not available, or
    370         doesn't work as advertised, e.g. in Win9X GUI programs like
    371         PythonWin or IDLE.
    372 
    373         Writing to the pipe is currently not supported.
    374 
    375     """
    376     tmpfile = ''
    377     pipe = None
    378     bufsize = None
    379     mode = 'r'
    380 
    381     def __init__(self,cmd,mode='r',bufsize=None):
    382 
    383         if mode != 'r':
    384             raise ValueError,'popen()-emulation only supports read mode'
    385         import tempfile
    386         self.tmpfile = tmpfile = tempfile.mktemp()
    387         os.system(cmd + ' > %s' % tmpfile)
    388         self.pipe = open(tmpfile,'rb')
    389         self.bufsize = bufsize
    390         self.mode = mode
    391 
    392     def read(self):
    393 
    394         return self.pipe.read()
    395 
    396     def readlines(self):
    397 
    398         if self.bufsize is not None:
    399             return self.pipe.readlines()
    400 
    401     def close(self,
    402 
    403               remove=os.unlink,error=os.error):
    404 
    405         if self.pipe:
    406             rc = self.pipe.close()
    407         else:
    408             rc = 255
    409         if self.tmpfile:
    410             try:
    411                 remove(self.tmpfile)
    412             except error:
    413                 pass
    414         return rc
    415 
    416     # Alias

    417     __del__ = close
    418 
    419 def popen(cmd, mode='r', bufsize=None):
    420 
    421     """ Portable popen() interface.
    422     """
    423     # Find a working popen implementation preferring win32pipe.popen

    424     # over os.popen over _popen

    425     popen = None
    426     if os.environ.get('OS','') == 'Windows_NT':
    427         # On NT win32pipe should work; on Win9x it hangs due to bugs

    428         # in the MS C lib (see MS KnowledgeBase article Q150956)

    429         try:
    430             import win32pipe
    431         except ImportError:
    432             pass
    433         else:
    434             popen = win32pipe.popen
    435     if popen is None:
    436         if hasattr(os,'popen'):
    437             popen = os.popen
    438             # Check whether it works... it doesn't in GUI programs

    439             # on Windows platforms

    440             if sys.platform == 'win32': # XXX Others too ?

    441                 try:
    442                     popen('')
    443                 except os.error:
    444                     popen = _popen
    445         else:
    446             popen = _popen
    447     if bufsize is None:
    448         return popen(cmd,mode)
    449     else:
    450         return popen(cmd,mode,bufsize)
    451 
    452 def _norm_version(version, build=''):
    453 
    454     """ Normalize the version and build strings and return a single
    455         version string using the format major.minor.build (or patchlevel).
    456     """
    457     l = string.split(version,'.')
    458     if build:
    459         l.append(build)
    460     try:
    461         ints = map(int,l)
    462     except ValueError:
    463         strings = l
    464     else:
    465         strings = map(str,ints)
    466     version = string.join(strings[:3],'.')
    467     return version
    468 
    469 _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
    470                          '.*'
    471                          '\[.* ([\d.]+)\])')
    472 
    473 # Examples of VER command output:

    474 #

    475 #   Windows 2000:  Microsoft Windows 2000 [Version 5.00.2195]

    476 #   Windows XP:    Microsoft Windows XP [Version 5.1.2600]

    477 #   Windows Vista: Microsoft Windows [Version 6.0.6002]

    478 #

    479 # Note that the "Version" string gets localized on different

    480 # Windows versions.

    481 
    482 def _syscmd_ver(system='', release='', version='',
    483 
    484                supported_platforms=('win32','win16','dos','os2')):
    485 
    486     """ Tries to figure out the OS version used and returns
    487         a tuple (system,release,version).
    488 
    489         It uses the "ver" shell command for this which is known
    490         to exists on Windows, DOS and OS/2. XXX Others too ?
    491 
    492         In case this fails, the given parameters are used as
    493         defaults.
    494 
    495     """
    496     if sys.platform not in supported_platforms:
    497         return system,release,version
    498 
    499     # Try some common cmd strings

    500     for cmd in ('ver','command /c ver','cmd /c ver'):
    501         try:
    502             pipe = popen(cmd)
    503             info = pipe.read()
    504             if pipe.close():
    505                 raise os.error,'command failed'
    506             # XXX How can I suppress shell errors from being written

    507             #     to stderr ?

    508         except os.error,why:
    509             #print 'Command %s failed: %s' % (cmd,why)

    510             continue
    511         except IOError,why:
    512             #print 'Command %s failed: %s' % (cmd,why)

    513             continue
    514         else:
    515             break
    516     else:
    517         return system,release,version
    518 
    519     # Parse the output

    520     info = string.strip(info)
    521     m = _ver_output.match(info)
    522     if m is not None:
    523         system,release,version = m.groups()
    524         # Strip trailing dots from version and release

    525         if release[-1] == '.':
    526             release = release[:-1]
    527         if version[-1] == '.':
    528             version = version[:-1]
    529         # Normalize the version and build strings (eliminating additional

    530         # zeros)

    531         version = _norm_version(version)
    532     return system,release,version
    533 
    534 def _win32_getvalue(key,name,default=''):
    535 
    536     """ Read a value for name from the registry key.
    537 
    538         In case this fails, default is returned.
    539 
    540     """
    541     try:
    542         # Use win32api if available

    543         from win32api import RegQueryValueEx
    544     except ImportError:
    545         # On Python 2.0 and later, emulate using _winreg

    546         import _winreg
    547         RegQueryValueEx = _winreg.QueryValueEx
    548     try:
    549         return RegQueryValueEx(key,name)
    550     except:
    551         return default
    552 
    553 def win32_ver(release='',version='',csd='',ptype=''):
    554 
    555     """ Get additional version information from the Windows Registry
    556         and return a tuple (version,csd,ptype) referring to version
    557         number, CSD level and OS type (multi/single
    558         processor).
    559 
    560         As a hint: ptype returns 'Uniprocessor Free' on single
    561         processor NT machines and 'Multiprocessor Free' on multi
    562         processor machines. The 'Free' refers to the OS version being
    563         free of debugging code. It could also state 'Checked' which
    564         means the OS version uses debugging code, i.e. code that
    565         checks arguments, ranges, etc. (Thomas Heller).
    566 
    567         Note: this function works best with Mark Hammond's win32
    568         package installed, but also on Python 2.3 and later. It
    569         obviously only runs on Win32 compatible platforms.
    570 
    571     """
    572     # XXX Is there any way to find out the processor type on WinXX ?

    573     # XXX Is win32 available on Windows CE ?

    574     #

    575     # Adapted from code posted by Karl Putland to comp.lang.python.

    576     #

    577     # The mappings between reg. values and release names can be found

    578     # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp

    579 
    580     # Import the needed APIs

    581     try:
    582         import win32api
    583         from win32api import RegQueryValueEx, RegOpenKeyEx, \
    584              RegCloseKey, GetVersionEx
    585         from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
    586              VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
    587     except ImportError:
    588         # Emulate the win32api module using Python APIs

    589         try:
    590             sys.getwindowsversion
    591         except AttributeError:
    592             # No emulation possible, so return the defaults...

    593             return release,version,csd,ptype
    594         else:
    595             # Emulation using _winreg (added in Python 2.0) and

    596             # sys.getwindowsversion() (added in Python 2.3)

    597             import _winreg
    598             GetVersionEx = sys.getwindowsversion
    599             RegQueryValueEx = _winreg.QueryValueEx
    600             RegOpenKeyEx = _winreg.OpenKeyEx
    601             RegCloseKey = _winreg.CloseKey
    602             HKEY_LOCAL_MACHINE = _winreg.HKEY_LOCAL_MACHINE
    603             VER_PLATFORM_WIN32_WINDOWS = 1
    604             VER_PLATFORM_WIN32_NT = 2
    605             VER_NT_WORKSTATION = 1
    606             VER_NT_SERVER = 3
    607             REG_SZ = 1
    608 
    609     # Find out the registry key and some general version infos

    610     winver = GetVersionEx()
    611     maj,min,buildno,plat,csd = winver
    612     version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
    613     if hasattr(winver, "service_pack"):
    614         if winver.service_pack != "":
    615             csd = 'SP%s' % winver.service_pack_major
    616     else:
    617         if csd[:13] == 'Service Pack ':
    618             csd = 'SP' + csd[13:]
    619 
    620     if plat == VER_PLATFORM_WIN32_WINDOWS:
    621         regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
    622         # Try to guess the release name

    623         if maj == 4:
    624             if min == 0:
    625                 release = '95'
    626             elif min == 10:
    627                 release = '98'
    628             elif min == 90:
    629                 release = 'Me'
    630             else:
    631                 release = 'postMe'
    632         elif maj == 5:
    633             release = '2000'
    634 
    635     elif plat == VER_PLATFORM_WIN32_NT:
    636         regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
    637         if maj <= 4:
    638             release = 'NT'
    639         elif maj == 5:
    640             if min == 0:
    641                 release = '2000'
    642             elif min == 1:
    643                 release = 'XP'
    644             elif min == 2:
    645                 release = '2003Server'
    646             else:
    647                 release = 'post2003'
    648         elif maj == 6:
    649             if hasattr(winver, "product_type"):
    650                 product_type = winver.product_type
    651             else:
    652                 product_type = VER_NT_WORKSTATION
    653                 # Without an OSVERSIONINFOEX capable sys.getwindowsversion(),

    654                 # or help from the registry, we cannot properly identify

    655                 # non-workstation versions.

    656                 try:
    657                     key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
    658                     name, type = RegQueryValueEx(key, "ProductName")
    659                     # Discard any type that isn't REG_SZ

    660                     if type == REG_SZ and name.find("Server") != -1:
    661                         product_type = VER_NT_SERVER
    662                 except WindowsError:
    663                     # Use default of VER_NT_WORKSTATION

    664                     pass
    665 
    666             if min == 0:
    667                 if product_type == VER_NT_WORKSTATION:
    668                     release = 'Vista'
    669                 else:
    670                     release = '2008Server'
    671             elif min == 1:
    672                 if product_type == VER_NT_WORKSTATION:
    673                     release = '7'
    674                 else:
    675                     release = '2008ServerR2'
    676             else:
    677                 release = 'post2008Server'
    678 
    679     else:
    680         if not release:
    681             # E.g. Win3.1 with win32s

    682             release = '%i.%i' % (maj,min)
    683         return release,version,csd,ptype
    684 
    685     # Open the registry key

    686     try:
    687         keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
    688         # Get a value to make sure the key exists...

    689         RegQueryValueEx(keyCurVer, 'SystemRoot')
    690     except:
    691         return release,version,csd,ptype
    692 
    693     # Parse values

    694     #subversion = _win32_getvalue(keyCurVer,

    695     #                            'SubVersionNumber',

    696     #                            ('',1))[0]

    697     #if subversion:

    698     #   release = release + subversion # 95a, 95b, etc.

    699     build = _win32_getvalue(keyCurVer,
    700                             'CurrentBuildNumber',
    701                             ('',1))[0]
    702     ptype = _win32_getvalue(keyCurVer,
    703                            'CurrentType',
    704                            (ptype,1))[0]
    705 
    706     # Normalize version

    707     version = _norm_version(version,build)
    708 
    709     # Close key

    710     RegCloseKey(keyCurVer)
    711     return release,version,csd,ptype
    712 
    713 def _mac_ver_lookup(selectors,default=None):
    714 
    715     from gestalt import gestalt
    716     import MacOS
    717     l = []
    718     append = l.append
    719     for selector in selectors:
    720         try:
    721             append(gestalt(selector))
    722         except (RuntimeError, MacOS.Error):
    723             append(default)
    724     return l
    725 
    726 def _bcd2str(bcd):
    727 
    728     return hex(bcd)[2:]
    729 
    730 def _mac_ver_gestalt():
    731     """
    732         Thanks to Mark R. Levinson for mailing documentation links and
    733         code examples for this function. Documentation for the
    734         gestalt() API is available online at:
    735 
    736            http://www.rgaros.nl/gestalt/
    737     """
    738     # Check whether the version info module is available

    739     try:
    740         import gestalt
    741         import MacOS
    742     except ImportError:
    743         return None
    744     # Get the infos

    745     sysv,sysa = _mac_ver_lookup(('sysv','sysa'))
    746     # Decode the infos

    747     if sysv:
    748         major = (sysv & 0xFF00) >> 8
    749         minor = (sysv & 0x00F0) >> 4
    750         patch = (sysv & 0x000F)
    751 
    752         if (major, minor) >= (10, 4):
    753             # the 'sysv' gestald cannot return patchlevels

    754             # higher than 9. Apple introduced 3 new

    755             # gestalt codes in 10.4 to deal with this

    756             # issue (needed because patch levels can

    757             # run higher than 9, such as 10.4.11)

    758             major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
    759             release = '%i.%i.%i' %(major, minor, patch)
    760         else:
    761             release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
    762 
    763     if sysa:
    764         machine = {0x1: '68k',
    765                    0x2: 'PowerPC',
    766                    0xa: 'i386'}.get(sysa,'')
    767 
    768     return release,versioninfo,machine
    769 
    770 def _mac_ver_xml():
    771     fn = '/System/Library/CoreServices/SystemVersion.plist'
    772     if not os.path.exists(fn):
    773         return None
    774 
    775     try:
    776         import plistlib
    777     except ImportError:
    778         return None
    779 
    780     pl = plistlib.readPlist(fn)
    781     release = pl['ProductVersion']
    782     versioninfo=('', '', '')
    783     machine = os.uname()[4]
    784     if machine in ('ppc', 'Power Macintosh'):
    785         # for compatibility with the gestalt based code

    786         machine = 'PowerPC'
    787 
    788     return release,versioninfo,machine
    789 
    790 
    791 def mac_ver(release='',versioninfo=('','',''),machine=''):
    792 
    793     """ Get MacOS version information and return it as tuple (release,
    794         versioninfo, machine) with versioninfo being a tuple (version,
    795         dev_stage, non_release_version).
    796 
    797         Entries which cannot be determined are set to the paramter values
    798         which default to ''. All tuple entries are strings.
    799     """
    800 
    801     # First try reading the information from an XML file which should

    802     # always be present

    803     info = _mac_ver_xml()
    804     if info is not None:
    805         return info
    806 
    807     # If that doesn't work for some reason fall back to reading the

    808     # information using gestalt calls.

    809     info = _mac_ver_gestalt()
    810     if info is not None:
    811         return info
    812 
    813     # If that also doesn't work return the default values

    814     return release,versioninfo,machine
    815 
    816 def _java_getprop(name,default):
    817 
    818     from java.lang import System
    819     try:
    820         value = System.getProperty(name)
    821         if value is None:
    822             return default
    823         return value
    824     except AttributeError:
    825         return default
    826 
    827 def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
    828 
    829     """ Version interface for Jython.
    830 
    831         Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
    832         a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
    833         tuple (os_name,os_version,os_arch).
    834 
    835         Values which cannot be determined are set to the defaults
    836         given as parameters (which all default to '').
    837 
    838     """
    839     # Import the needed APIs

    840     try:
    841         import java.lang
    842     except ImportError:
    843         return release,vendor,vminfo,osinfo
    844 
    845     vendor = _java_getprop('java.vendor', vendor)
    846     release = _java_getprop('java.version', release)
    847     vm_name, vm_release, vm_vendor = vminfo
    848     vm_name = _java_getprop('java.vm.name', vm_name)
    849     vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
    850     vm_release = _java_getprop('java.vm.version', vm_release)
    851     vminfo = vm_name, vm_release, vm_vendor
    852     os_name, os_version, os_arch = osinfo
    853     os_arch = _java_getprop('java.os.arch', os_arch)
    854     os_name = _java_getprop('java.os.name', os_name)
    855     os_version = _java_getprop('java.os.version', os_version)
    856     osinfo = os_name, os_version, os_arch
    857 
    858     return release, vendor, vminfo, osinfo
    859 
    860 ### System name aliasing

    861 
    862 def system_alias(system,release,version):
    863 
    864     """ Returns (system,release,version) aliased to common
    865         marketing names used for some systems.
    866 
    867         It also does some reordering of the information in some cases
    868         where it would otherwise cause confusion.
    869 
    870     """
    871     if system == 'Rhapsody':
    872         # Apple's BSD derivative

    873         # XXX How can we determine the marketing release number ?

    874         return 'MacOS X Server',system+release,version
    875 
    876     elif system == 'SunOS':
    877         # Sun's OS

    878         if release < '5':
    879             # These releases use the old name SunOS

    880             return system,release,version
    881         # Modify release (marketing release = SunOS release - 3)

    882         l = string.split(release,'.')
    883         if l:
    884             try:
    885                 major = int(l[0])
    886             except ValueError:
    887                 pass
    888             else:
    889                 major = major - 3
    890                 l[0] = str(major)
    891                 release = string.join(l,'.')
    892         if release < '6':
    893             system = 'Solaris'
    894         else:
    895             # XXX Whatever the new SunOS marketing name is...

    896             system = 'Solaris'
    897 
    898     elif system == 'IRIX64':
    899         # IRIX reports IRIX64 on platforms with 64-bit support; yet it

    900         # is really a version and not a different platform, since 32-bit

    901         # apps are also supported..

    902         system = 'IRIX'
    903         if version:
    904             version = version + ' (64bit)'
    905         else:
    906             version = '64bit'
    907 
    908     elif system in ('win32','win16'):
    909         # In case one of the other tricks

    910         system = 'Windows'
    911 
    912     return system,release,version
    913 
    914 ### Various internal helpers

    915 
    916 def _platform(*args):
    917 
    918     """ Helper to format the platform string in a filename
    919         compatible format e.g. "system-version-machine".
    920     """
    921     # Format the platform string

    922     platform = string.join(
    923         map(string.strip,
    924             filter(len, args)),
    925         '-')
    926 
    927     # Cleanup some possible filename obstacles...

    928     replace = string.replace
    929     platform = replace(platform,' ','_')
    930     platform = replace(platform,'/','-')
    931     platform = replace(platform,'\\','-')
    932     platform = replace(platform,':','-')
    933     platform = replace(platform,';','-')
    934     platform = replace(platform,'"','-')
    935     platform = replace(platform,'(','-')
    936     platform = replace(platform,')','-')
    937 
    938     # No need to report 'unknown' information...
    939     platform = replace(platform,'unknown','')
    940 
    941     # Fold '--'s and remove trailing '-'
    942     while 1:
    943         cleaned = replace(platform,'--','-')
    944         if cleaned == platform:
    945             break
    946         platform = cleaned
    947     while platform[-1] == '-':
    948         platform = platform[:-1]
    949 
    950     return platform
    951 
    952 def _node(default=''):
    953 
    954     """ Helper to determine the node name of this machine.
    955     """
    956     try:
    957         import socket
    958     except ImportError:
    959         # No sockets...
    960         return default
    961     try:
    962         return socket.gethostname()
    963     except socket.error:
    964         # Still not working...
    965         return default
    966 
    967 # os.path.abspath is new in Python 1.5.2:
    968 if not hasattr(os.path,'abspath'):
    969 
    970     def _abspath(path,
    971 
    972                  isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
    973                  normpath=os.path.normpath):
    974 
    975         if not isabs(path):
    976             path = join(getcwd(), path)
    977         return normpath(path)
    978 
    979 else:
    980 
    981     _abspath = os.path.abspath
    982 
    983 def _follow_symlinks(filepath):
    984 
    985     """ In case filepath is a symlink, follow it until a
    986         real file is reached.
    987     """
    988     filepath = _abspath(filepath)
    989     while os.path.islink(filepath):
    990         filepath = os.path.normpath(
    991             os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
    992     return filepath
    993 
    994 def _syscmd_uname(option,default=''):
    995 
    996     """ Interface to the system's uname command.
    997     """
    998     if sys.platform in ('dos','win32','win16','os2'):
    999         # XXX Others too ?
   1000         return default
   1001     try:
   1002         f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
   1003     except (AttributeError,os.error):
   1004         return default
   1005     output = string.strip(f.read())
   1006     rc = f.close()
   1007     if not output or rc:
   1008         return default
   1009     else:
   1010         return output
   1011 
   1012 def _syscmd_file(target,default=''):
   1013 
   1014     """ Interface to the system's file command.
   1015 
   1016         The function uses the -b option of the file command to have it
   1017         ommit the filename in its output and if possible the -L option
   1018         to have the command follow symlinks. It returns default in
   1019         case the command should fail.
   1020 
   1021     """
   1022     if sys.platform in ('dos','win32','win16','os2'):
   1023         # XXX Others too ?
   1024         return default
   1025     target = _follow_symlinks(target).replace('"', '\\"')
   1026     try:
   1027         f = os.popen('file "%s" 2> %s' % (target, DEV_NULL))
   1028     except (AttributeError,os.error):
   1029         return default
   1030     output = string.strip(f.read())
   1031     rc = f.close()
   1032     if not output or rc:
   1033         return default
   1034     else:
   1035         return output
   1036 
   1037 ### Information about the used architecture
   1038 
   1039 # Default values for architecture; non-empty strings override the
   1040 # defaults given as parameters
   1041 _default_architecture = {
   1042     'win32': ('','WindowsPE'),
   1043     'win16': ('','Windows'),
   1044     'dos': ('','MSDOS'),
   1045 }
   1046 
   1047 _architecture_split = re.compile(r'[\s,]').split
   1048 
   1049 def architecture(executable=sys.executable,bits='',linkage=''):
   1050 
   1051     """ Queries the given executable (defaults to the Python interpreter
   1052         binary) for various architecture information.
   1053 
   1054         Returns a tuple (bits,linkage) which contains information about
   1055         the bit architecture and the linkage format used for the
   1056         executable. Both values are returned as strings.
   1057 
   1058         Values that cannot be determined are returned as given by the
   1059         parameter presets. If bits is given as '', the sizeof(pointer)
   1060         (or sizeof(long) on Python version < 1.5.2) is used as
   1061         indicator for the supported pointer size.
   1062 
   1063         The function relies on the system's "file" command to do the
   1064         actual work. This is available on most if not all Unix
   1065         platforms. On some non-Unix platforms where the "file" command
   1066         does not exist and the executable is set to the Python interpreter
   1067         binary defaults from _default_architecture are used.
   1068 
   1069     """
   1070     # Use the sizeof(pointer) as default number of bits if nothing
   1071     # else is given as default.
   1072     if not bits:
   1073         import struct
   1074         try:
   1075             size = struct.calcsize('P')
   1076         except struct.error:
   1077             # Older installations can only query longs
   1078             size = struct.calcsize('l')
   1079         bits = str(size*8) + 'bit'
   1080 
   1081     # Get data from the 'file' system command
   1082     if executable:
   1083         output = _syscmd_file(executable, '')
   1084     else:
   1085         output = ''
   1086 
   1087     if not output and \
   1088        executable == sys.executable:
   1089         # "file" command did not return anything; we'll try to provide
   1090         # some sensible defaults then...

   1091         if sys.platform in _default_architecture:
   1092             b, l = _default_architecture[sys.platform]
   1093             if b:
   1094                 bits = b
   1095             if l:
   1096                 linkage = l
   1097         return bits, linkage
   1098 
   1099     # Split the output into a list of strings omitting the filename

   1100     fileout = _architecture_split(output)[1:]
   1101 
   1102     if 'executable' not in fileout:
   1103         # Format not supported

   1104         return bits,linkage
   1105 
   1106     # Bits

   1107     if '32-bit' in fileout:
   1108         bits = '32bit'
   1109     elif 'N32' in fileout:
   1110         # On Irix only

   1111         bits = 'n32bit'
   1112     elif '64-bit' in fileout:
   1113         bits = '64bit'
   1114 
   1115     # Linkage

   1116     if 'ELF' in fileout:
   1117         linkage = 'ELF'
   1118     elif 'PE' in fileout:
   1119         # E.g. Windows uses this format

   1120         if 'Windows' in fileout:
   1121             linkage = 'WindowsPE'
   1122         else:
   1123             linkage = 'PE'
   1124     elif 'COFF' in fileout:
   1125         linkage = 'COFF'
   1126     elif 'MS-DOS' in fileout:
   1127         linkage = 'MSDOS'
   1128     else:
   1129         # XXX the A.OUT format also falls under this class...

   1130         pass
   1131 
   1132     return bits,linkage
   1133 
   1134 ### Portable uname() interface

   1135 
   1136 _uname_cache = None
   1137 
   1138 def uname():
   1139 
   1140     """ Fairly portable uname interface. Returns a tuple
   1141         of strings (system,node,release,version,machine,processor)
   1142         identifying the underlying platform.
   1143 
   1144         Note that unlike the os.uname function this also returns
   1145         possible processor information as an additional tuple entry.
   1146 
   1147         Entries which cannot be determined are set to ''.
   1148 
   1149     """
   1150     global _uname_cache
   1151     no_os_uname = 0
   1152 
   1153     if _uname_cache is not None:
   1154         return _uname_cache
   1155 
   1156     processor = ''
   1157 
   1158     # Get some infos from the builtin os.uname API...

   1159     try:
   1160         system,node,release,version,machine = os.uname()
   1161     except AttributeError:
   1162         no_os_uname = 1
   1163 
   1164     if no_os_uname or not filter(None, (system, node, release, version, machine)):
   1165         # Hmm, no there is either no uname or uname has returned

   1166         #'unknowns'... we'll have to poke around the system then.

   1167         if no_os_uname:
   1168             system = sys.platform
   1169             release = ''
   1170             version = ''
   1171             node = _node()
   1172             machine = ''
   1173 
   1174         use_syscmd_ver = 1
   1175 
   1176         # Try win32_ver() on win32 platforms

   1177         if system == 'win32':
   1178             release,version,csd,ptype = win32_ver()
   1179             if release and version:
   1180                 use_syscmd_ver = 0
   1181             # Try to use the PROCESSOR_* environment variables

   1182             # available on Win XP and later; see

   1183             # http://support.microsoft.com/kb/888731 and

   1184             # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM

   1185             if not machine:
   1186                 # WOW64 processes mask the native architecture

   1187                 if "PROCESSOR_ARCHITEW6432" in os.environ:
   1188                     machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
   1189                 else:
   1190                     machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
   1191             if not processor:
   1192                 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
   1193 
   1194         # Try the 'ver' system command available on some

   1195         # platforms

   1196         if use_syscmd_ver:
   1197             system,release,version = _syscmd_ver(system)
   1198             # Normalize system to what win32_ver() normally returns

   1199             # (_syscmd_ver() tends to return the vendor name as well)

   1200             if system == 'Microsoft Windows':
   1201                 system = 'Windows'
   1202             elif system == 'Microsoft' and release == 'Windows':
   1203                 # Under Windows Vista and Windows Server 2008,

   1204                 # Microsoft changed the output of the ver command. The

   1205                 # release is no longer printed.  This causes the

   1206                 # system and release to be misidentified.

   1207                 system = 'Windows'
   1208                 if '6.0' == version[:3]:
   1209                     release = 'Vista'
   1210                 else:
   1211                     release = ''
   1212 
   1213         # In case we still don't know anything useful, we'll try to

   1214         # help ourselves

   1215         if system in ('win32','win16'):
   1216             if not version:
   1217                 if system == 'win32':
   1218                     version = '32bit'
   1219                 else:
   1220                     version = '16bit'
   1221             system = 'Windows'
   1222 
   1223         elif system[:4] == 'java':
   1224             release,vendor,vminfo,osinfo = java_ver()
   1225             system = 'Java'
   1226             version = string.join(vminfo,', ')
   1227             if not version:
   1228                 version = vendor
   1229 
   1230     # System specific extensions

   1231     if system == 'OpenVMS':
   1232         # OpenVMS seems to have release and version mixed up

   1233         if not release or release == '0':
   1234             release = version
   1235             version = ''
   1236         # Get processor information

   1237         try:
   1238             import vms_lib
   1239         except ImportError:
   1240             pass
   1241         else:
   1242             csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
   1243             if (cpu_number >= 128):
   1244                 processor = 'Alpha'
   1245             else:
   1246                 processor = 'VAX'
   1247     if not processor:
   1248         # Get processor information from the uname system command

   1249         processor = _syscmd_uname('-p','')
   1250 
   1251     #If any unknowns still exist, replace them with ''s, which are more portable

   1252     if system == 'unknown':
   1253         system = ''
   1254     if node == 'unknown':
   1255         node = ''
   1256     if release == 'unknown':
   1257         release = ''
   1258     if version == 'unknown':
   1259         version = ''
   1260     if machine == 'unknown':
   1261         machine = ''
   1262     if processor == 'unknown':
   1263         processor = ''
   1264 
   1265     #  normalize name

   1266     if system == 'Microsoft' and release == 'Windows':
   1267         system = 'Windows'
   1268         release = 'Vista'
   1269 
   1270     _uname_cache = system,node,release,version,machine,processor
   1271     return _uname_cache
   1272 
   1273 ### Direct interfaces to some of the uname() return values

   1274 
   1275 def system():
   1276 
   1277     """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
   1278 
   1279         An empty string is returned if the value cannot be determined.
   1280 
   1281     """
   1282     return uname()[0]
   1283 
   1284 def node():
   1285 
   1286     """ Returns the computer's network name (which may not be fully
   1287         qualified)
   1288 
   1289         An empty string is returned if the value cannot be determined.
   1290 
   1291     """
   1292     return uname()[1]
   1293 
   1294 def release():
   1295 
   1296     """ Returns the system's release, e.g. '2.2.0' or 'NT'
   1297 
   1298         An empty string is returned if the value cannot be determined.
   1299 
   1300     """
   1301     return uname()[2]
   1302 
   1303 def version():
   1304 
   1305     """ Returns the system's release version, e.g. '#3 on degas'
   1306 
   1307         An empty string is returned if the value cannot be determined.
   1308 
   1309     """
   1310     return uname()[3]
   1311 
   1312 def machine():
   1313 
   1314     """ Returns the machine type, e.g. 'i386'
   1315 
   1316         An empty string is returned if the value cannot be determined.
   1317 
   1318     """
   1319     return uname()[4]
   1320 
   1321 def processor():
   1322 
   1323     """ Returns the (true) processor name, e.g. 'amdk6'
   1324 
   1325         An empty string is returned if the value cannot be
   1326         determined. Note that many platforms do not provide this
   1327         information or simply return the same value as for machine(),
   1328         e.g.  NetBSD does this.
   1329 
   1330     """
   1331     return uname()[5]
   1332 
   1333 ### Various APIs for extracting information from sys.version

   1334 
   1335 _sys_version_parser = re.compile(
   1336     r'([\w.+]+)\s*'
   1337     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
   1338     '\[([^\]]+)\]?')
   1339 
   1340 _ironpython_sys_version_parser = re.compile(
   1341     r'IronPython\s*'
   1342     '([\d\.]+)'
   1343     '(?: \(([\d\.]+)\))?'
   1344     ' on (.NET [\d\.]+)')
   1345 
   1346 _pypy_sys_version_parser = re.compile(
   1347     r'([\w.+]+)\s*'
   1348     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
   1349     '\[PyPy [^\]]+\]?')
   1350 
   1351 _sys_version_cache = {}
   1352 
   1353 def _sys_version(sys_version=None):
   1354 
   1355     """ Returns a parsed version of Python's sys.version as tuple
   1356         (name, version, branch, revision, buildno, builddate, compiler)
   1357         referring to the Python implementation name, version, branch,
   1358         revision, build number, build date/time as string and the compiler
   1359         identification string.
   1360 
   1361         Note that unlike the Python sys.version, the returned value
   1362         for the Python version will always include the patchlevel (it
   1363         defaults to '.0').
   1364 
   1365         The function returns empty strings for tuple entries that
   1366         cannot be determined.
   1367 
   1368         sys_version may be given to parse an alternative version
   1369         string, e.g. if the version was read from a different Python
   1370         interpreter.
   1371 
   1372     """
   1373     # Get the Python version

   1374     if sys_version is None:
   1375         sys_version = sys.version
   1376 
   1377     # Try the cache first

   1378     result = _sys_version_cache.get(sys_version, None)
   1379     if result is not None:
   1380         return result
   1381 
   1382     # Parse it

   1383     if sys_version[:10] == 'IronPython':
   1384         # IronPython

   1385         name = 'IronPython'
   1386         match = _ironpython_sys_version_parser.match(sys_version)
   1387         if match is None:
   1388             raise ValueError(
   1389                 'failed to parse IronPython sys.version: %s' %
   1390                 repr(sys_version))
   1391         version, alt_version, compiler = match.groups()
   1392         buildno = ''
   1393         builddate = ''
   1394 
   1395     elif sys.platform[:4] == 'java':
   1396         # Jython

   1397         name = 'Jython'
   1398         match = _sys_version_parser.match(sys_version)
   1399         if match is None:
   1400             raise ValueError(
   1401                 'failed to parse Jython sys.version: %s' %
   1402                 repr(sys_version))
   1403         version, buildno, builddate, buildtime, _ = match.groups()
   1404         compiler = sys.platform
   1405 
   1406     elif "PyPy" in sys_version:
   1407         # PyPy

   1408         name = "PyPy"
   1409         match = _pypy_sys_version_parser.match(sys_version)
   1410         if match is None:
   1411             raise ValueError("failed to parse PyPy sys.version: %s" %
   1412                              repr(sys_version))
   1413         version, buildno, builddate, buildtime = match.groups()
   1414         compiler = ""
   1415 
   1416     else:
   1417         # CPython

   1418         match = _sys_version_parser.match(sys_version)
   1419         if match is None:
   1420             raise ValueError(
   1421                 'failed to parse CPython sys.version: %s' %
   1422                 repr(sys_version))
   1423         version, buildno, builddate, buildtime, compiler = \
   1424               match.groups()
   1425         name = 'CPython'
   1426         builddate = builddate + ' ' + buildtime
   1427 
   1428     if hasattr(sys, 'subversion'):
   1429         # sys.subversion was added in Python 2.5

   1430         _, branch, revision = sys.subversion
   1431     else:
   1432         branch = ''
   1433         revision = ''
   1434 
   1435     # Add the patchlevel version if missing

   1436     l = string.split(version, '.')
   1437     if len(l) == 2:
   1438         l.append('0')
   1439         version = string.join(l, '.')
   1440 
   1441     # Build and cache the result

   1442     result = (name, version, branch, revision, buildno, builddate, compiler)
   1443     _sys_version_cache[sys_version] = result
   1444     return result
   1445 
   1446 def python_implementation():
   1447 
   1448     """ Returns a string identifying the Python implementation.
   1449 
   1450         Currently, the following implementations are identified:
   1451           'CPython' (C implementation of Python),
   1452           'IronPython' (.NET implementation of Python),
   1453           'Jython' (Java implementation of Python),
   1454           'PyPy' (Python implementation of Python).
   1455 
   1456     """
   1457     return _sys_version()[0]
   1458 
   1459 def python_version():
   1460 
   1461     """ Returns the Python version as string 'major.minor.patchlevel'
   1462 
   1463         Note that unlike the Python sys.version, the returned value
   1464         will always include the patchlevel (it defaults to 0).
   1465 
   1466     """
   1467     return _sys_version()[1]
   1468 
   1469 def python_version_tuple():
   1470 
   1471     """ Returns the Python version as tuple (major, minor, patchlevel)
   1472         of strings.
   1473 
   1474         Note that unlike the Python sys.version, the returned value
   1475         will always include the patchlevel (it defaults to 0).
   1476 
   1477     """
   1478     return tuple(string.split(_sys_version()[1], '.'))
   1479 
   1480 def python_branch():
   1481 
   1482     """ Returns a string identifying the Python implementation
   1483         branch.
   1484 
   1485         For CPython this is the Subversion branch from which the
   1486         Python binary was built.
   1487 
   1488         If not available, an empty string is returned.
   1489 
   1490     """
   1491 
   1492     return _sys_version()[2]
   1493 
   1494 def python_revision():
   1495 
   1496     """ Returns a string identifying the Python implementation
   1497         revision.
   1498 
   1499         For CPython this is the Subversion revision from which the
   1500         Python binary was built.
   1501 
   1502         If not available, an empty string is returned.
   1503 
   1504     """
   1505     return _sys_version()[3]
   1506 
   1507 def python_build():
   1508 
   1509     """ Returns a tuple (buildno, builddate) stating the Python
   1510         build number and date as strings.
   1511 
   1512     """
   1513     return _sys_version()[4:6]
   1514 
   1515 def python_compiler():
   1516 
   1517     """ Returns a string identifying the compiler used for compiling
   1518         Python.
   1519 
   1520     """
   1521     return _sys_version()[6]
   1522 
   1523 ### The Opus Magnum of platform strings :-)

   1524 
   1525 _platform_cache = {}
   1526 
   1527 def platform(aliased=0, terse=0):
   1528 
   1529     """ Returns a single string identifying the underlying platform
   1530         with as much useful information as possible (but no more :).
   1531 
   1532         The output is intended to be human readable rather than
   1533         machine parseable. It may look different on different
   1534         platforms and this is intended.
   1535 
   1536         If "aliased" is true, the function will use aliases for
   1537         various platforms that report system names which differ from
   1538         their common names, e.g. SunOS will be reported as
   1539         Solaris. The system_alias() function is used to implement
   1540         this.
   1541 
   1542         Setting terse to true causes the function to return only the
   1543         absolute minimum information needed to identify the platform.
   1544 
   1545     """
   1546     result = _platform_cache.get((aliased, terse), None)
   1547     if result is not None:
   1548         return result
   1549 
   1550     # Get uname information and then apply platform specific cosmetics

   1551     # to it...

   1552     system,node,release,version,machine,processor = uname()
   1553     if machine == processor:
   1554         processor = ''
   1555     if aliased:
   1556         system,release,version = system_alias(system,release,version)
   1557 
   1558     if system == 'Windows':
   1559         # MS platforms

   1560         rel,vers,csd,ptype = win32_ver(version)
   1561         if terse:
   1562             platform = _platform(system,release)
   1563         else:
   1564             platform = _platform(system,release,version,csd)
   1565 
   1566     elif system in ('Linux',):
   1567         # Linux based systems

   1568         distname,distversion,distid = dist('')
   1569         if distname and not terse:
   1570             platform = _platform(system,release,machine,processor,
   1571                                  'with',
   1572                                  distname,distversion,distid)
   1573         else:
   1574             # If the distribution name is unknown check for libc vs. glibc

   1575             libcname,libcversion = libc_ver(sys.executable)
   1576             platform = _platform(system,release,machine,processor,
   1577                                  'with',
   1578                                  libcname+libcversion)
   1579     elif system == 'Java':
   1580         # Java platforms

   1581         r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
   1582         if terse or not os_name:
   1583             platform = _platform(system,release,version)
   1584         else:
   1585             platform = _platform(system,release,version,
   1586                                  'on',
   1587                                  os_name,os_version,os_arch)
   1588 
   1589     elif system == 'MacOS':
   1590         # MacOS platforms

   1591         if terse:
   1592             platform = _platform(system,release)
   1593         else:
   1594             platform = _platform(system,release,machine)
   1595 
   1596     else:
   1597         # Generic handler

   1598         if terse:
   1599             platform = _platform(system,release)
   1600         else:
   1601             bits,linkage = architecture(sys.executable)
   1602             platform = _platform(system,release,machine,processor,bits,linkage)
   1603 
   1604     _platform_cache[(aliased, terse)] = platform
   1605     return platform
   1606 
   1607 ### Command line interface

   1608 
   1609 if __name__ == '__main__':
   1610     # Default is to print the aliased verbose platform string

   1611     terse = ('terse' in sys.argv or '--terse' in sys.argv)
   1612     aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
   1613     print platform(aliased,terse)
   1614     sys.exit(0)
   1615