Back to index

python3.2  3.2.2
platform.py
Go to the documentation of this file.
00001 #!/usr/bin/env python3
00002 
00003 """ This module tries to retrieve as much platform-identifying data as
00004     possible. It makes this information available via function APIs.
00005 
00006     If called from the command line, it prints the platform
00007     information concatenated as single string to stdout. The output
00008     format is useable as part of a filename.
00009 
00010 """
00011 #    This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
00012 #    If you find problems, please submit bug reports/patches via the
00013 #    Python bug tracker (http://bugs.python.org) and assign them to "lemburg".
00014 #
00015 #    Still needed:
00016 #    * more support for WinCE
00017 #    * support for MS-DOS (PythonDX ?)
00018 #    * support for Amiga and other still unsupported platforms running Python
00019 #    * support for additional Linux distributions
00020 #
00021 #    Many thanks to all those who helped adding platform-specific
00022 #    checks (in no particular order):
00023 #
00024 #      Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
00025 #      Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
00026 #      Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
00027 #      Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
00028 #      Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
00029 #      Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
00030 #
00031 #    History:
00032 #
00033 #    <see CVS and SVN checkin messages for history>
00034 #
00035 #    1.0.7 - added DEV_NULL
00036 #    1.0.6 - added linux_distribution()
00037 #    1.0.5 - fixed Java support to allow running the module on Jython
00038 #    1.0.4 - added IronPython support
00039 #    1.0.3 - added normalization of Windows system name
00040 #    1.0.2 - added more Windows support
00041 #    1.0.1 - reformatted to make doc.py happy
00042 #    1.0.0 - reformatted a bit and checked into Python CVS
00043 #    0.8.0 - added sys.version parser and various new access
00044 #            APIs (python_version(), python_compiler(), etc.)
00045 #    0.7.2 - fixed architecture() to use sizeof(pointer) where available
00046 #    0.7.1 - added support for Caldera OpenLinux
00047 #    0.7.0 - some fixes for WinCE; untabified the source file
00048 #    0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
00049 #            vms_lib.getsyi() configured
00050 #    0.6.1 - added code to prevent 'uname -p' on platforms which are
00051 #            known not to support it
00052 #    0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
00053 #            did some cleanup of the interfaces - some APIs have changed
00054 #    0.5.5 - fixed another type in the MacOS code... should have
00055 #            used more coffee today ;-)
00056 #    0.5.4 - fixed a few typos in the MacOS code
00057 #    0.5.3 - added experimental MacOS support; added better popen()
00058 #            workarounds in _syscmd_ver() -- still not 100% elegant
00059 #            though
00060 #    0.5.2 - fixed uname() to return '' instead of 'unknown' in all
00061 #            return values (the system uname command tends to return
00062 #            'unknown' instead of just leaving the field emtpy)
00063 #    0.5.1 - included code for slackware dist; added exception handlers
00064 #            to cover up situations where platforms don't have os.popen
00065 #            (e.g. Mac) or fail on socket.gethostname(); fixed libc
00066 #            detection RE
00067 #    0.5.0 - changed the API names referring to system commands to *syscmd*;
00068 #            added java_ver(); made syscmd_ver() a private
00069 #            API (was system_ver() in previous versions) -- use uname()
00070 #            instead; extended the win32_ver() to also return processor
00071 #            type information
00072 #    0.4.0 - added win32_ver() and modified the platform() output for WinXX
00073 #    0.3.4 - fixed a bug in _follow_symlinks()
00074 #    0.3.3 - fixed popen() and "file" command invokation bugs
00075 #    0.3.2 - added architecture() API and support for it in platform()
00076 #    0.3.1 - fixed syscmd_ver() RE to support Windows NT
00077 #    0.3.0 - added system alias support
00078 #    0.2.3 - removed 'wince' again... oh well.
00079 #    0.2.2 - added 'wince' to syscmd_ver() supported platforms
00080 #    0.2.1 - added cache logic and changed the platform string format
00081 #    0.2.0 - changed the API to use functions instead of module globals
00082 #            since some action take too long to be run on module import
00083 #    0.1.0 - first release
00084 #
00085 #    You can always get the latest version of this module at:
00086 #
00087 #             http://www.egenix.com/files/python/platform.py
00088 #
00089 #    If that URL should fail, try contacting the author.
00090 
00091 __copyright__ = """
00092     Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
00093     Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
00094 
00095     Permission to use, copy, modify, and distribute this software and its
00096     documentation for any purpose and without fee or royalty is hereby granted,
00097     provided that the above copyright notice appear in all copies and that
00098     both that copyright notice and this permission notice appear in
00099     supporting documentation or portions thereof, including modifications,
00100     that you make.
00101 
00102     EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
00103     THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
00104     FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
00105     INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
00106     FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
00107     NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
00108     WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
00109 
00110 """
00111 
00112 __version__ = '1.0.7'
00113 
00114 import sys, os, re
00115 
00116 ### Globals & Constants
00117 
00118 # Determine the platform's /dev/null device
00119 try:
00120     DEV_NULL = os.devnull
00121 except AttributeError:
00122     # os.devnull was added in Python 2.4, so emulate it for earlier
00123     # Python versions
00124     if sys.platform in ('dos','win32','win16','os2'):
00125         # Use the old CP/M NUL as device name
00126         DEV_NULL = 'NUL'
00127     else:
00128         # Standard Unix uses /dev/null
00129         DEV_NULL = '/dev/null'
00130 
00131 ### Platform specific APIs
00132 
00133 _libc_search = re.compile(r'(__libc_init)'
00134                           '|'
00135                           '(GLIBC_([0-9.]+))'
00136                           '|'
00137                           '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
00138 
00139 def libc_ver(executable=sys.executable,lib='',version='',
00140 
00141              chunksize=2048):
00142 
00143     """ Tries to determine the libc version that the file executable
00144         (which defaults to the Python interpreter) is linked against.
00145 
00146         Returns a tuple of strings (lib,version) which default to the
00147         given parameters in case the lookup fails.
00148 
00149         Note that the function has intimate knowledge of how different
00150         libc versions add symbols to the executable and thus is probably
00151         only useable for executables compiled using gcc.
00152 
00153         The file is read and scanned in chunks of chunksize bytes.
00154 
00155     """
00156     if hasattr(os.path, 'realpath'):
00157         # Python 2.2 introduced os.path.realpath(); it is used
00158         # here to work around problems with Cygwin not being
00159         # able to open symlinks for reading
00160         executable = os.path.realpath(executable)
00161     f = open(executable,'rb')
00162     binary = f.read(chunksize).decode('latin-1')
00163     pos = 0
00164     while 1:
00165         m = _libc_search.search(binary,pos)
00166         if not m:
00167             binary = f.read(chunksize).decode('latin-1')
00168             if not binary:
00169                 break
00170             pos = 0
00171             continue
00172         libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
00173         if libcinit and not lib:
00174             lib = 'libc'
00175         elif glibc:
00176             if lib != 'glibc':
00177                 lib = 'glibc'
00178                 version = glibcversion
00179             elif glibcversion > version:
00180                 version = glibcversion
00181         elif so:
00182             if lib != 'glibc':
00183                 lib = 'libc'
00184                 if soversion > version:
00185                     version = soversion
00186                 if threads and version[-len(threads):] != threads:
00187                     version = version + threads
00188         pos = m.end()
00189     f.close()
00190     return lib,version
00191 
00192 def _dist_try_harder(distname,version,id):
00193 
00194     """ Tries some special tricks to get the distribution
00195         information in case the default method fails.
00196 
00197         Currently supports older SuSE Linux, Caldera OpenLinux and
00198         Slackware Linux distributions.
00199 
00200     """
00201     if os.path.exists('/var/adm/inst-log/info'):
00202         # SuSE Linux stores distribution information in that file
00203         distname = 'SuSE'
00204         for line in open('/var/adm/inst-log/info'):
00205             tv = line.split()
00206             if len(tv) == 2:
00207                 tag,value = tv
00208             else:
00209                 continue
00210             if tag == 'MIN_DIST_VERSION':
00211                 version = value.strip()
00212             elif tag == 'DIST_IDENT':
00213                 values = value.split('-')
00214                 id = values[2]
00215         return distname,version,id
00216 
00217     if os.path.exists('/etc/.installed'):
00218         # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
00219         for line in open('/etc/.installed'):
00220             pkg = line.split('-')
00221             if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
00222                 # XXX does Caldera support non Intel platforms ? If yes,
00223                 #     where can we find the needed id ?
00224                 return 'OpenLinux',pkg[1],id
00225 
00226     if os.path.isdir('/usr/lib/setup'):
00227         # Check for slackware verson tag file (thanks to Greg Andruk)
00228         verfiles = os.listdir('/usr/lib/setup')
00229         for n in range(len(verfiles)-1, -1, -1):
00230             if verfiles[n][:14] != 'slack-version-':
00231                 del verfiles[n]
00232         if verfiles:
00233             verfiles.sort()
00234             distname = 'slackware'
00235             version = verfiles[-1][14:]
00236             return distname,version,id
00237 
00238     return distname,version,id
00239 
00240 _release_filename = re.compile(r'(\w+)[-_](release|version)', re.ASCII)
00241 _lsb_release_version = re.compile(r'(.+)'
00242                                    ' release '
00243                                    '([\d.]+)'
00244                                    '[^(]*(?:\((.+)\))?', re.ASCII)
00245 _release_version = re.compile(r'([^0-9]+)'
00246                                '(?: release )?'
00247                                '([\d.]+)'
00248                                '[^(]*(?:\((.+)\))?', re.ASCII)
00249 
00250 # See also http://www.novell.com/coolsolutions/feature/11251.html
00251 # and http://linuxmafia.com/faq/Admin/release-files.html
00252 # and http://data.linux-ntfs.org/rpm/whichrpm
00253 # and http://www.die.net/doc/linux/man/man1/lsb_release.1.html
00254 
00255 _supported_dists = (
00256     'SuSE', 'debian', 'fedora', 'redhat', 'centos',
00257     'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',
00258     'UnitedLinux', 'turbolinux')
00259 
00260 def _parse_release_file(firstline):
00261 
00262     # Default to empty 'version' and 'id' strings.  Both defaults are used
00263     # when 'firstline' is empty.  'id' defaults to empty when an id can not
00264     # be deduced.
00265     version = ''
00266     id = ''
00267 
00268     # Parse the first line
00269     m = _lsb_release_version.match(firstline)
00270     if m is not None:
00271         # LSB format: "distro release x.x (codename)"
00272         return tuple(m.groups())
00273 
00274     # Pre-LSB format: "distro x.x (codename)"
00275     m = _release_version.match(firstline)
00276     if m is not None:
00277         return tuple(m.groups())
00278 
00279     # Unkown format... take the first two words
00280     l = firstline.strip().split()
00281     if l:
00282         version = l[0]
00283         if len(l) > 1:
00284             id = l[1]
00285     return '', version, id
00286 
00287 def linux_distribution(distname='', version='', id='',
00288 
00289                        supported_dists=_supported_dists,
00290                        full_distribution_name=1):
00291 
00292     """ Tries to determine the name of the Linux OS distribution name.
00293 
00294         The function first looks for a distribution release file in
00295         /etc and then reverts to _dist_try_harder() in case no
00296         suitable files are found.
00297 
00298         supported_dists may be given to define the set of Linux
00299         distributions to look for. It defaults to a list of currently
00300         supported Linux distributions identified by their release file
00301         name.
00302 
00303         If full_distribution_name is true (default), the full
00304         distribution read from the OS is returned. Otherwise the short
00305         name taken from supported_dists is used.
00306 
00307         Returns a tuple (distname,version,id) which default to the
00308         args given as parameters.
00309 
00310     """
00311     try:
00312         etc = os.listdir('/etc')
00313     except os.error:
00314         # Probably not a Unix system
00315         return distname,version,id
00316     etc.sort()
00317     for file in etc:
00318         m = _release_filename.match(file)
00319         if m is not None:
00320             _distname,dummy = m.groups()
00321             if _distname in supported_dists:
00322                 distname = _distname
00323                 break
00324     else:
00325         return _dist_try_harder(distname,version,id)
00326 
00327     # Read the first line
00328     with open('/etc/'+file, 'r') as f:
00329         firstline = f.readline()
00330     _distname, _version, _id = _parse_release_file(firstline)
00331 
00332     if _distname and full_distribution_name:
00333         distname = _distname
00334     if _version:
00335         version = _version
00336     if _id:
00337         id = _id
00338     return distname, version, id
00339 
00340 # To maintain backwards compatibility:
00341 
00342 def dist(distname='',version='',id='',
00343 
00344          supported_dists=_supported_dists):
00345 
00346     """ Tries to determine the name of the Linux OS distribution name.
00347 
00348         The function first looks for a distribution release file in
00349         /etc and then reverts to _dist_try_harder() in case no
00350         suitable files are found.
00351 
00352         Returns a tuple (distname,version,id) which default to the
00353         args given as parameters.
00354 
00355     """
00356     return linux_distribution(distname, version, id,
00357                               supported_dists=supported_dists,
00358                               full_distribution_name=0)
00359 
00360 class _popen:
00361 
00362     """ Fairly portable (alternative) popen implementation.
00363 
00364         This is mostly needed in case os.popen() is not available, or
00365         doesn't work as advertised, e.g. in Win9X GUI programs like
00366         PythonWin or IDLE.
00367 
00368         Writing to the pipe is currently not supported.
00369 
00370     """
00371     tmpfile = ''
00372     pipe = None
00373     bufsize = None
00374     mode = 'r'
00375 
00376     def __init__(self,cmd,mode='r',bufsize=None):
00377 
00378         if mode != 'r':
00379             raise ValueError('popen()-emulation only supports read mode')
00380         import tempfile
00381         self.tmpfile = tmpfile = tempfile.mktemp()
00382         os.system(cmd + ' > %s' % tmpfile)
00383         self.pipe = open(tmpfile,'rb')
00384         self.bufsize = bufsize
00385         self.mode = mode
00386 
00387     def read(self):
00388 
00389         return self.pipe.read()
00390 
00391     def readlines(self):
00392 
00393         if self.bufsize is not None:
00394             return self.pipe.readlines()
00395 
00396     def close(self,
00397 
00398               remove=os.unlink,error=os.error):
00399 
00400         if self.pipe:
00401             rc = self.pipe.close()
00402         else:
00403             rc = 255
00404         if self.tmpfile:
00405             try:
00406                 remove(self.tmpfile)
00407             except error:
00408                 pass
00409         return rc
00410 
00411     # Alias
00412     __del__ = close
00413 
00414 def popen(cmd, mode='r', bufsize=-1):
00415 
00416     """ Portable popen() interface.
00417     """
00418     # Find a working popen implementation preferring win32pipe.popen
00419     # over os.popen over _popen
00420     popen = None
00421     if os.environ.get('OS','') == 'Windows_NT':
00422         # On NT win32pipe should work; on Win9x it hangs due to bugs
00423         # in the MS C lib (see MS KnowledgeBase article Q150956)
00424         try:
00425             import win32pipe
00426         except ImportError:
00427             pass
00428         else:
00429             popen = win32pipe.popen
00430     if popen is None:
00431         if hasattr(os,'popen'):
00432             popen = os.popen
00433             # Check whether it works... it doesn't in GUI programs
00434             # on Windows platforms
00435             if sys.platform == 'win32': # XXX Others too ?
00436                 try:
00437                     popen('')
00438                 except os.error:
00439                     popen = _popen
00440         else:
00441             popen = _popen
00442     if bufsize is None:
00443         return popen(cmd,mode)
00444     else:
00445         return popen(cmd,mode,bufsize)
00446 
00447 def _norm_version(version, build=''):
00448 
00449     """ Normalize the version and build strings and return a single
00450         version string using the format major.minor.build (or patchlevel).
00451     """
00452     l = version.split('.')
00453     if build:
00454         l.append(build)
00455     try:
00456         ints = map(int,l)
00457     except ValueError:
00458         strings = l
00459     else:
00460         strings = list(map(str,ints))
00461     version = '.'.join(strings[:3])
00462     return version
00463 
00464 _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
00465                          '.*'
00466                          '\[.* ([\d.]+)\])')
00467 
00468 # Examples of VER command output:
00469 #
00470 #   Windows 2000:  Microsoft Windows 2000 [Version 5.00.2195]
00471 #   Windows XP:    Microsoft Windows XP [Version 5.1.2600]
00472 #   Windows Vista: Microsoft Windows [Version 6.0.6002]
00473 #
00474 # Note that the "Version" string gets localized on different
00475 # Windows versions.
00476 
00477 def _syscmd_ver(system='', release='', version='',
00478 
00479                supported_platforms=('win32','win16','dos','os2')):
00480 
00481     """ Tries to figure out the OS version used and returns
00482         a tuple (system,release,version).
00483 
00484         It uses the "ver" shell command for this which is known
00485         to exists on Windows, DOS and OS/2. XXX Others too ?
00486 
00487         In case this fails, the given parameters are used as
00488         defaults.
00489 
00490     """
00491     if sys.platform not in supported_platforms:
00492         return system,release,version
00493 
00494     # Try some common cmd strings
00495     for cmd in ('ver','command /c ver','cmd /c ver'):
00496         try:
00497             pipe = popen(cmd)
00498             info = pipe.read()
00499             if pipe.close():
00500                 raise os.error('command failed')
00501             # XXX How can I suppress shell errors from being written
00502             #     to stderr ?
00503         except os.error as why:
00504             #print 'Command %s failed: %s' % (cmd,why)
00505             continue
00506         except IOError as why:
00507             #print 'Command %s failed: %s' % (cmd,why)
00508             continue
00509         else:
00510             break
00511     else:
00512         return system,release,version
00513 
00514     # Parse the output
00515     info = info.strip()
00516     m = _ver_output.match(info)
00517     if m is not None:
00518         system,release,version = m.groups()
00519         # Strip trailing dots from version and release
00520         if release[-1] == '.':
00521             release = release[:-1]
00522         if version[-1] == '.':
00523             version = version[:-1]
00524         # Normalize the version and build strings (eliminating additional
00525         # zeros)
00526         version = _norm_version(version)
00527     return system,release,version
00528 
00529 def _win32_getvalue(key,name,default=''):
00530 
00531     """ Read a value for name from the registry key.
00532 
00533         In case this fails, default is returned.
00534 
00535     """
00536     try:
00537         # Use win32api if available
00538         from win32api import RegQueryValueEx
00539     except ImportError:
00540         # On Python 2.0 and later, emulate using winreg
00541         import winreg
00542         RegQueryValueEx = winreg.QueryValueEx
00543     try:
00544         return RegQueryValueEx(key,name)
00545     except:
00546         return default
00547 
00548 def win32_ver(release='',version='',csd='',ptype=''):
00549 
00550     """ Get additional version information from the Windows Registry
00551         and return a tuple (version,csd,ptype) referring to version
00552         number, CSD level and OS type (multi/single
00553         processor).
00554 
00555         As a hint: ptype returns 'Uniprocessor Free' on single
00556         processor NT machines and 'Multiprocessor Free' on multi
00557         processor machines. The 'Free' refers to the OS version being
00558         free of debugging code. It could also state 'Checked' which
00559         means the OS version uses debugging code, i.e. code that
00560         checks arguments, ranges, etc. (Thomas Heller).
00561 
00562         Note: this function works best with Mark Hammond's win32
00563         package installed, but also on Python 2.3 and later. It
00564         obviously only runs on Win32 compatible platforms.
00565 
00566     """
00567     # XXX Is there any way to find out the processor type on WinXX ?
00568     # XXX Is win32 available on Windows CE ?
00569     #
00570     # Adapted from code posted by Karl Putland to comp.lang.python.
00571     #
00572     # The mappings between reg. values and release names can be found
00573     # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
00574 
00575     # Import the needed APIs
00576     try:
00577         import win32api
00578         from win32api import RegQueryValueEx, RegOpenKeyEx, \
00579              RegCloseKey, GetVersionEx
00580         from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
00581              VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
00582     except ImportError:
00583         # Emulate the win32api module using Python APIs
00584         try:
00585             sys.getwindowsversion
00586         except AttributeError:
00587             # No emulation possible, so return the defaults...
00588             return release,version,csd,ptype
00589         else:
00590             # Emulation using winreg (added in Python 2.0) and
00591             # sys.getwindowsversion() (added in Python 2.3)
00592             import winreg
00593             GetVersionEx = sys.getwindowsversion
00594             RegQueryValueEx = winreg.QueryValueEx
00595             RegOpenKeyEx = winreg.OpenKeyEx
00596             RegCloseKey = winreg.CloseKey
00597             HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE
00598             VER_PLATFORM_WIN32_WINDOWS = 1
00599             VER_PLATFORM_WIN32_NT = 2
00600             VER_NT_WORKSTATION = 1
00601             VER_NT_SERVER = 3
00602             REG_SZ = 1
00603 
00604     # Find out the registry key and some general version infos
00605     winver = GetVersionEx()
00606     maj,min,buildno,plat,csd = winver
00607     version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
00608     if hasattr(winver, "service_pack"):
00609         if winver.service_pack != "":
00610             csd = 'SP%s' % winver.service_pack_major
00611     else:
00612         if csd[:13] == 'Service Pack ':
00613             csd = 'SP' + csd[13:]
00614 
00615     if plat == VER_PLATFORM_WIN32_WINDOWS:
00616         regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
00617         # Try to guess the release name
00618         if maj == 4:
00619             if min == 0:
00620                 release = '95'
00621             elif min == 10:
00622                 release = '98'
00623             elif min == 90:
00624                 release = 'Me'
00625             else:
00626                 release = 'postMe'
00627         elif maj == 5:
00628             release = '2000'
00629 
00630     elif plat == VER_PLATFORM_WIN32_NT:
00631         regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
00632         if maj <= 4:
00633             release = 'NT'
00634         elif maj == 5:
00635             if min == 0:
00636                 release = '2000'
00637             elif min == 1:
00638                 release = 'XP'
00639             elif min == 2:
00640                 release = '2003Server'
00641             else:
00642                 release = 'post2003'
00643         elif maj == 6:
00644             if hasattr(winver, "product_type"):
00645                 product_type = winver.product_type
00646             else:
00647                 product_type = VER_NT_WORKSTATION
00648                 # Without an OSVERSIONINFOEX capable sys.getwindowsversion(),
00649                 # or help from the registry, we cannot properly identify
00650                 # non-workstation versions.
00651                 try:
00652                     key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
00653                     name, type = RegQueryValueEx(key, "ProductName")
00654                     # Discard any type that isn't REG_SZ
00655                     if type == REG_SZ and name.find("Server") != -1:
00656                         product_type = VER_NT_SERVER
00657                 except WindowsError:
00658                     # Use default of VER_NT_WORKSTATION
00659                     pass
00660 
00661             if min == 0:
00662                 if product_type == VER_NT_WORKSTATION:
00663                     release = 'Vista'
00664                 else:
00665                     release = '2008Server'
00666             elif min == 1:
00667                 if product_type == VER_NT_WORKSTATION:
00668                     release = '7'
00669                 else:
00670                     release = '2008ServerR2'
00671             else:
00672                 release = 'post2008Server'
00673 
00674     else:
00675         if not release:
00676             # E.g. Win3.1 with win32s
00677             release = '%i.%i' % (maj,min)
00678         return release,version,csd,ptype
00679 
00680     # Open the registry key
00681     try:
00682         keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
00683         # Get a value to make sure the key exists...
00684         RegQueryValueEx(keyCurVer, 'SystemRoot')
00685     except:
00686         return release,version,csd,ptype
00687 
00688     # Parse values
00689     #subversion = _win32_getvalue(keyCurVer,
00690     #                            'SubVersionNumber',
00691     #                            ('',1))[0]
00692     #if subversion:
00693     #   release = release + subversion # 95a, 95b, etc.
00694     build = _win32_getvalue(keyCurVer,
00695                             'CurrentBuildNumber',
00696                             ('',1))[0]
00697     ptype = _win32_getvalue(keyCurVer,
00698                            'CurrentType',
00699                            (ptype,1))[0]
00700 
00701     # Normalize version
00702     version = _norm_version(version,build)
00703 
00704     # Close key
00705     RegCloseKey(keyCurVer)
00706     return release,version,csd,ptype
00707 
00708 def _mac_ver_lookup(selectors,default=None):
00709 
00710     from _gestalt import gestalt
00711     l = []
00712     append = l.append
00713     for selector in selectors:
00714         try:
00715             append(gestalt(selector))
00716         except (RuntimeError, OSError):
00717             append(default)
00718     return l
00719 
00720 def _bcd2str(bcd):
00721 
00722     return hex(bcd)[2:]
00723 
00724 def _mac_ver_gestalt():
00725     """
00726         Thanks to Mark R. Levinson for mailing documentation links and
00727         code examples for this function. Documentation for the
00728         gestalt() API is available online at:
00729 
00730            http://www.rgaros.nl/gestalt/
00731     """
00732     # Check whether the version info module is available
00733     try:
00734         import _gestalt
00735     except ImportError:
00736         return None
00737     # Get the infos
00738     sysv, sysa = _mac_ver_lookup(('sysv','sysa'))
00739     # Decode the infos
00740     if sysv:
00741         major = (sysv & 0xFF00) >> 8
00742         minor = (sysv & 0x00F0) >> 4
00743         patch = (sysv & 0x000F)
00744 
00745         if (major, minor) >= (10, 4):
00746             # the 'sysv' gestald cannot return patchlevels
00747             # higher than 9. Apple introduced 3 new
00748             # gestalt codes in 10.4 to deal with this
00749             # issue (needed because patch levels can
00750             # run higher than 9, such as 10.4.11)
00751             major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
00752             release = '%i.%i.%i' %(major, minor, patch)
00753         else:
00754             release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
00755 
00756     if sysa:
00757         machine = {0x1: '68k',
00758                    0x2: 'PowerPC',
00759                    0xa: 'i386'}.get(sysa,'')
00760 
00761     versioninfo=('', '', '')
00762     return release,versioninfo,machine
00763 
00764 def _mac_ver_xml():
00765     fn = '/System/Library/CoreServices/SystemVersion.plist'
00766     if not os.path.exists(fn):
00767         return None
00768 
00769     try:
00770         import plistlib
00771     except ImportError:
00772         return None
00773 
00774     pl = plistlib.readPlist(fn)
00775     release = pl['ProductVersion']
00776     versioninfo=('', '', '')
00777     machine = os.uname()[4]
00778     if machine in ('ppc', 'Power Macintosh'):
00779         # for compatibility with the gestalt based code
00780         machine = 'PowerPC'
00781 
00782     return release,versioninfo,machine
00783 
00784 
00785 def mac_ver(release='',versioninfo=('','',''),machine=''):
00786 
00787     """ Get MacOS version information and return it as tuple (release,
00788         versioninfo, machine) with versioninfo being a tuple (version,
00789         dev_stage, non_release_version).
00790 
00791         Entries which cannot be determined are set to the paramter values
00792         which default to ''. All tuple entries are strings.
00793     """
00794 
00795     # First try reading the information from an XML file which should
00796     # always be present
00797     info = _mac_ver_xml()
00798     if info is not None:
00799         return info
00800 
00801     # If that doesn't work for some reason fall back to reading the
00802     # information using gestalt calls.
00803     info = _mac_ver_gestalt()
00804     if info is not None:
00805         return info
00806 
00807     # If that also doesn't work return the default values
00808     return release,versioninfo,machine
00809 
00810 def _java_getprop(name,default):
00811 
00812     from java.lang import System
00813     try:
00814         value = System.getProperty(name)
00815         if value is None:
00816             return default
00817         return value
00818     except AttributeError:
00819         return default
00820 
00821 def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
00822 
00823     """ Version interface for Jython.
00824 
00825         Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
00826         a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
00827         tuple (os_name,os_version,os_arch).
00828 
00829         Values which cannot be determined are set to the defaults
00830         given as parameters (which all default to '').
00831 
00832     """
00833     # Import the needed APIs
00834     try:
00835         import java.lang
00836     except ImportError:
00837         return release,vendor,vminfo,osinfo
00838 
00839     vendor = _java_getprop('java.vendor', vendor)
00840     release = _java_getprop('java.version', release)
00841     vm_name, vm_release, vm_vendor = vminfo
00842     vm_name = _java_getprop('java.vm.name', vm_name)
00843     vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
00844     vm_release = _java_getprop('java.vm.version', vm_release)
00845     vminfo = vm_name, vm_release, vm_vendor
00846     os_name, os_version, os_arch = osinfo
00847     os_arch = _java_getprop('java.os.arch', os_arch)
00848     os_name = _java_getprop('java.os.name', os_name)
00849     os_version = _java_getprop('java.os.version', os_version)
00850     osinfo = os_name, os_version, os_arch
00851 
00852     return release, vendor, vminfo, osinfo
00853 
00854 ### System name aliasing
00855 
00856 def system_alias(system,release,version):
00857 
00858     """ Returns (system,release,version) aliased to common
00859         marketing names used for some systems.
00860 
00861         It also does some reordering of the information in some cases
00862         where it would otherwise cause confusion.
00863 
00864     """
00865     if system == 'Rhapsody':
00866         # Apple's BSD derivative
00867         # XXX How can we determine the marketing release number ?
00868         return 'MacOS X Server',system+release,version
00869 
00870     elif system == 'SunOS':
00871         # Sun's OS
00872         if release < '5':
00873             # These releases use the old name SunOS
00874             return system,release,version
00875         # Modify release (marketing release = SunOS release - 3)
00876         l = release.split('.')
00877         if l:
00878             try:
00879                 major = int(l[0])
00880             except ValueError:
00881                 pass
00882             else:
00883                 major = major - 3
00884                 l[0] = str(major)
00885                 release = '.'.join(l)
00886         if release < '6':
00887             system = 'Solaris'
00888         else:
00889             # XXX Whatever the new SunOS marketing name is...
00890             system = 'Solaris'
00891 
00892     elif system == 'IRIX64':
00893         # IRIX reports IRIX64 on platforms with 64-bit support; yet it
00894         # is really a version and not a different platform, since 32-bit
00895         # apps are also supported..
00896         system = 'IRIX'
00897         if version:
00898             version = version + ' (64bit)'
00899         else:
00900             version = '64bit'
00901 
00902     elif system in ('win32','win16'):
00903         # In case one of the other tricks
00904         system = 'Windows'
00905 
00906     return system,release,version
00907 
00908 ### Various internal helpers
00909 
00910 def _platform(*args):
00911 
00912     """ Helper to format the platform string in a filename
00913         compatible format e.g. "system-version-machine".
00914     """
00915     # Format the platform string
00916     platform = '-'.join(x.strip() for x in filter(len, args))
00917 
00918     # Cleanup some possible filename obstacles...
00919     platform = platform.replace(' ','_')
00920     platform = platform.replace('/','-')
00921     platform = platform.replace('\\','-')
00922     platform = platform.replace(':','-')
00923     platform = platform.replace(';','-')
00924     platform = platform.replace('"','-')
00925     platform = platform.replace('(','-')
00926     platform = platform.replace(')','-')
00927 
00928     # No need to report 'unknown' information...
00929     platform = platform.replace('unknown','')
00930 
00931     # Fold '--'s and remove trailing '-'
00932     while 1:
00933         cleaned = platform.replace('--','-')
00934         if cleaned == platform:
00935             break
00936         platform = cleaned
00937     while platform[-1] == '-':
00938         platform = platform[:-1]
00939 
00940     return platform
00941 
00942 def _node(default=''):
00943 
00944     """ Helper to determine the node name of this machine.
00945     """
00946     try:
00947         import socket
00948     except ImportError:
00949         # No sockets...
00950         return default
00951     try:
00952         return socket.gethostname()
00953     except socket.error:
00954         # Still not working...
00955         return default
00956 
00957 def _follow_symlinks(filepath):
00958 
00959     """ In case filepath is a symlink, follow it until a
00960         real file is reached.
00961     """
00962     filepath = os.path.abspath(filepath)
00963     while os.path.islink(filepath):
00964         filepath = os.path.normpath(
00965             os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
00966     return filepath
00967 
00968 def _syscmd_uname(option,default=''):
00969 
00970     """ Interface to the system's uname command.
00971     """
00972     if sys.platform in ('dos','win32','win16','os2'):
00973         # XXX Others too ?
00974         return default
00975     try:
00976         f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
00977     except (AttributeError,os.error):
00978         return default
00979     output = f.read().strip()
00980     rc = f.close()
00981     if not output or rc:
00982         return default
00983     else:
00984         return output
00985 
00986 def _syscmd_file(target,default=''):
00987 
00988     """ Interface to the system's file command.
00989 
00990         The function uses the -b option of the file command to have it
00991         omit the filename in its output. Follow the symlinks. It returns
00992         default in case the command should fail.
00993 
00994     """
00995     if sys.platform in ('dos','win32','win16','os2'):
00996         # XXX Others too ?
00997         return default
00998     target = _follow_symlinks(target).replace('"', '\\"')
00999     try:
01000         f = os.popen('file -b "%s" 2> %s' % (target, DEV_NULL))
01001     except (AttributeError,os.error):
01002         return default
01003     output = f.read().strip()
01004     rc = f.close()
01005     if not output or rc:
01006         return default
01007     else:
01008         return output
01009 
01010 ### Information about the used architecture
01011 
01012 # Default values for architecture; non-empty strings override the
01013 # defaults given as parameters
01014 _default_architecture = {
01015     'win32': ('','WindowsPE'),
01016     'win16': ('','Windows'),
01017     'dos': ('','MSDOS'),
01018 }
01019 
01020 def architecture(executable=sys.executable,bits='',linkage=''):
01021 
01022     """ Queries the given executable (defaults to the Python interpreter
01023         binary) for various architecture information.
01024 
01025         Returns a tuple (bits,linkage) which contains information about
01026         the bit architecture and the linkage format used for the
01027         executable. Both values are returned as strings.
01028 
01029         Values that cannot be determined are returned as given by the
01030         parameter presets. If bits is given as '', the sizeof(pointer)
01031         (or sizeof(long) on Python version < 1.5.2) is used as
01032         indicator for the supported pointer size.
01033 
01034         The function relies on the system's "file" command to do the
01035         actual work. This is available on most if not all Unix
01036         platforms. On some non-Unix platforms where the "file" command
01037         does not exist and the executable is set to the Python interpreter
01038         binary defaults from _default_architecture are used.
01039 
01040     """
01041     # Use the sizeof(pointer) as default number of bits if nothing
01042     # else is given as default.
01043     if not bits:
01044         import struct
01045         try:
01046             size = struct.calcsize('P')
01047         except struct.error:
01048             # Older installations can only query longs
01049             size = struct.calcsize('l')
01050         bits = str(size*8) + 'bit'
01051 
01052     # Get data from the 'file' system command
01053     if executable:
01054         fileout = _syscmd_file(executable, '')
01055     else:
01056         fileout = ''
01057 
01058     if not fileout and \
01059        executable == sys.executable:
01060         # "file" command did not return anything; we'll try to provide
01061         # some sensible defaults then...
01062         if sys.platform in _default_architecture:
01063             b,l = _default_architecture[sys.platform]
01064             if b:
01065                 bits = b
01066             if l:
01067                 linkage = l
01068         return bits,linkage
01069 
01070     if 'executable' not in fileout:
01071         # Format not supported
01072         return bits,linkage
01073 
01074     # Bits
01075     if '32-bit' in fileout:
01076         bits = '32bit'
01077     elif 'N32' in fileout:
01078         # On Irix only
01079         bits = 'n32bit'
01080     elif '64-bit' in fileout:
01081         bits = '64bit'
01082 
01083     # Linkage
01084     if 'ELF' in fileout:
01085         linkage = 'ELF'
01086     elif 'PE' in fileout:
01087         # E.g. Windows uses this format
01088         if 'Windows' in fileout:
01089             linkage = 'WindowsPE'
01090         else:
01091             linkage = 'PE'
01092     elif 'COFF' in fileout:
01093         linkage = 'COFF'
01094     elif 'MS-DOS' in fileout:
01095         linkage = 'MSDOS'
01096     else:
01097         # XXX the A.OUT format also falls under this class...
01098         pass
01099 
01100     return bits,linkage
01101 
01102 ### Portable uname() interface
01103 
01104 _uname_cache = None
01105 
01106 def uname():
01107 
01108     """ Fairly portable uname interface. Returns a tuple
01109         of strings (system,node,release,version,machine,processor)
01110         identifying the underlying platform.
01111 
01112         Note that unlike the os.uname function this also returns
01113         possible processor information as an additional tuple entry.
01114 
01115         Entries which cannot be determined are set to ''.
01116 
01117     """
01118     global _uname_cache
01119     no_os_uname = 0
01120 
01121     if _uname_cache is not None:
01122         return _uname_cache
01123 
01124     processor = ''
01125 
01126     # Get some infos from the builtin os.uname API...
01127     try:
01128         system,node,release,version,machine = os.uname()
01129     except AttributeError:
01130         no_os_uname = 1
01131 
01132     if no_os_uname or not list(filter(None, (system, node, release, version, machine))):
01133         # Hmm, no there is either no uname or uname has returned
01134         #'unknowns'... we'll have to poke around the system then.
01135         if no_os_uname:
01136             system = sys.platform
01137             release = ''
01138             version = ''
01139             node = _node()
01140             machine = ''
01141 
01142         use_syscmd_ver = 1
01143 
01144         # Try win32_ver() on win32 platforms
01145         if system == 'win32':
01146             release,version,csd,ptype = win32_ver()
01147             if release and version:
01148                 use_syscmd_ver = 0
01149             # Try to use the PROCESSOR_* environment variables
01150             # available on Win XP and later; see
01151             # http://support.microsoft.com/kb/888731 and
01152             # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
01153             if not machine:
01154                 # WOW64 processes mask the native architecture
01155                 if "PROCESSOR_ARCHITEW6432" in os.environ:
01156                     machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
01157                 else:
01158                     machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
01159             if not processor:
01160                 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
01161 
01162         # Try the 'ver' system command available on some
01163         # platforms
01164         if use_syscmd_ver:
01165             system,release,version = _syscmd_ver(system)
01166             # Normalize system to what win32_ver() normally returns
01167             # (_syscmd_ver() tends to return the vendor name as well)
01168             if system == 'Microsoft Windows':
01169                 system = 'Windows'
01170             elif system == 'Microsoft' and release == 'Windows':
01171                 # Under Windows Vista and Windows Server 2008,
01172                 # Microsoft changed the output of the ver command. The
01173                 # release is no longer printed.  This causes the
01174                 # system and release to be misidentified.
01175                 system = 'Windows'
01176                 if '6.0' == version[:3]:
01177                     release = 'Vista'
01178                 else:
01179                     release = ''
01180 
01181         # In case we still don't know anything useful, we'll try to
01182         # help ourselves
01183         if system in ('win32','win16'):
01184             if not version:
01185                 if system == 'win32':
01186                     version = '32bit'
01187                 else:
01188                     version = '16bit'
01189             system = 'Windows'
01190 
01191         elif system[:4] == 'java':
01192             release,vendor,vminfo,osinfo = java_ver()
01193             system = 'Java'
01194             version = ', '.join(vminfo)
01195             if not version:
01196                 version = vendor
01197 
01198     # System specific extensions
01199     if system == 'OpenVMS':
01200         # OpenVMS seems to have release and version mixed up
01201         if not release or release == '0':
01202             release = version
01203             version = ''
01204         # Get processor information
01205         try:
01206             import vms_lib
01207         except ImportError:
01208             pass
01209         else:
01210             csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
01211             if (cpu_number >= 128):
01212                 processor = 'Alpha'
01213             else:
01214                 processor = 'VAX'
01215     if not processor:
01216         # Get processor information from the uname system command
01217         processor = _syscmd_uname('-p','')
01218 
01219     #If any unknowns still exist, replace them with ''s, which are more portable
01220     if system == 'unknown':
01221         system = ''
01222     if node == 'unknown':
01223         node = ''
01224     if release == 'unknown':
01225         release = ''
01226     if version == 'unknown':
01227         version = ''
01228     if machine == 'unknown':
01229         machine = ''
01230     if processor == 'unknown':
01231         processor = ''
01232 
01233     #  normalize name
01234     if system == 'Microsoft' and release == 'Windows':
01235         system = 'Windows'
01236         release = 'Vista'
01237 
01238     _uname_cache = system,node,release,version,machine,processor
01239     return _uname_cache
01240 
01241 ### Direct interfaces to some of the uname() return values
01242 
01243 def system():
01244 
01245     """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
01246 
01247         An empty string is returned if the value cannot be determined.
01248 
01249     """
01250     return uname()[0]
01251 
01252 def node():
01253 
01254     """ Returns the computer's network name (which may not be fully
01255         qualified)
01256 
01257         An empty string is returned if the value cannot be determined.
01258 
01259     """
01260     return uname()[1]
01261 
01262 def release():
01263 
01264     """ Returns the system's release, e.g. '2.2.0' or 'NT'
01265 
01266         An empty string is returned if the value cannot be determined.
01267 
01268     """
01269     return uname()[2]
01270 
01271 def version():
01272 
01273     """ Returns the system's release version, e.g. '#3 on degas'
01274 
01275         An empty string is returned if the value cannot be determined.
01276 
01277     """
01278     return uname()[3]
01279 
01280 def machine():
01281 
01282     """ Returns the machine type, e.g. 'i386'
01283 
01284         An empty string is returned if the value cannot be determined.
01285 
01286     """
01287     return uname()[4]
01288 
01289 def processor():
01290 
01291     """ Returns the (true) processor name, e.g. 'amdk6'
01292 
01293         An empty string is returned if the value cannot be
01294         determined. Note that many platforms do not provide this
01295         information or simply return the same value as for machine(),
01296         e.g.  NetBSD does this.
01297 
01298     """
01299     return uname()[5]
01300 
01301 ### Various APIs for extracting information from sys.version
01302 
01303 _sys_version_parser = re.compile(
01304     r'([\w.+]+)\s*'
01305     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
01306     '\[([^\]]+)\]?', re.ASCII)
01307 
01308 _ironpython_sys_version_parser = re.compile(
01309     r'IronPython\s*'
01310     '([\d\.]+)'
01311     '(?: \(([\d\.]+)\))?'
01312     ' on (.NET [\d\.]+)', re.ASCII)
01313 
01314 _pypy_sys_version_parser = re.compile(
01315     r'([\w.+]+)\s*'
01316     '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
01317     '\[PyPy [^\]]+\]?')
01318 
01319 _sys_version_cache = {}
01320 
01321 def _sys_version(sys_version=None):
01322 
01323     """ Returns a parsed version of Python's sys.version as tuple
01324         (name, version, branch, revision, buildno, builddate, compiler)
01325         referring to the Python implementation name, version, branch,
01326         revision, build number, build date/time as string and the compiler
01327         identification string.
01328 
01329         Note that unlike the Python sys.version, the returned value
01330         for the Python version will always include the patchlevel (it
01331         defaults to '.0').
01332 
01333         The function returns empty strings for tuple entries that
01334         cannot be determined.
01335 
01336         sys_version may be given to parse an alternative version
01337         string, e.g. if the version was read from a different Python
01338         interpreter.
01339 
01340     """
01341     # Get the Python version
01342     if sys_version is None:
01343         sys_version = sys.version
01344 
01345     # Try the cache first
01346     result = _sys_version_cache.get(sys_version, None)
01347     if result is not None:
01348         return result
01349 
01350     # Parse it
01351     if sys_version[:10] == 'IronPython':
01352         # IronPython
01353         name = 'IronPython'
01354         match = _ironpython_sys_version_parser.match(sys_version)
01355         if match is None:
01356             raise ValueError(
01357                 'failed to parse IronPython sys.version: %s' %
01358                 repr(sys_version))
01359         version, alt_version, compiler = match.groups()
01360         buildno = ''
01361         builddate = ''
01362 
01363     elif sys.platform[:4] == 'java':
01364         # Jython
01365         name = 'Jython'
01366         match = _sys_version_parser.match(sys_version)
01367         if match is None:
01368             raise ValueError(
01369                 'failed to parse Jython sys.version: %s' %
01370                 repr(sys_version))
01371         version, buildno, builddate, buildtime, _ = match.groups()
01372         compiler = sys.platform
01373 
01374     elif "PyPy" in sys_version:
01375         # PyPy
01376         name = "PyPy"
01377         match = _pypy_sys_version_parser.match(sys_version)
01378         if match is None:
01379             raise ValueError("failed to parse PyPy sys.version: %s" %
01380                              repr(sys_version))
01381         version, buildno, builddate, buildtime = match.groups()
01382         compiler = ""
01383 
01384     else:
01385         # CPython
01386         match = _sys_version_parser.match(sys_version)
01387         if match is None:
01388             raise ValueError(
01389                 'failed to parse CPython sys.version: %s' %
01390                 repr(sys_version))
01391         version, buildno, builddate, buildtime, compiler = \
01392               match.groups()
01393         name = 'CPython'
01394         builddate = builddate + ' ' + buildtime
01395 
01396     if hasattr(sys, '_mercurial'):
01397         _, branch, revision = sys._mercurial
01398     elif hasattr(sys, 'subversion'):
01399         # sys.subversion was added in Python 2.5
01400         _, branch, revision = sys.subversion
01401     else:
01402         branch = ''
01403         revision = ''
01404 
01405     # Add the patchlevel version if missing
01406     l = version.split('.')
01407     if len(l) == 2:
01408         l.append('0')
01409         version = '.'.join(l)
01410 
01411     # Build and cache the result
01412     result = (name, version, branch, revision, buildno, builddate, compiler)
01413     _sys_version_cache[sys_version] = result
01414     return result
01415 
01416 def python_implementation():
01417 
01418     """ Returns a string identifying the Python implementation.
01419 
01420         Currently, the following implementations are identified:
01421           'CPython' (C implementation of Python),
01422           'IronPython' (.NET implementation of Python),
01423           'Jython' (Java implementation of Python),
01424           'PyPy' (Python implementation of Python).
01425 
01426     """
01427     return _sys_version()[0]
01428 
01429 def python_version():
01430 
01431     """ Returns the Python version as string 'major.minor.patchlevel'
01432 
01433         Note that unlike the Python sys.version, the returned value
01434         will always include the patchlevel (it defaults to 0).
01435 
01436     """
01437     return _sys_version()[1]
01438 
01439 def python_version_tuple():
01440 
01441     """ Returns the Python version as tuple (major, minor, patchlevel)
01442         of strings.
01443 
01444         Note that unlike the Python sys.version, the returned value
01445         will always include the patchlevel (it defaults to 0).
01446 
01447     """
01448     return tuple(_sys_version()[1].split('.'))
01449 
01450 def python_branch():
01451 
01452     """ Returns a string identifying the Python implementation
01453         branch.
01454 
01455         For CPython this is the Subversion branch from which the
01456         Python binary was built.
01457 
01458         If not available, an empty string is returned.
01459 
01460     """
01461 
01462     return _sys_version()[2]
01463 
01464 def python_revision():
01465 
01466     """ Returns a string identifying the Python implementation
01467         revision.
01468 
01469         For CPython this is the Subversion revision from which the
01470         Python binary was built.
01471 
01472         If not available, an empty string is returned.
01473 
01474     """
01475     return _sys_version()[3]
01476 
01477 def python_build():
01478 
01479     """ Returns a tuple (buildno, builddate) stating the Python
01480         build number and date as strings.
01481 
01482     """
01483     return _sys_version()[4:6]
01484 
01485 def python_compiler():
01486 
01487     """ Returns a string identifying the compiler used for compiling
01488         Python.
01489 
01490     """
01491     return _sys_version()[6]
01492 
01493 ### The Opus Magnum of platform strings :-)
01494 
01495 _platform_cache = {}
01496 
01497 def platform(aliased=0, terse=0):
01498 
01499     """ Returns a single string identifying the underlying platform
01500         with as much useful information as possible (but no more :).
01501 
01502         The output is intended to be human readable rather than
01503         machine parseable. It may look different on different
01504         platforms and this is intended.
01505 
01506         If "aliased" is true, the function will use aliases for
01507         various platforms that report system names which differ from
01508         their common names, e.g. SunOS will be reported as
01509         Solaris. The system_alias() function is used to implement
01510         this.
01511 
01512         Setting terse to true causes the function to return only the
01513         absolute minimum information needed to identify the platform.
01514 
01515     """
01516     result = _platform_cache.get((aliased, terse), None)
01517     if result is not None:
01518         return result
01519 
01520     # Get uname information and then apply platform specific cosmetics
01521     # to it...
01522     system,node,release,version,machine,processor = uname()
01523     if machine == processor:
01524         processor = ''
01525     if aliased:
01526         system,release,version = system_alias(system,release,version)
01527 
01528     if system == 'Windows':
01529         # MS platforms
01530         rel,vers,csd,ptype = win32_ver(version)
01531         if terse:
01532             platform = _platform(system,release)
01533         else:
01534             platform = _platform(system,release,version,csd)
01535 
01536     elif system in ('Linux',):
01537         # Linux based systems
01538         distname,distversion,distid = dist('')
01539         if distname and not terse:
01540             platform = _platform(system,release,machine,processor,
01541                                  'with',
01542                                  distname,distversion,distid)
01543         else:
01544             # If the distribution name is unknown check for libc vs. glibc
01545             libcname,libcversion = libc_ver(sys.executable)
01546             platform = _platform(system,release,machine,processor,
01547                                  'with',
01548                                  libcname+libcversion)
01549     elif system == 'Java':
01550         # Java platforms
01551         r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
01552         if terse or not os_name:
01553             platform = _platform(system,release,version)
01554         else:
01555             platform = _platform(system,release,version,
01556                                  'on',
01557                                  os_name,os_version,os_arch)
01558 
01559     elif system == 'MacOS':
01560         # MacOS platforms
01561         if terse:
01562             platform = _platform(system,release)
01563         else:
01564             platform = _platform(system,release,machine)
01565 
01566     else:
01567         # Generic handler
01568         if terse:
01569             platform = _platform(system,release)
01570         else:
01571             bits,linkage = architecture(sys.executable)
01572             platform = _platform(system,release,machine,processor,bits,linkage)
01573 
01574     _platform_cache[(aliased, terse)] = platform
01575     return platform
01576 
01577 ### Command line interface
01578 
01579 if __name__ == '__main__':
01580     # Default is to print the aliased verbose platform string
01581     terse = ('terse' in sys.argv or '--terse' in sys.argv)
01582     aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
01583     print(platform(aliased,terse))
01584     sys.exit(0)