Home | History | Annotate | Download | only in python2.7
      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 and 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 (service pack), 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             elif min == 2:
    677                 if product_type == VER_NT_WORKSTATION:
    678                     release = '8'
    679                 else:
    680                     release = '2012Server'
    681             else:
    682                 release = 'post2012Server'
    683 
    684     else:
    685         if not release:
    686             # E.g. Win3.1 with win32s
    687             release = '%i.%i' % (maj,min)
    688         return release,version,csd,ptype
    689 
    690     # Open the registry key
    691     try:
    692         keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
    693         # Get a value to make sure the key exists...
    694         RegQueryValueEx(keyCurVer, 'SystemRoot')
    695     except:
    696         return release,version,csd,ptype
    697 
    698     # Parse values
    699     #subversion = _win32_getvalue(keyCurVer,
    700     #                            'SubVersionNumber',
    701     #                            ('',1))[0]
    702     #if subversion:
    703     #   release = release + subversion # 95a, 95b, etc.
    704     build = _win32_getvalue(keyCurVer,
    705                             'CurrentBuildNumber',
    706                             ('',1))[0]
    707     ptype = _win32_getvalue(keyCurVer,
    708                            'CurrentType',
    709                            (ptype,1))[0]
    710 
    711     # Normalize version
    712     version = _norm_version(version,build)
    713 
    714     # Close key
    715     RegCloseKey(keyCurVer)
    716     return release,version,csd,ptype
    717 
    718 def _mac_ver_lookup(selectors,default=None):
    719 
    720     from gestalt import gestalt
    721     import MacOS
    722     l = []
    723     append = l.append
    724     for selector in selectors:
    725         try:
    726             append(gestalt(selector))
    727         except (RuntimeError, MacOS.Error):
    728             append(default)
    729     return l
    730 
    731 def _bcd2str(bcd):
    732 
    733     return hex(bcd)[2:]
    734 
    735 def _mac_ver_gestalt():
    736     """
    737         Thanks to Mark R. Levinson for mailing documentation links and
    738         code examples for this function. Documentation for the
    739         gestalt() API is available online at:
    740 
    741            http://www.rgaros.nl/gestalt/
    742     """
    743     # Check whether the version info module is available
    744     try:
    745         import gestalt
    746         import MacOS
    747     except ImportError:
    748         return None
    749     # Get the infos
    750     sysv,sysa = _mac_ver_lookup(('sysv','sysa'))
    751     # Decode the infos
    752     if sysv:
    753         major = (sysv & 0xFF00) >> 8
    754         minor = (sysv & 0x00F0) >> 4
    755         patch = (sysv & 0x000F)
    756 
    757         if (major, minor) >= (10, 4):
    758             # the 'sysv' gestald cannot return patchlevels
    759             # higher than 9. Apple introduced 3 new
    760             # gestalt codes in 10.4 to deal with this
    761             # issue (needed because patch levels can
    762             # run higher than 9, such as 10.4.11)
    763             major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
    764             release = '%i.%i.%i' %(major, minor, patch)
    765         else:
    766             release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
    767 
    768     if sysa:
    769         machine = {0x1: '68k',
    770                    0x2: 'PowerPC',
    771                    0xa: 'i386'}.get(sysa,'')
    772 
    773     versioninfo=('', '', '')
    774     return release,versioninfo,machine
    775 
    776 def _mac_ver_xml():
    777     fn = '/System/Library/CoreServices/SystemVersion.plist'
    778     if not os.path.exists(fn):
    779         return None
    780 
    781     try:
    782         import plistlib
    783     except ImportError:
    784         return None
    785 
    786     pl = plistlib.readPlist(fn)
    787     release = pl['ProductVersion']
    788     versioninfo=('', '', '')
    789     machine = os.uname()[4]
    790     if machine in ('ppc', 'Power Macintosh'):
    791         # for compatibility with the gestalt based code
    792         machine = 'PowerPC'
    793 
    794     return release,versioninfo,machine
    795 
    796 
    797 def mac_ver(release='',versioninfo=('','',''),machine=''):
    798 
    799     """ Get MacOS version information and return it as tuple (release,
    800         versioninfo, machine) with versioninfo being a tuple (version,
    801         dev_stage, non_release_version).
    802 
    803         Entries which cannot be determined are set to the paramter values
    804         which default to ''. All tuple entries are strings.
    805     """
    806 
    807     # First try reading the information from an XML file which should
    808     # always be present
    809     info = _mac_ver_xml()
    810     if info is not None:
    811         return info
    812 
    813     # If that doesn't work for some reason fall back to reading the
    814     # information using gestalt calls.
    815     info = _mac_ver_gestalt()
    816     if info is not None:
    817         return info
    818 
    819     # If that also doesn't work return the default values
    820     return release,versioninfo,machine
    821 
    822 def _java_getprop(name,default):
    823 
    824     from java.lang import System
    825     try:
    826         value = System.getProperty(name)
    827         if value is None:
    828             return default
    829         return value
    830     except AttributeError:
    831         return default
    832 
    833 def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
    834 
    835     """ Version interface for Jython.
    836 
    837         Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
    838         a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
    839         tuple (os_name,os_version,os_arch).
    840 
    841         Values which cannot be determined are set to the defaults
    842         given as parameters (which all default to '').
    843 
    844     """
    845     # Import the needed APIs
    846     try:
    847         import java.lang
    848     except ImportError:
    849         return release,vendor,vminfo,osinfo
    850 
    851     vendor = _java_getprop('java.vendor', vendor)
    852     release = _java_getprop('java.version', release)
    853     vm_name, vm_release, vm_vendor = vminfo
    854     vm_name = _java_getprop('java.vm.name', vm_name)
    855     vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
    856     vm_release = _java_getprop('java.vm.version', vm_release)
    857     vminfo = vm_name, vm_release, vm_vendor
    858     os_name, os_version, os_arch = osinfo
    859     os_arch = _java_getprop('java.os.arch', os_arch)
    860     os_name = _java_getprop('java.os.name', os_name)
    861     os_version = _java_getprop('java.os.version', os_version)
    862     osinfo = os_name, os_version, os_arch
    863 
    864     return release, vendor, vminfo, osinfo
    865 
    866 ### System name aliasing
    867 
    868 def system_alias(system,release,version):
    869 
    870     """ Returns (system,release,version) aliased to common
    871         marketing names used for some systems.
    872 
    873         It also does some reordering of the information in some cases
    874         where it would otherwise cause confusion.
    875 
    876     """
    877     if system == 'Rhapsody':
    878         # Apple's BSD derivative
    879         # XXX How can we determine the marketing release number ?
    880         return 'MacOS X Server',system+release,version
    881 
    882     elif system == 'SunOS':
    883         # Sun's OS
    884         if release < '5':
    885             # These releases use the old name SunOS
    886             return system,release,version
    887         # Modify release (marketing release = SunOS release - 3)
    888         l = string.split(release,'.')
    889         if l:
    890             try:
    891                 major = int(l[0])
    892             except ValueError:
    893                 pass
    894             else:
    895                 major = major - 3
    896                 l[0] = str(major)
    897                 release = string.join(l,'.')
    898         if release < '6':
    899             system = 'Solaris'
    900         else:
    901             # XXX Whatever the new SunOS marketing name is...
    902             system = 'Solaris'
    903 
    904     elif system == 'IRIX64':
    905         # IRIX reports IRIX64 on platforms with 64-bit support; yet it
    906         # is really a version and not a different platform, since 32-bit
    907         # apps are also supported..
    908         system = 'IRIX'
    909         if version:
    910             version = version + ' (64bit)'
    911         else:
    912             version = '64bit'
    913 
    914     elif system in ('win32','win16'):
    915         # In case one of the other tricks
    916         system = 'Windows'
    917 
    918     return system,release,version
    919 
    920 ### Various internal helpers
    921 
    922 def _platform(*args):
    923 
    924     """ Helper to format the platform string in a filename
    925         compatible format e.g. "system-version-machine".
    926     """
    927     # Format the platform string
    928     platform = string.join(
    929         map(string.strip,
    930             filter(len, args)),
    931         '-')
    932 
    933     # Cleanup some possible filename obstacles...
    934     replace = string.replace
    935     platform = replace(platform,' ','_')
    936     platform = replace(platform,'/','-')
    937     platform = replace(platform,'\\','-')
    938     platform = replace(platform,':','-')
    939     platform = replace(platform,';','-')
    940     platform = replace(platform,'"','-')
    941     platform = replace(platform,'(','-')
    942     platform = replace(platform,')','-')
    943 
    944     # No need to report 'unknown' information...
    945     platform = replace(platform,'unknown','')
    946 
    947     # Fold '--'s and remove trailing '-'
    948     while 1:
    949         cleaned = replace(platform,'--','-')
    950         if cleaned == platform:
    951             break
    952         platform = cleaned
    953     while platform[-1] == '-':
    954         platform = platform[:-1]
    955 
    956     return platform
    957 
    958 def _node(default=''):
    959 
    960     """ Helper to determine the node name of this machine.
    961     """
    962     try:
    963         import socket
    964     except ImportError:
    965         # No sockets...
    966         return default
    967     try:
    968         return socket.gethostname()
    969     except socket.error:
    970         # Still not working...
    971         return default
    972 
    973 # os.path.abspath is new in Python 1.5.2:
    974 if not hasattr(os.path,'abspath'):
    975 
    976     def _abspath(path,
    977 
    978                  isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
    979                  normpath=os.path.normpath):
    980 
    981         if not isabs(path):
    982             path = join(getcwd(), path)
    983         return normpath(path)
    984 
    985 else:
    986 
    987     _abspath = os.path.abspath
    988 
    989 def _follow_symlinks(filepath):
    990 
    991     """ In case filepath is a symlink, follow it until a
    992         real file is reached.
    993     """
    994     filepath = _abspath(filepath)
    995     while os.path.islink(filepath):
    996         filepath = os.path.normpath(
    997             os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
    998     return filepath
    999 
   1000 def _syscmd_uname(option,default=''):
   1001 
   1002     """ Interface to the system's uname command.
   1003     """
   1004     if sys.platform in ('dos','win32','win16','os2'):
   1005         # XXX Others too ?
   1006         return default
   1007     try:
   1008         f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
   1009     except (AttributeError,os.error):
   1010         return default
   1011     output = string.strip(f.read())
   1012     rc = f.close()
   1013     if not output or rc:
   1014         return default
   1015     else:
   1016         return output
   1017 
   1018 def _syscmd_file(target,default=''):
   1019 
   1020     """ Interface to the system's file command.
   1021 
   1022         The function uses the -b option of the file command to have it
   1023         ommit the filename in its output and if possible the -L option
   1024         to have the command follow symlinks. It returns default in
   1025         case the command should fail.
   1026 
   1027     """
   1028 
   1029     # We do the import here to avoid a bootstrap issue.
   1030     # See c73b90b6dadd changeset.
   1031     #
   1032     # [..]
   1033     # ranlib libpython2.7.a
   1034     # gcc   -o python \
   1035     #        Modules/python.o \
   1036     #        libpython2.7.a -lsocket -lnsl -ldl    -lm
   1037     # Traceback (most recent call last):
   1038     #  File "./setup.py", line 8, in <module>
   1039     #    from platform import machine as platform_machine
   1040     #  File "[..]/build/Lib/platform.py", line 116, in <module>
   1041     #    import sys,string,os,re,subprocess
   1042     #  File "[..]/build/Lib/subprocess.py", line 429, in <module>
   1043     #    import select
   1044     # ImportError: No module named select
   1045 
   1046     import subprocess
   1047 
   1048     if sys.platform in ('dos','win32','win16','os2'):
   1049         # XXX Others too ?
   1050         return default
   1051     target = _follow_symlinks(target)
   1052     try:
   1053         proc = subprocess.Popen(['file', target],
   1054                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
   1055 
   1056     except (AttributeError,os.error):
   1057         return default
   1058     output = proc.communicate()[0]
   1059     rc = proc.wait()
   1060     if not output or rc:
   1061         return default
   1062     else:
   1063         return output
   1064 
   1065 ### Information about the used architecture
   1066 
   1067 # Default values for architecture; non-empty strings override the
   1068 # defaults given as parameters
   1069 _default_architecture = {
   1070     'win32': ('','WindowsPE'),
   1071     'win16': ('','Windows'),
   1072     'dos': ('','MSDOS'),
   1073 }
   1074 
   1075 _architecture_split = re.compile(r'[\s,]').split
   1076 
   1077 def architecture(executable=sys.executable,bits='',linkage=''):
   1078 
   1079     """ Queries the given executable (defaults to the Python interpreter
   1080         binary) for various architecture information.
   1081 
   1082         Returns a tuple (bits,linkage) which contains information about
   1083         the bit architecture and the linkage format used for the
   1084         executable. Both values are returned as strings.
   1085 
   1086         Values that cannot be determined are returned as given by the
   1087         parameter presets. If bits is given as '', the sizeof(pointer)
   1088         (or sizeof(long) on Python version < 1.5.2) is used as
   1089         indicator for the supported pointer size.
   1090 
   1091         The function relies on the system's "file" command to do the
   1092         actual work. This is available on most if not all Unix
   1093         platforms. On some non-Unix platforms where the "file" command
   1094         does not exist and the executable is set to the Python interpreter
   1095         binary defaults from _default_architecture are used.
   1096 
   1097     """
   1098     # Use the sizeof(pointer) as default number of bits if nothing
   1099     # else is given as default.
   1100     if not bits:
   1101         import struct
   1102         try:
   1103             size = struct.calcsize('P')
   1104         except struct.error:
   1105             # Older installations can only query longs
   1106             size = struct.calcsize('l')
   1107         bits = str(size*8) + 'bit'
   1108 
   1109     # Get data from the 'file' system command
   1110     if executable:
   1111         output = _syscmd_file(executable, '')
   1112     else:
   1113         output = ''
   1114 
   1115     if not output and \
   1116        executable == sys.executable:
   1117         # "file" command did not return anything; we'll try to provide
   1118         # some sensible defaults then...
   1119         if sys.platform in _default_architecture:
   1120             b, l = _default_architecture[sys.platform]
   1121             if b:
   1122                 bits = b
   1123             if l:
   1124                 linkage = l
   1125         return bits, linkage
   1126 
   1127     # Split the output into a list of strings omitting the filename
   1128     fileout = _architecture_split(output)[1:]
   1129 
   1130     if 'executable' not in fileout:
   1131         # Format not supported
   1132         return bits,linkage
   1133 
   1134     # Bits
   1135     if '32-bit' in fileout:
   1136         bits = '32bit'
   1137     elif 'N32' in fileout:
   1138         # On Irix only
   1139         bits = 'n32bit'
   1140     elif '64-bit' in fileout:
   1141         bits = '64bit'
   1142 
   1143     # Linkage
   1144     if 'ELF' in fileout:
   1145         linkage = 'ELF'
   1146     elif 'PE' in fileout:
   1147         # E.g. Windows uses this format
   1148         if 'Windows' in fileout:
   1149             linkage = 'WindowsPE'
   1150         else:
   1151             linkage = 'PE'
   1152     elif 'COFF' in fileout:
   1153         linkage = 'COFF'
   1154     elif 'MS-DOS' in fileout:
   1155         linkage = 'MSDOS'
   1156     else:
   1157         # XXX the A.OUT format also falls under this class...
   1158         pass
   1159 
   1160     return bits,linkage
   1161 
   1162 ### Portable uname() interface
   1163 
   1164 _uname_cache = None
   1165 
   1166 def uname():
   1167 
   1168     """ Fairly portable uname interface. Returns a tuple
   1169         of strings (system,node,release,version,machine,processor)
   1170         identifying the underlying platform.
   1171 
   1172         Note that unlike the os.uname function this also returns
   1173         possible processor information as an additional tuple entry.
   1174 
   1175         Entries which cannot be determined are set to ''.
   1176 
   1177     """
   1178     global _uname_cache
   1179     no_os_uname = 0
   1180 
   1181     if _uname_cache is not None:
   1182         return _uname_cache
   1183 
   1184     processor = ''
   1185 
   1186     # Get some infos from the builtin os.uname API...
   1187     try:
   1188         system,node,release,version,machine = os.uname()
   1189     except AttributeError:
   1190         no_os_uname = 1
   1191 
   1192     if no_os_uname or not filter(None, (system, node, release, version, machine)):
   1193         # Hmm, no there is either no uname or uname has returned
   1194         #'unknowns'... we'll have to poke around the system then.
   1195         if no_os_uname:
   1196             system = sys.platform
   1197             release = ''
   1198             version = ''
   1199             node = _node()
   1200             machine = ''
   1201 
   1202         use_syscmd_ver = 1
   1203 
   1204         # Try win32_ver() on win32 platforms
   1205         if system == 'win32':
   1206             release,version,csd,ptype = win32_ver()
   1207             if release and version:
   1208                 use_syscmd_ver = 0
   1209             # Try to use the PROCESSOR_* environment variables
   1210             # available on Win XP and later; see
   1211             # http://support.microsoft.com/kb/888731 and
   1212             # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
   1213             if not machine:
   1214                 # WOW64 processes mask the native architecture
   1215                 if "PROCESSOR_ARCHITEW6432" in os.environ:
   1216                     machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
   1217                 else:
   1218                     machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
   1219             if not processor:
   1220                 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
   1221 
   1222         # Try the 'ver' system command available on some
   1223         # platforms
   1224         if use_syscmd_ver:
   1225             system,release,version = _syscmd_ver(system)
   1226             # Normalize system to what win32_ver() normally returns
   1227             # (_syscmd_ver() tends to return the vendor name as well)
   1228             if system == 'Microsoft Windows':
   1229                 system = 'Windows'
   1230             elif system == 'Microsoft' and release == 'Windows':
   1231                 # Under Windows Vista and Windows Server 2008,
   1232                 # Microsoft changed the output of the ver command. The
   1233                 # release is no longer printed.  This causes the
   1234                 # system and release to be misidentified.
   1235                 system = 'Windows'
   1236                 if '6.0' == version[:3]:
   1237                     release = 'Vista'
   1238                 else:
   1239                     release = ''
   1240 
   1241         # In case we still don't know anything useful, we'll try to
   1242         # help ourselves
   1243         if system in ('win32','win16'):
   1244             if not version:
   1245                 if system == 'win32':
   1246                     version = '32bit'
   1247                 else:
   1248                     version = '16bit'
   1249             system = 'Windows'
   1250 
   1251         elif system[:4] == 'java':
   1252             release,vendor,vminfo,osinfo = java_ver()
   1253             system = 'Java'
   1254             version = string.join(vminfo,', ')
   1255             if not version:
   1256                 version = vendor
   1257 
   1258     # System specific extensions
   1259     if system == 'OpenVMS':
   1260         # OpenVMS seems to have release and version mixed up
   1261         if not release or release == '0':
   1262             release = version
   1263             version = ''
   1264         # Get processor information
   1265         try:
   1266             import vms_lib
   1267         except ImportError:
   1268             pass
   1269         else:
   1270             csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
   1271             if (cpu_number >= 128):
   1272                 processor = 'Alpha'
   1273             else:
   1274                 processor = 'VAX'
   1275     if not processor:
   1276         # Get processor information from the uname system command
   1277         processor = _syscmd_uname('-p','')
   1278 
   1279     #If any unknowns still exist, replace them with ''s, which are more portable
   1280     if system == 'unknown':
   1281         system = ''
   1282     if node == 'unknown':
   1283         node = ''
   1284     if release == 'unknown':
   1285         release = ''
   1286     if version == 'unknown':
   1287         version = ''
   1288     if machine == 'unknown':
   1289         machine = ''
   1290     if processor == 'unknown':
   1291         processor = ''
   1292 
   1293     #  normalize name
   1294     if system == 'Microsoft' and release == 'Windows':
   1295         system = 'Windows'
   1296         release = 'Vista'
   1297 
   1298     _uname_cache = system,node,release,version,machine,processor
   1299     return _uname_cache
   1300 
   1301 ### Direct interfaces to some of the uname() return values
   1302 
   1303 def system():
   1304 
   1305     """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
   1306 
   1307         An empty string is returned if the value cannot be determined.
   1308 
   1309     """
   1310     return uname()[0]
   1311 
   1312 def node():
   1313 
   1314     """ Returns the computer's network name (which may not be fully
   1315         qualified)
   1316 
   1317         An empty string is returned if the value cannot be determined.
   1318 
   1319     """
   1320     return uname()[1]
   1321 
   1322 def release():
   1323 
   1324     """ Returns the system's release, e.g. '2.2.0' or 'NT'
   1325 
   1326         An empty string is returned if the value cannot be determined.
   1327 
   1328     """
   1329     return uname()[2]
   1330 
   1331 def version():
   1332 
   1333     """ Returns the system's release version, e.g. '#3 on degas'
   1334 
   1335         An empty string is returned if the value cannot be determined.
   1336 
   1337     """
   1338     return uname()[3]
   1339 
   1340 def machine():
   1341 
   1342     """ Returns the machine type, e.g. 'i386'
   1343 
   1344         An empty string is returned if the value cannot be determined.
   1345 
   1346     """
   1347     return uname()[4]
   1348 
   1349 def processor():
   1350 
   1351     """ Returns the (true) processor name, e.g. 'amdk6'
   1352 
   1353         An empty string is returned if the value cannot be
   1354         determined. Note that many platforms do not provide this
   1355         information or simply return the same value as for machine(),
   1356         e.g.  NetBSD does this.
   1357 
   1358     """
   1359     return uname()[5]
   1360 
   1361 ### Various APIs for extracting information from sys.version
   1362 
   1363 _sys_version_parser = re.compile(
   1364     r'([\w.+]+)\s*'
   1365     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
   1366     '\[([^\]]+)\]?')
   1367 
   1368 _ironpython_sys_version_parser = re.compile(
   1369     r'IronPython\s*'
   1370     '([\d\.]+)'
   1371     '(?: \(([\d\.]+)\))?'
   1372     ' on (.NET [\d\.]+)')
   1373 
   1374 _pypy_sys_version_parser = re.compile(
   1375     r'([\w.+]+)\s*'
   1376     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
   1377     '\[PyPy [^\]]+\]?')
   1378 
   1379 _sys_version_cache = {}
   1380 
   1381 def _sys_version(sys_version=None):
   1382 
   1383     """ Returns a parsed version of Python's sys.version as tuple
   1384         (name, version, branch, revision, buildno, builddate, compiler)
   1385         referring to the Python implementation name, version, branch,
   1386         revision, build number, build date/time as string and the compiler
   1387         identification string.
   1388 
   1389         Note that unlike the Python sys.version, the returned value
   1390         for the Python version will always include the patchlevel (it
   1391         defaults to '.0').
   1392 
   1393         The function returns empty strings for tuple entries that
   1394         cannot be determined.
   1395 
   1396         sys_version may be given to parse an alternative version
   1397         string, e.g. if the version was read from a different Python
   1398         interpreter.
   1399 
   1400     """
   1401     # Get the Python version
   1402     if sys_version is None:
   1403         sys_version = sys.version
   1404 
   1405     # Try the cache first
   1406     result = _sys_version_cache.get(sys_version, None)
   1407     if result is not None:
   1408         return result
   1409 
   1410     # Parse it
   1411     if sys_version[:10] == 'IronPython':
   1412         # IronPython
   1413         name = 'IronPython'
   1414         match = _ironpython_sys_version_parser.match(sys_version)
   1415         if match is None:
   1416             raise ValueError(
   1417                 'failed to parse IronPython sys.version: %s' %
   1418                 repr(sys_version))
   1419         version, alt_version, compiler = match.groups()
   1420         buildno = ''
   1421         builddate = ''
   1422 
   1423     elif sys.platform[:4] == 'java':
   1424         # Jython
   1425         name = 'Jython'
   1426         match = _sys_version_parser.match(sys_version)
   1427         if match is None:
   1428             raise ValueError(
   1429                 'failed to parse Jython sys.version: %s' %
   1430                 repr(sys_version))
   1431         version, buildno, builddate, buildtime, _ = match.groups()
   1432         compiler = sys.platform
   1433 
   1434     elif "PyPy" in sys_version:
   1435         # PyPy
   1436         name = "PyPy"
   1437         match = _pypy_sys_version_parser.match(sys_version)
   1438         if match is None:
   1439             raise ValueError("failed to parse PyPy sys.version: %s" %
   1440                              repr(sys_version))
   1441         version, buildno, builddate, buildtime = match.groups()
   1442         compiler = ""
   1443 
   1444     else:
   1445         # CPython
   1446         match = _sys_version_parser.match(sys_version)
   1447         if match is None:
   1448             raise ValueError(
   1449                 'failed to parse CPython sys.version: %s' %
   1450                 repr(sys_version))
   1451         version, buildno, builddate, buildtime, compiler = \
   1452               match.groups()
   1453         name = 'CPython'
   1454         builddate = builddate + ' ' + buildtime
   1455 
   1456     if hasattr(sys, 'subversion'):
   1457         # sys.subversion was added in Python 2.5
   1458         _, branch, revision = sys.subversion
   1459     else:
   1460         branch = ''
   1461         revision = ''
   1462 
   1463     # Add the patchlevel version if missing
   1464     l = string.split(version, '.')
   1465     if len(l) == 2:
   1466         l.append('0')
   1467         version = string.join(l, '.')
   1468 
   1469     # Build and cache the result
   1470     result = (name, version, branch, revision, buildno, builddate, compiler)
   1471     _sys_version_cache[sys_version] = result
   1472     return result
   1473 
   1474 def python_implementation():
   1475 
   1476     """ Returns a string identifying the Python implementation.
   1477 
   1478         Currently, the following implementations are identified:
   1479           'CPython' (C implementation of Python),
   1480           'IronPython' (.NET implementation of Python),
   1481           'Jython' (Java implementation of Python),
   1482           'PyPy' (Python implementation of Python).
   1483 
   1484     """
   1485     return _sys_version()[0]
   1486 
   1487 def python_version():
   1488 
   1489     """ Returns the Python version as string 'major.minor.patchlevel'
   1490 
   1491         Note that unlike the Python sys.version, the returned value
   1492         will always include the patchlevel (it defaults to 0).
   1493 
   1494     """
   1495     return _sys_version()[1]
   1496 
   1497 def python_version_tuple():
   1498 
   1499     """ Returns the Python version as tuple (major, minor, patchlevel)
   1500         of strings.
   1501 
   1502         Note that unlike the Python sys.version, the returned value
   1503         will always include the patchlevel (it defaults to 0).
   1504 
   1505     """
   1506     return tuple(string.split(_sys_version()[1], '.'))
   1507 
   1508 def python_branch():
   1509 
   1510     """ Returns a string identifying the Python implementation
   1511         branch.
   1512 
   1513         For CPython this is the Subversion branch from which the
   1514         Python binary was built.
   1515 
   1516         If not available, an empty string is returned.
   1517 
   1518     """
   1519 
   1520     return _sys_version()[2]
   1521 
   1522 def python_revision():
   1523 
   1524     """ Returns a string identifying the Python implementation
   1525         revision.
   1526 
   1527         For CPython this is the Subversion revision from which the
   1528         Python binary was built.
   1529 
   1530         If not available, an empty string is returned.
   1531 
   1532     """
   1533     return _sys_version()[3]
   1534 
   1535 def python_build():
   1536 
   1537     """ Returns a tuple (buildno, builddate) stating the Python
   1538         build number and date as strings.
   1539 
   1540     """
   1541     return _sys_version()[4:6]
   1542 
   1543 def python_compiler():
   1544 
   1545     """ Returns a string identifying the compiler used for compiling
   1546         Python.
   1547 
   1548     """
   1549     return _sys_version()[6]
   1550 
   1551 ### The Opus Magnum of platform strings :-)
   1552 
   1553 _platform_cache = {}
   1554 
   1555 def platform(aliased=0, terse=0):
   1556 
   1557     """ Returns a single string identifying the underlying platform
   1558         with as much useful information as possible (but no more :).
   1559 
   1560         The output is intended to be human readable rather than
   1561         machine parseable. It may look different on different
   1562         platforms and this is intended.
   1563 
   1564         If "aliased" is true, the function will use aliases for
   1565         various platforms that report system names which differ from
   1566         their common names, e.g. SunOS will be reported as
   1567         Solaris. The system_alias() function is used to implement
   1568         this.
   1569 
   1570         Setting terse to true causes the function to return only the
   1571         absolute minimum information needed to identify the platform.
   1572 
   1573     """
   1574     result = _platform_cache.get((aliased, terse), None)
   1575     if result is not None:
   1576         return result
   1577 
   1578     # Get uname information and then apply platform specific cosmetics
   1579     # to it...
   1580     system,node,release,version,machine,processor = uname()
   1581     if machine == processor:
   1582         processor = ''
   1583     if aliased:
   1584         system,release,version = system_alias(system,release,version)
   1585 
   1586     if system == 'Windows':
   1587         # MS platforms
   1588         rel,vers,csd,ptype = win32_ver(version)
   1589         if terse:
   1590             platform = _platform(system,release)
   1591         else:
   1592             platform = _platform(system,release,version,csd)
   1593 
   1594     elif system in ('Linux',):
   1595         # Linux based systems
   1596         distname,distversion,distid = dist('')
   1597         if distname and not terse:
   1598             platform = _platform(system,release,machine,processor,
   1599                                  'with',
   1600                                  distname,distversion,distid)
   1601         else:
   1602             # If the distribution name is unknown check for libc vs. glibc
   1603             libcname,libcversion = libc_ver(sys.executable)
   1604             platform = _platform(system,release,machine,processor,
   1605                                  'with',
   1606                                  libcname+libcversion)
   1607     elif system == 'Java':
   1608         # Java platforms
   1609         r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
   1610         if terse or not os_name:
   1611             platform = _platform(system,release,version)
   1612         else:
   1613             platform = _platform(system,release,version,
   1614                                  'on',
   1615                                  os_name,os_version,os_arch)
   1616 
   1617     elif system == 'MacOS':
   1618         # MacOS platforms
   1619         if terse:
   1620             platform = _platform(system,release)
   1621         else:
   1622             platform = _platform(system,release,machine)
   1623 
   1624     else:
   1625         # Generic handler
   1626         if terse:
   1627             platform = _platform(system,release)
   1628         else:
   1629             bits,linkage = architecture(sys.executable)
   1630             platform = _platform(system,release,machine,processor,bits,linkage)
   1631 
   1632     _platform_cache[(aliased, terse)] = platform
   1633     return platform
   1634 
   1635 ### Command line interface
   1636 
   1637 if __name__ == '__main__':
   1638     # Default is to print the aliased verbose platform string
   1639     terse = ('terse' in sys.argv or '--terse' in sys.argv)
   1640     aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
   1641     print platform(aliased,terse)
   1642     sys.exit(0)
   1643