diff options
Diffstat (limited to 'Lib/platform.py')
| -rwxr-xr-x | Lib/platform.py | 629 |
1 files changed, 485 insertions, 144 deletions
diff --git a/Lib/platform.py b/Lib/platform.py index 0df65db..c501456 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -10,7 +10,7 @@ """ # This module is maintained by Marc-Andre Lemburg <mal@egenix.com>. # If you find problems, please submit bug reports/patches via the -# Python SourceForge Project Page and assign them to "lemburg". +# Python bug tracker (http://bugs.python.org) and assign them to "lemburg". # # Note: Please keep this module compatible to Python 1.5.2. # @@ -28,12 +28,15 @@ # Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg # Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark # Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support), -# Colin Kong, Trent Mick, Guido van Rossum +# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter # # History: # # <see CVS and SVN checkin messages for history> # +# 1.0.6 - added linux_distribution() +# 1.0.5 - fixed Java support to allow running the module on Jython +# 1.0.4 - added IronPython support # 1.0.3 - added normalization of Windows system name # 1.0.2 - added more Windows support # 1.0.1 - reformatted to make doc.py happy @@ -88,7 +91,7 @@ __copyright__ = """ Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com - Copyright (c) 2000-2003, eGenix.com Software GmbH; mailto:info@egenix.com + Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee or royalty is hereby granted, @@ -107,7 +110,7 @@ __copyright__ = """ """ -__version__ = '1.0.4' +__version__ = '1.0.6' import sys,string,os,re @@ -136,6 +139,11 @@ def libc_ver(executable=sys.executable,lib='',version='', The file is read and scanned in chunks of chunksize bytes. """ + if hasattr(os.path, 'realpath'): + # Python 2.2 introduced os.path.realpath(); it is used + # here to work around problems with Cygwin not being + # able to open symlinks for reading + executable = os.path.realpath(executable) f = open(executable,'rb') binary = f.read(chunksize) pos = 0 @@ -218,14 +226,74 @@ def _dist_try_harder(distname,version,id): return distname,version,id _release_filename = re.compile(r'(\w+)[-_](release|version)') -_release_version = re.compile(r'([\d.]+)[^(]*(?:\((.+)\))?') - -# Note:In supported_dists below we need 'fedora' before 'redhat' as in -# Fedora redhat-release is a link to fedora-release. - -def dist(distname='',version='',id='', - - supported_dists=('SuSE', 'debian', 'fedora', 'redhat', 'mandrake')): +_lsb_release_version = re.compile(r'(.+)' + ' release ' + '([\d.]+)' + '[^(]*(?:\((.+)\))?') +_release_version = re.compile(r'([^0-9]+)' + '(?: release )?' + '([\d.]+)' + '[^(]*(?:\((.+)\))?') + +# See also http://www.novell.com/coolsolutions/feature/11251.html +# and http://linuxmafia.com/faq/Admin/release-files.html +# and http://data.linux-ntfs.org/rpm/whichrpm +# and http://www.die.net/doc/linux/man/man1/lsb_release.1.html + +_supported_dists = ( + 'SuSE', 'debian', 'fedora', 'redhat', 'centos', + 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', + 'UnitedLinux', 'turbolinux') + +def _parse_release_file(firstline): + + # Default to empty 'version' and 'id' strings. Both defaults are used + # when 'firstline' is empty. 'id' defaults to empty when an id can not + # be deduced. + version = '' + id = '' + + # Parse the first line + m = _lsb_release_version.match(firstline) + if m is not None: + # LSB format: "distro release x.x (codename)" + return tuple(m.groups()) + + # Pre-LSB format: "distro x.x (codename)" + m = _release_version.match(firstline) + if m is not None: + return tuple(m.groups()) + + # Unkown format... take the first two words + l = string.split(string.strip(firstline)) + if l: + version = l[0] + if len(l) > 1: + id = l[1] + return '', version, id + +def _test_parse_release_file(): + + for input, output in ( + # Examples of release file contents: + ('SuSE Linux 9.3 (x86-64)', ('SuSE Linux ', '9.3', 'x86-64')) + ('SUSE LINUX 10.1 (X86-64)', ('SUSE LINUX ', '10.1', 'X86-64')) + ('SUSE LINUX 10.1 (i586)', ('SUSE LINUX ', '10.1', 'i586')) + ('Fedora Core release 5 (Bordeaux)', ('Fedora Core', '5', 'Bordeaux')) + ('Red Hat Linux release 8.0 (Psyche)', ('Red Hat Linux', '8.0', 'Psyche')) + ('Red Hat Linux release 9 (Shrike)', ('Red Hat Linux', '9', 'Shrike')) + ('Red Hat Enterprise Linux release 4 (Nahant)', ('Red Hat Enterprise Linux', '4', 'Nahant')) + ('CentOS release 4', ('CentOS', '4', None)) + ('Rocks release 4.2.1 (Cydonia)', ('Rocks', '4.2.1', 'Cydonia')) + ): + parsed = _parse_release_file(input) + if parsed != output: + print (input, parsed) + +def linux_distribution(distname='', version='', id='', + + supported_dists=_supported_dists, + full_distribution_name=1): """ Tries to determine the name of the Linux OS distribution name. @@ -233,6 +301,15 @@ def dist(distname='',version='',id='', /etc and then reverts to _dist_try_harder() in case no suitable files are found. + supported_dists may be given to define the set of Linux + distributions to look for. It defaults to a list of currently + supported Linux distributions identified by their release file + name. + + If full_distribution_name is true (default), the full + distribution read from the OS is returned. Otherwise the short + name taken from supported_dists is used. + Returns a tuple (distname,version,id) which default to the args given as parameters. @@ -242,33 +319,50 @@ def dist(distname='',version='',id='', except os.error: # Probably not a Unix system return distname,version,id + etc.sort() for file in etc: m = _release_filename.match(file) - if m: + if m is not None: _distname,dummy = m.groups() if _distname in supported_dists: distname = _distname break else: return _dist_try_harder(distname,version,id) - f = open('/etc/'+file,'r') + + # Read the first line + f = open('/etc/'+file, 'r') firstline = f.readline() f.close() - m = _release_version.search(firstline) - if m: - _version,_id = m.groups() - if _version: - version = _version - if _id: - id = _id - else: - # Unkown format... take the first two words - l = string.split(string.strip(firstline)) - if l: - version = l[0] - if len(l) > 1: - id = l[1] - return distname,version,id + _distname, _version, _id = _parse_release_file(firstline) + + if _distname and full_distribution_name: + distname = _distname + if _version: + version = _version + if _id: + id = _id + return distname, version, id + +# To maintain backwards compatibility: + +def dist(distname='',version='',id='', + + supported_dists=_supported_dists): + + """ Tries to determine the name of the Linux OS distribution name. + + The function first looks for a distribution release file in + /etc and then reverts to _dist_try_harder() in case no + suitable files are found. + + Returns a tuple (distname,version,id) which default to the + args given as parameters. + + """ + return linux_distribution(distname, version, id, + supported_dists=supported_dists, + full_distribution_name=0) class _popen: @@ -357,7 +451,7 @@ def popen(cmd, mode='r', bufsize=None): else: return popen(cmd,mode,bufsize) -def _norm_version(version,build=''): +def _norm_version(version, build=''): """ Normalize the version and build strings and return a single version string using the format major.minor.build (or patchlevel). @@ -378,7 +472,7 @@ _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' '.*' 'Version ([\d.]+))') -def _syscmd_ver(system='',release='',version='', +def _syscmd_ver(system='', release='', version='', supported_platforms=('win32','win16','dos','os2')): @@ -418,7 +512,7 @@ def _syscmd_ver(system='',release='',version='', # Parse the output info = string.strip(info) m = _ver_output.match(info) - if m: + if m is not None: system,release,version = m.groups() # Strip trailing dots from version and release if release[-1] == '.': @@ -437,7 +531,13 @@ def _win32_getvalue(key,name,default=''): In case this fails, default is returned. """ - from win32api import RegQueryValueEx + try: + # Use win32api if available + from win32api import RegQueryValueEx + except ImportError: + # On Python 2.0 and later, emulate using _winreg + import _winreg + RegQueryValueEx = _winreg.QueryValueEx try: return RegQueryValueEx(key,name) except: @@ -457,9 +557,9 @@ def win32_ver(release='',version='',csd='',ptype=''): means the OS version uses debugging code, i.e. code that checks arguments, ranges, etc. (Thomas Heller). - Note: this function only works if Mark Hammond's win32 - package is installed and obviously only runs on Win32 - compatible platforms. + Note: this function works best with Mark Hammond's win32 + package installed, but also on Python 2.3 and later. It + obviously only runs on Win32 compatible platforms. """ # XXX Is there any way to find out the processor type on WinXX ? @@ -473,17 +573,42 @@ def win32_ver(release='',version='',csd='',ptype=''): # Import the needed APIs try: import win32api + from win32api import RegQueryValueEx, RegOpenKeyEx, \ + RegCloseKey, GetVersionEx + from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \ + VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION except ImportError: - return release,version,csd,ptype - from win32api import RegQueryValueEx,RegOpenKeyEx,RegCloseKey,GetVersionEx - from win32con import HKEY_LOCAL_MACHINE,VER_PLATFORM_WIN32_NT,\ - VER_PLATFORM_WIN32_WINDOWS + # Emulate the win32api module using Python APIs + try: + sys.getwindowsversion + except AttributeError: + # No emulation possible, so return the defaults... + return release,version,csd,ptype + else: + # Emulation using _winreg (added in Python 2.0) and + # sys.getwindowsversion() (added in Python 2.3) + import _winreg + GetVersionEx = sys.getwindowsversion + RegQueryValueEx = _winreg.QueryValueEx + RegOpenKeyEx = _winreg.OpenKeyEx + RegCloseKey = _winreg.CloseKey + HKEY_LOCAL_MACHINE = _winreg.HKEY_LOCAL_MACHINE + VER_PLATFORM_WIN32_WINDOWS = 1 + VER_PLATFORM_WIN32_NT = 2 + VER_NT_WORKSTATION = 1 + VER_NT_SERVER = 3 + REG_SZ = 1 # Find out the registry key and some general version infos - maj,min,buildno,plat,csd = GetVersionEx() + winver = GetVersionEx() + maj,min,buildno,plat,csd = winver version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF) - if csd[:13] == 'Service Pack ': - csd = 'SP' + csd[13:] + if hasattr(winver, "service_pack"): + if winver.service_pack != "": + csd = 'SP%s' % winver.service_pack_major + else: + if csd[:13] == 'Service Pack ': + csd = 'SP' + csd[13:] if plat == VER_PLATFORM_WIN32_WINDOWS: regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion' # Try to guess the release name @@ -511,6 +636,36 @@ def win32_ver(release='',version='',csd='',ptype=''): release = '2003Server' else: release = 'post2003' + elif maj == 6: + if hasattr(winver, "product_type"): + product_type = winver.product_type + else: + product_type = VER_NT_WORKSTATION + # Without an OSVERSIONINFOEX capable sys.getwindowsversion(), + # or help from the registry, we cannot properly identify + # non-workstation versions. + try: + key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey) + name, type = RegQueryValueEx(key, "ProductName") + # Discard any type that isn't REG_SZ + if type == REG_SZ and name.find("Server") != -1: + product_type = VER_NT_SERVER + except WindowsError: + # Use default of VER_NT_WORKSTATION + pass + + if min == 0: + if product_type == VER_NT_WORKSTATION: + release = 'Vista' + else: + release = '2008Server' + elif min == 1: + if product_type == VER_NT_WORKSTATION: + release = '7' + else: + release = '2008ServerR2' + else: + release = 'post2008Server' else: if not release: # E.g. Win3.1 with win32s @@ -519,9 +674,9 @@ def win32_ver(release='',version='',csd='',ptype=''): # Open the registry key try: - keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE,regkey) + keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey) # Get a value to make sure the key exists... - RegQueryValueEx(keyCurVer,'SystemRoot') + RegQueryValueEx(keyCurVer, 'SystemRoot') except: return release,version,csd,ptype @@ -562,30 +717,22 @@ def _bcd2str(bcd): return hex(bcd)[2:] -def mac_ver(release='',versioninfo=('','',''),machine=''): - - """ Get MacOS version information and return it as tuple (release, - versioninfo, machine) with versioninfo being a tuple (version, - dev_stage, non_release_version). - - Entries which cannot be determined are set to the paramter values - which default to ''. All tuple entries are strings. - +def _mac_ver_gestalt(): + """ Thanks to Mark R. Levinson for mailing documentation links and code examples for this function. Documentation for the gestalt() API is available online at: http://www.rgaros.nl/gestalt/ - """ # Check whether the version info module is available try: import gestalt import MacOS except ImportError: - return release,versioninfo,machine + return None # Get the infos - sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa')) + sysv,sysa = _mac_ver_lookup(('sysv','sysa')) # Decode the infos if sysv: major = (sysv & 0xFF00) >> 8 @@ -602,31 +749,69 @@ def mac_ver(release='',versioninfo=('','',''),machine=''): release = '%i.%i.%i' %(major, minor, patch) else: release = '%s.%i.%i' % (_bcd2str(major),minor,patch) - if sysu: - major = int((sysu & 0xFF000000L) >> 24) - minor = (sysu & 0x00F00000) >> 20 - bugfix = (sysu & 0x000F0000) >> 16 - stage = (sysu & 0x0000FF00) >> 8 - nonrel = (sysu & 0x000000FF) - version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix) - nonrel = _bcd2str(nonrel) - stage = {0x20:'development', - 0x40:'alpha', - 0x60:'beta', - 0x80:'final'}.get(stage,'') - versioninfo = (version,stage,nonrel) + if sysa: machine = {0x1: '68k', 0x2: 'PowerPC', 0xa: 'i386'}.get(sysa,'') + + return release,versioninfo,machine + +def _mac_ver_xml(): + fn = '/System/Library/CoreServices/SystemVersion.plist' + if not os.path.exists(fn): + return None + + try: + import plistlib + except ImportError: + return None + + pl = plistlib.readPlist(fn) + release = pl['ProductVersion'] + versioninfo=('', '', '') + machine = os.uname()[4] + if machine in ('ppc', 'Power Macintosh'): + # for compatibility with the gestalt based code + machine = 'PowerPC' + + return release,versioninfo,machine + + +def mac_ver(release='',versioninfo=('','',''),machine=''): + + """ Get MacOS version information and return it as tuple (release, + versioninfo, machine) with versioninfo being a tuple (version, + dev_stage, non_release_version). + + Entries which cannot be determined are set to the paramter values + which default to ''. All tuple entries are strings. + """ + + # First try reading the information from an XML file which should + # always be present + info = _mac_ver_xml() + if info is not None: + return info + + # If that doesn't work for some reason fall back to reading the + # information using gestalt calls. + info = _mac_ver_gestalt() + if info is not None: + return info + + # If that also doesn't work return the default values return release,versioninfo,machine def _java_getprop(name,default): from java.lang import System try: - return System.getProperty(name) - except: + value = System.getProperty(name) + if value is None: + return default + return value + except AttributeError: return default def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')): @@ -647,20 +832,20 @@ def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')): except ImportError: return release,vendor,vminfo,osinfo - vendor = _java_getprop('java.vendor',vendor) - release = _java_getprop('java.version',release) - vm_name,vm_release,vm_vendor = vminfo - vm_name = _java_getprop('java.vm.name',vm_name) - vm_vendor = _java_getprop('java.vm.vendor',vm_vendor) - vm_release = _java_getprop('java.vm.version',vm_release) - vminfo = vm_name,vm_release,vm_vendor - os_name,os_version,os_arch = osinfo - os_arch = _java_getprop('java.os.arch',os_arch) - os_name = _java_getprop('java.os.name',os_name) - os_version = _java_getprop('java.os.version',os_version) - osinfo = os_name,os_version,os_arch - - return release,vendor,vminfo,osinfo + vendor = _java_getprop('java.vendor', vendor) + release = _java_getprop('java.version', release) + vm_name, vm_release, vm_vendor = vminfo + vm_name = _java_getprop('java.vm.name', vm_name) + vm_vendor = _java_getprop('java.vm.vendor', vm_vendor) + vm_release = _java_getprop('java.vm.version', vm_release) + vminfo = vm_name, vm_release, vm_vendor + os_name, os_version, os_arch = osinfo + os_arch = _java_getprop('java.os.arch', os_arch) + os_name = _java_getprop('java.os.name', os_name) + os_version = _java_getprop('java.os.version', os_version) + osinfo = os_name, os_version, os_arch + + return release, vendor, vminfo, osinfo ### System name aliasing @@ -726,7 +911,7 @@ def _platform(*args): # Format the platform string platform = string.join( map(string.strip, - filter(len,args)), + filter(len, args)), '-') # Cleanup some possible filename obstacles... @@ -824,9 +1009,12 @@ def _syscmd_file(target,default=''): case the command should fail. """ - target = _follow_symlinks(target) + if sys.platform in ('dos','win32','win16','os2'): + # XXX Others too ? + return default + target = _follow_symlinks(target).replace('"', '\\"') try: - f = os.popen('file %s 2> /dev/null' % target) + f = os.popen('file "%s" 2> /dev/null' % target) except (AttributeError,os.error): return default output = string.strip(f.read()) @@ -881,19 +1069,22 @@ def architecture(executable=sys.executable,bits='',linkage=''): bits = str(size*8) + 'bit' # Get data from the 'file' system command - output = _syscmd_file(executable,'') + if executable: + output = _syscmd_file(executable, '') + else: + output = '' if not output and \ executable == sys.executable: # "file" command did not return anything; we'll try to provide # some sensible defaults then... - if _default_architecture.has_key(sys.platform): - b,l = _default_architecture[sys.platform] + if sys.platform in _default_architecture: + b, l = _default_architecture[sys.platform] if b: bits = b if l: linkage = l - return bits,linkage + return bits, linkage # Split the output into a list of strings omitting the filename fileout = _architecture_split(output)[1:] @@ -947,29 +1138,44 @@ def uname(): """ global _uname_cache + no_os_uname = 0 if _uname_cache is not None: return _uname_cache + processor = '' + # Get some infos from the builtin os.uname API... try: system,node,release,version,machine = os.uname() - except AttributeError: - # Hmm, no uname... we'll have to poke around the system then. - system = sys.platform - release = '' - version = '' - node = _node() - machine = '' - processor = '' - use_syscmd_ver = 1 + no_os_uname = 1 + + if no_os_uname or not filter(None, (system, node, release, version, machine)): + # Hmm, no there is either no uname or uname has returned + #'unknowns'... we'll have to poke around the system then. + if no_os_uname: + system = sys.platform + release = '' + version = '' + node = _node() + machine = '' + + use_syscmd_ver = 01 # Try win32_ver() on win32 platforms if system == 'win32': release,version,csd,ptype = win32_ver() if release and version: use_syscmd_ver = 0 + # Try to use the PROCESSOR_* environment variables + # available on Win XP and later; see + # http://support.microsoft.com/kb/888731 and + # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM + if not machine: + machine = os.environ.get('PROCESSOR_ARCHITECTURE', '') + if not processor: + processor = os.environ.get('PROCESSOR_IDENTIFIER', machine) # Try the 'ver' system command available on some # platforms @@ -979,6 +1185,16 @@ def uname(): # (_syscmd_ver() tends to return the vendor name as well) if system == 'Microsoft Windows': system = 'Windows' + elif system == 'Microsoft' and release == 'Windows': + # Under Windows Vista and Windows Server 2008, + # Microsoft changed the output of the ver command. The + # release is no longer printed. This causes the + # system and release to be misidentified. + system = 'Windows' + if '6.0' == version[:3]: + release = 'Vista' + else: + release = '' # In case we still don't know anything useful, we'll try to # help ourselves @@ -1001,30 +1217,28 @@ def uname(): release,(version,stage,nonrel),machine = mac_ver() system = 'MacOS' - else: - # System specific extensions - if system == 'OpenVMS': - # OpenVMS seems to have release and version mixed up - if not release or release == '0': - release = version - version = '' - # Get processor information - try: - import vms_lib - except ImportError: - pass - else: - csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0) - if (cpu_number >= 128): - processor = 'Alpha' - else: - processor = 'VAX' + # System specific extensions + if system == 'OpenVMS': + # OpenVMS seems to have release and version mixed up + if not release or release == '0': + release = version + version = '' + # Get processor information + try: + import vms_lib + except ImportError: + pass else: - # Get processor information from the uname system command - processor = _syscmd_uname('-p','') + csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0) + if (cpu_number >= 128): + processor = 'Alpha' + else: + processor = 'VAX' + if not processor: + # Get processor information from the uname system command + processor = _syscmd_uname('-p','') - # 'unknown' is not really any useful as information; we'll convert - # it to '' which is more portable + #If any unknowns still exist, replace them with ''s, which are more portable if system == 'unknown': system = '' if node == 'unknown': @@ -1108,36 +1322,136 @@ def processor(): ### Various APIs for extracting information from sys.version -_sys_version_parser = re.compile(r'([\w.+]+)\s*' - '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' - '\[([^\]]+)\]?') -_sys_version_cache = None +_sys_version_parser = re.compile( + r'([\w.+]+)\s*' + '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' + '\[([^\]]+)\]?') -def _sys_version(): +_jython_sys_version_parser = re.compile( + r'([\d\.]+)') + +_ironpython_sys_version_parser = re.compile( + r'IronPython\s*' + '([\d\.]+)' + '(?: \(([\d\.]+)\))?' + ' on (.NET [\d\.]+)') + +_sys_version_cache = {} + +def _sys_version(sys_version=None): """ Returns a parsed version of Python's sys.version as tuple - (version, buildno, builddate, compiler) referring to the Python - version, build number, build date/time as string and the compiler + (name, version, branch, revision, buildno, builddate, compiler) + referring to the Python implementation name, version, branch, + revision, build number, build date/time as string and the compiler identification string. Note that unlike the Python sys.version, the returned value for the Python version will always include the patchlevel (it defaults to '.0'). + The function returns empty strings for tuple entries that + cannot be determined. + + sys_version may be given to parse an alternative version + string, e.g. if the version was read from a different Python + interpreter. + """ - global _sys_version_cache + # Get the Python version + if sys_version is None: + sys_version = sys.version + + # Try the cache first + result = _sys_version_cache.get(sys_version, None) + if result is not None: + return result + + # Parse it + if sys_version[:10] == 'IronPython': + # IronPython + name = 'IronPython' + match = _ironpython_sys_version_parser.match(sys_version) + if match is None: + raise ValueError( + 'failed to parse IronPython sys.version: %s' % + repr(sys_version)) + version, alt_version, compiler = match.groups() + branch = '' + revision = '' + buildno = '' + builddate = '' + + elif sys.platform[:4] == 'java': + # Jython + name = 'Jython' + match = _jython_sys_version_parser.match(sys_version) + if match is None: + raise ValueError( + 'failed to parse Jython sys.version: %s' % + repr(sys_version)) + version, = match.groups() + branch = '' + revision = '' + compiler = sys.platform + buildno = '' + builddate = '' - if _sys_version_cache is not None: - return _sys_version_cache - version, buildno, builddate, buildtime, compiler = \ - _sys_version_parser.match(sys.version).groups() - builddate = builddate + ' ' + buildtime + else: + # CPython + match = _sys_version_parser.match(sys_version) + if match is None: + raise ValueError( + 'failed to parse CPython sys.version: %s' % + repr(sys_version)) + version, buildno, builddate, buildtime, compiler = \ + match.groups() + if hasattr(sys, 'subversion'): + # sys.subversion was added in Python 2.5 + name, branch, revision = sys.subversion + else: + name = 'CPython' + branch = '' + revision = '' + builddate = builddate + ' ' + buildtime + + # Add the patchlevel version if missing l = string.split(version, '.') if len(l) == 2: l.append('0') version = string.join(l, '.') - _sys_version_cache = (version, buildno, builddate, compiler) - return _sys_version_cache + + # Build and cache the result + result = (name, version, branch, revision, buildno, builddate, compiler) + _sys_version_cache[sys_version] = result + return result + +def _test_sys_version(): + + _sys_version_cache.clear() + for input, output in ( + ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]', + ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')), + ('IronPython 1.0.60816 on .NET 2.0.50727.42', + ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')), + ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42', + ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')), + ): + parsed = _sys_version(input) + if parsed != output: + print (input, parsed) + +def python_implementation(): + + """ Returns a string identifying the Python implementation. + + Currently, the following implementations are identified: + 'CPython' (C implementation of Python), + 'IronPython' (.NET implementation of Python), + 'Jython' (Java implementation of Python). + + """ + return _sys_version()[0] def python_version(): @@ -1147,7 +1461,7 @@ def python_version(): will always include the patchlevel (it defaults to 0). """ - return _sys_version()[0] + return _sys_version()[1] def python_version_tuple(): @@ -1158,7 +1472,34 @@ def python_version_tuple(): will always include the patchlevel (it defaults to 0). """ - return string.split(_sys_version()[0], '.') + return tuple(string.split(_sys_version()[1], '.')) + +def python_branch(): + + """ Returns a string identifying the Python implementation + branch. + + For CPython this is the Subversion branch from which the + Python binary was built. + + If not available, an empty string is returned. + + """ + + return _sys_version()[2] + +def python_revision(): + + """ Returns a string identifying the Python implementation + revision. + + For CPython this is the Subversion revision from which the + Python binary was built. + + If not available, an empty string is returned. + + """ + return _sys_version()[3] def python_build(): @@ -1166,7 +1507,7 @@ def python_build(): build number and date as strings. """ - return _sys_version()[1:3] + return _sys_version()[4:6] def python_compiler(): @@ -1174,7 +1515,7 @@ def python_compiler(): Python. """ - return _sys_version()[3] + return _sys_version()[6] ### The Opus Magnum of platform strings :-) @@ -1235,7 +1576,7 @@ def platform(aliased=0, terse=0): elif system == 'Java': # Java platforms r,v,vminfo,(os_name,os_version,os_arch) = java_ver() - if terse: + if terse or not os_name: platform = _platform(system,release,version) else: platform = _platform(system,release,version, |
