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