0001 #!/usr/bin/env python 0002 0003 """ This module tries to retrieve as much platform-identifying data as 0004 possible. It makes this information available via function APIs. 0005 0006 If called from the command line, it prints the platform 0007 information concatenated as single string to stdout. The output 0008 format is useable as part of a filename. 0009 0010 """ 0011 # This module is maintained by Marc-Andre Lemburg <mal@egenix.com>. 0012 # If you find problems, please submit bug reports/patches via the 0013 # Python SourceForge Project Page and assign them to "lemburg". 0014 # 0015 # Note: Please keep this module compatible to Python 1.5.2. 0016 # 0017 # Still needed: 0018 # * more support for WinCE 0019 # * support for MS-DOS (PythonDX ?) 0020 # * support for Amiga and other still unsupported platforms running Python 0021 # * support for additional Linux distributions 0022 # 0023 # Many thanks to all those who helped adding platform-specific 0024 # checks (in no particular order): 0025 # 0026 # Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell, 0027 # Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef 0028 # Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg 0029 # Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark 0030 # Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support), 0031 # Colin Kong, Trent Mick, Guido van Rossum 0032 # 0033 # History: 0034 # 1.0.3 - added normalization of Windows system name 0035 # 1.0.2 - added more Windows support 0036 # 1.0.1 - reformatted to make doc.py happy 0037 # 1.0.0 - reformatted a bit and checked into Python CVS 0038 # 0.8.0 - added sys.version parser and various new access 0039 # APIs (python_version(), python_compiler(), etc.) 0040 # 0.7.2 - fixed architecture() to use sizeof(pointer) where available 0041 # 0.7.1 - added support for Caldera OpenLinux 0042 # 0.7.0 - some fixes for WinCE; untabified the source file 0043 # 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and 0044 # vms_lib.getsyi() configured 0045 # 0.6.1 - added code to prevent 'uname -p' on platforms which are 0046 # known not to support it 0047 # 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k; 0048 # did some cleanup of the interfaces - some APIs have changed 0049 # 0.5.5 - fixed another type in the MacOS code... should have 0050 # used more coffee today ;-) 0051 # 0.5.4 - fixed a few typos in the MacOS code 0052 # 0.5.3 - added experimental MacOS support; added better popen() 0053 # workarounds in _syscmd_ver() -- still not 100% elegant 0054 # though 0055 # 0.5.2 - fixed uname() to return '' instead of 'unknown' in all 0056 # return values (the system uname command tends to return 0057 # 'unknown' instead of just leaving the field emtpy) 0058 # 0.5.1 - included code for slackware dist; added exception handlers 0059 # to cover up situations where platforms don't have os.popen 0060 # (e.g. Mac) or fail on socket.gethostname(); fixed libc 0061 # detection RE 0062 # 0.5.0 - changed the API names referring to system commands to *syscmd*; 0063 # added java_ver(); made syscmd_ver() a private 0064 # API (was system_ver() in previous versions) -- use uname() 0065 # instead; extended the win32_ver() to also return processor 0066 # type information 0067 # 0.4.0 - added win32_ver() and modified the platform() output for WinXX 0068 # 0.3.4 - fixed a bug in _follow_symlinks() 0069 # 0.3.3 - fixed popen() and "file" command invokation bugs 0070 # 0.3.2 - added architecture() API and support for it in platform() 0071 # 0.3.1 - fixed syscmd_ver() RE to support Windows NT 0072 # 0.3.0 - added system alias support 0073 # 0.2.3 - removed 'wince' again... oh well. 0074 # 0.2.2 - added 'wince' to syscmd_ver() supported platforms 0075 # 0.2.1 - added cache logic and changed the platform string format 0076 # 0.2.0 - changed the API to use functions instead of module globals 0077 # since some action take too long to be run on module import 0078 # 0.1.0 - first release 0079 # 0080 # You can always get the latest version of this module at: 0081 # 0082 # http://www.egenix.com/files/python/platform.py 0083 # 0084 # If that URL should fail, try contacting the author. 0085 0086 __copyright__ = """ 0087 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com 0088 Copyright (c) 2000-2003, eGenix.com Software GmbH; mailto:info@egenix.com 0089 0090 Permission to use, copy, modify, and distribute this software and its 0091 documentation for any purpose and without fee or royalty is hereby granted, 0092 provided that the above copyright notice appear in all copies and that 0093 both that copyright notice and this permission notice appear in 0094 supporting documentation or portions thereof, including modifications, 0095 that you make. 0096 0097 EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO 0098 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 0099 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, 0100 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 0101 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 0102 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 0103 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! 0104 0105 """ 0106 0107 __version__ = '1.0.2' 0108 0109 import sys,string,os,re 0110 0111 ### Platform specific APIs 0112 0113 _libc_search = re.compile(r'(__libc_init)' 0114 '|' 0115 '(GLIBC_([0-9.]+))' 0116 '|' 0117 '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)') 0118 0119 def libc_ver(executable=sys.executable,lib='',version='', 0120 0121 chunksize=2048): 0122 0123 """ Tries to determine the libc version that the file executable 0124 (which defaults to the Python interpreter) is linked against. 0125 0126 Returns a tuple of strings (lib,version) which default to the 0127 given parameters in case the lookup fails. 0128 0129 Note that the function has intimate knowledge of how different 0130 libc versions add symbols to the executable and thus is probably 0131 only useable for executables compiled using gcc. 0132 0133 The file is read and scanned in chunks of chunksize bytes. 0134 0135 """ 0136 f = open(executable,'rb') 0137 binary = f.read(chunksize) 0138 pos = 0 0139 while 1: 0140 m = _libc_search.search(binary,pos) 0141 if not m: 0142 binary = f.read(chunksize) 0143 if not binary: 0144 break 0145 pos = 0 0146 continue 0147 libcinit,glibc,glibcversion,so,threads,soversion = m.groups() 0148 if libcinit and not lib: 0149 lib = 'libc' 0150 elif glibc: 0151 if lib != 'glibc': 0152 lib = 'glibc' 0153 version = glibcversion 0154 elif glibcversion > version: 0155 version = glibcversion 0156 elif so: 0157 if lib != 'glibc': 0158 lib = 'libc' 0159 if soversion > version: 0160 version = soversion 0161 if threads and version[-len(threads):] != threads: 0162 version = version + threads 0163 pos = m.end() 0164 f.close() 0165 return lib,version 0166 0167 def _dist_try_harder(distname,version,id): 0168 0169 """ Tries some special tricks to get the distribution 0170 information in case the default method fails. 0171 0172 Currently supports older SuSE Linux, Caldera OpenLinux and 0173 Slackware Linux distributions. 0174 0175 """ 0176 if os.path.exists('/var/adm/inst-log/info'): 0177 # SuSE Linux stores distribution information in that file 0178 info = open('/var/adm/inst-log/info').readlines() 0179 distname = 'SuSE' 0180 for line in info: 0181 tv = string.split(line) 0182 if len(tv) == 2: 0183 tag,value = tv 0184 else: 0185 continue 0186 if tag == 'MIN_DIST_VERSION': 0187 version = string.strip(value) 0188 elif tag == 'DIST_IDENT': 0189 values = string.split(value,'-') 0190 id = values[2] 0191 return distname,version,id 0192 0193 if os.path.exists('/etc/.installed'): 0194 # Caldera OpenLinux has some infos in that file (thanks to Colin Kong) 0195 info = open('/etc/.installed').readlines() 0196 for line in info: 0197 pkg = string.split(line,'-') 0198 if len(pkg) >= 2 and pkg[0] == 'OpenLinux': 0199 # XXX does Caldera support non Intel platforms ? If yes, 0200 # where can we find the needed id ? 0201 return 'OpenLinux',pkg[1],id 0202 0203 if os.path.isdir('/usr/lib/setup'): 0204 # Check for slackware verson tag file (thanks to Greg Andruk) 0205 verfiles = os.listdir('/usr/lib/setup') 0206 for n in range(len(verfiles)-1, -1, -1): 0207 if verfiles[n][:14] != 'slack-version-': 0208 del verfiles[n] 0209 if verfiles: 0210 verfiles.sort() 0211 distname = 'slackware' 0212 version = verfiles[-1][14:] 0213 return distname,version,id 0214 0215 return distname,version,id 0216 0217 _release_filename = re.compile(r'(\w+)[-_](release|version)') 0218 _release_version = re.compile(r'([\d.]+)[^(]*(?:\((.+)\))?') 0219 0220 def dist(distname='',version='',id='', 0221 0222 supported_dists=('SuSE','debian','redhat','mandrake')): 0223 0224 """ Tries to determine the name of the Linux OS distribution name. 0225 0226 The function first looks for a distribution release file in 0227 /etc and then reverts to _dist_try_harder() in case no 0228 suitable files are found. 0229 0230 Returns a tuple (distname,version,id) which default to the 0231 args given as parameters. 0232 0233 """ 0234 try: 0235 etc = os.listdir('/etc') 0236 except os.error: 0237 # Probably not a Unix system 0238 return distname,version,id 0239 for file in etc: 0240 m = _release_filename.match(file) 0241 if m: 0242 _distname,dummy = m.groups() 0243 if _distname in supported_dists: 0244 distname = _distname 0245 break 0246 else: 0247 return _dist_try_harder(distname,version,id) 0248 f = open('/etc/'+file,'r') 0249 firstline = f.readline() 0250 f.close() 0251 m = _release_version.search(firstline) 0252 if m: 0253 _version,_id = m.groups() 0254 if _version: 0255 version = _version 0256 if _id: 0257 id = _id 0258 else: 0259 # Unkown format... take the first two words 0260 l = string.split(string.strip(firstline)) 0261 if l: 0262 version = l[0] 0263 if len(l) > 1: 0264 id = l[1] 0265 return distname,version,id 0266 0267 class _popen: 0268 0269 """ Fairly portable (alternative) popen implementation. 0270 0271 This is mostly needed in case os.popen() is not available, or 0272 doesn't work as advertised, e.g. in Win9X GUI programs like 0273 PythonWin or IDLE. 0274 0275 Writing to the pipe is currently not supported. 0276 0277 """ 0278 tmpfile = '' 0279 pipe = None 0280 bufsize = None 0281 mode = 'r' 0282 0283 def __init__(self,cmd,mode='r',bufsize=None): 0284 0285 if mode != 'r': 0286 raise ValueError,'popen()-emulation only supports read mode' 0287 import tempfile 0288 self.tmpfile = tmpfile = tempfile.mktemp() 0289 os.system(cmd + ' > %s' % tmpfile) 0290 self.pipe = open(tmpfile,'rb') 0291 self.bufsize = bufsize 0292 self.mode = mode 0293 0294 def read(self): 0295 0296 return self.pipe.read() 0297 0298 def readlines(self): 0299 0300 if self.bufsize is not None: 0301 return self.pipe.readlines() 0302 0303 def close(self, 0304 0305 remove=os.unlink,error=os.error): 0306 0307 if self.pipe: 0308 rc = self.pipe.close() 0309 else: 0310 rc = 255 0311 if self.tmpfile: 0312 try: 0313 remove(self.tmpfile) 0314 except error: 0315 pass 0316 return rc 0317 0318 # Alias 0319 __del__ = close 0320 0321 def popen(cmd, mode='r', bufsize=None): 0322 0323 """ Portable popen() interface. 0324 """ 0325 # Find a working popen implementation preferring win32pipe.popen 0326 # over os.popen over _popen 0327 popen = None 0328 if os.environ.get('OS','') == 'Windows_NT': 0329 # On NT win32pipe should work; on Win9x it hangs due to bugs 0330 # in the MS C lib (see MS KnowledgeBase article Q150956) 0331 try: 0332 import win32pipe 0333 except ImportError: 0334 pass 0335 else: 0336 popen = win32pipe.popen 0337 if popen is None: 0338 if hasattr(os,'popen'): 0339 popen = os.popen 0340 # Check whether it works... it doesn't in GUI programs 0341 # on Windows platforms 0342 if sys.platform == 'win32': # XXX Others too ? 0343 try: 0344 popen('') 0345 except os.error: 0346 popen = _popen 0347 else: 0348 popen = _popen 0349 if bufsize is None: 0350 return popen(cmd,mode) 0351 else: 0352 return popen(cmd,mode,bufsize) 0353 0354 def _norm_version(version,build=''): 0355 0356 """ Normalize the version and build strings and return a single 0357 vesion string using the format major.minor.build (or patchlevel). 0358 """ 0359 l = string.split(version,'.') 0360 if build: 0361 l.append(build) 0362 try: 0363 ints = map(int,l) 0364 except ValueError: 0365 strings = l 0366 else: 0367 strings = map(str,ints) 0368 version = string.join(strings[:3],'.') 0369 return version 0370 0371 _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' 0372 '.*' 0373 'Version ([\d.]+))') 0374 0375 def _syscmd_ver(system='',release='',version='', 0376 0377 supported_platforms=('win32','win16','dos','os2')): 0378 0379 """ Tries to figure out the OS version used and returns 0380 a tuple (system,release,version). 0381 0382 It uses the "ver" shell command for this which is known 0383 to exists on Windows, DOS and OS/2. XXX Others too ? 0384 0385 In case this fails, the given parameters are used as 0386 defaults. 0387 0388 """ 0389 if sys.platform not in supported_platforms: 0390 return system,release,version 0391 0392 # Try some common cmd strings 0393 for cmd in ('ver','command /c ver','cmd /c ver'): 0394 try: 0395 pipe = popen(cmd) 0396 info = pipe.read() 0397 if pipe.close(): 0398 raise os.error,'command failed' 0399 # XXX How can I supress shell errors from being written 0400 # to stderr ? 0401 except os.error,why: 0402 #print 'Command %s failed: %s' % (cmd,why) 0403 continue 0404 except IOError,why: 0405 #print 'Command %s failed: %s' % (cmd,why) 0406 continue 0407 else: 0408 break 0409 else: 0410 return system,release,version 0411 0412 # Parse the output 0413 info = string.strip(info) 0414 m = _ver_output.match(info) 0415 if m: 0416 system,release,version = m.groups() 0417 # Strip trailing dots from version and release 0418 if release[-1] == '.': 0419 release = release[:-1] 0420 if version[-1] == '.': 0421 version = version[:-1] 0422 # Normalize the version and build strings (eliminating additional 0423 # zeros) 0424 version = _norm_version(version) 0425 return system,release,version 0426 0427 def _win32_getvalue(key,name,default=''): 0428 0429 """ Read a value for name from the registry key. 0430 0431 In case this fails, default is returned. 0432 0433 """ 0434 from win32api import RegQueryValueEx 0435 try: 0436 return RegQueryValueEx(key,name) 0437 except: 0438 return default 0439 0440 def win32_ver(release='',version='',csd='',ptype=''): 0441 0442 """ Get additional version information from the Windows Registry 0443 and return a tuple (version,csd,ptype) referring to version 0444 number, CSD level and OS type (multi/single 0445 processor). 0446 0447 As a hint: ptype returns 'Uniprocessor Free' on single 0448 processor NT machines and 'Multiprocessor Free' on multi 0449 processor machines. The 'Free' refers to the OS version being 0450 free of debugging code. It could also state 'Checked' which 0451 means the OS version uses debugging code, i.e. code that 0452 checks arguments, ranges, etc. (Thomas Heller). 0453 0454 Note: this function only works if Mark Hammond's win32 0455 package is installed and obviously only runs on Win32 0456 compatible platforms. 0457 0458 """ 0459 # XXX Is there any way to find out the processor type on WinXX ? 0460 # XXX Is win32 available on Windows CE ? 0461 # 0462 # Adapted from code posted by Karl Putland to comp.lang.python. 0463 # 0464 # The mappings between reg. values and release names can be found 0465 # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp 0466 0467 # Import the needed APIs 0468 try: 0469 import win32api 0470 except ImportError: 0471 return release,version,csd,ptype 0472 from win32api import RegQueryValueEx,RegOpenKeyEx,RegCloseKey,GetVersionEx 0473 from win32con import HKEY_LOCAL_MACHINE,VER_PLATFORM_WIN32_NT,\ 0474 VER_PLATFORM_WIN32_WINDOWS 0475 0476 # Find out the registry key and some general version infos 0477 maj,min,buildno,plat,csd = GetVersionEx() 0478 version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF) 0479 if csd[:13] == 'Service Pack ': 0480 csd = 'SP' + csd[13:] 0481 if plat == VER_PLATFORM_WIN32_WINDOWS: 0482 regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion' 0483 # Try to guess the release name 0484 if maj == 4: 0485 if min == 0: 0486 release = '95' 0487 elif min == 10: 0488 release = '98' 0489 elif min == 90: 0490 release = 'Me' 0491 else: 0492 release = 'postMe' 0493 elif maj == 5: 0494 release = '2000' 0495 elif plat == VER_PLATFORM_WIN32_NT: 0496 regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion' 0497 if maj <= 4: 0498 release = 'NT' 0499 elif maj == 5: 0500 if min == 0: 0501 release = '2000' 0502 elif min == 1: 0503 release = 'XP' 0504 elif min == 2: 0505 release = '2003Server' 0506 else: 0507 release = 'post2003' 0508 else: 0509 if not release: 0510 # E.g. Win3.1 with win32s 0511 release = '%i.%i' % (maj,min) 0512 return release,version,csd,ptype 0513 0514 # Open the registry key 0515 try: 0516 keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE,regkey) 0517 # Get a value to make sure the key exists... 0518 RegQueryValueEx(keyCurVer,'SystemRoot') 0519 except: 0520 return release,version,csd,ptype 0521 0522 # Parse values 0523 #subversion = _win32_getvalue(keyCurVer, 0524 # 'SubVersionNumber', 0525 # ('',1))[0] 0526 #if subversion: 0527 # release = release + subversion # 95a, 95b, etc. 0528 build = _win32_getvalue(keyCurVer, 0529 'CurrentBuildNumber', 0530 ('',1))[0] 0531 ptype = _win32_getvalue(keyCurVer, 0532 'CurrentType', 0533 (ptype,1))[0] 0534 0535 # Normalize version 0536 version = _norm_version(version,build) 0537 0538 # Close key 0539 RegCloseKey(keyCurVer) 0540 return release,version,csd,ptype 0541 0542 def _mac_ver_lookup(selectors,default=None): 0543 0544 from gestalt import gestalt 0545 import MacOS 0546 l = [] 0547 append = l.append 0548 for selector in selectors: 0549 try: 0550 append(gestalt(selector)) 0551 except (RuntimeError, MacOS.Error): 0552 append(default) 0553 return l 0554 0555 def _bcd2str(bcd): 0556 0557 return hex(bcd)[2:] 0558 0559 def mac_ver(release='',versioninfo=('','',''),machine=''): 0560 0561 """ Get MacOS version information and return it as tuple (release, 0562 versioninfo, machine) with versioninfo being a tuple (version, 0563 dev_stage, non_release_version). 0564 0565 Entries which cannot be determined are set to the paramter values 0566 which default to ''. All tuple entries are strings. 0567 0568 Thanks to Mark R. Levinson for mailing documentation links and 0569 code examples for this function. Documentation for the 0570 gestalt() API is available online at: 0571 0572 http://www.rgaros.nl/gestalt/ 0573 0574 """ 0575 # Check whether the version info module is available 0576 try: 0577 import gestalt 0578 import MacOS 0579 except ImportError: 0580 return release,versioninfo,machine 0581 # Get the infos 0582 sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa')) 0583 # Decode the infos 0584 if sysv: 0585 major = (sysv & 0xFF00) >> 8 0586 minor = (sysv & 0x00F0) >> 4 0587 patch = (sysv & 0x000F) 0588 release = '%s.%i.%i' % (_bcd2str(major),minor,patch) 0589 if sysu: 0590 major = int((sysu & 0xFF000000L) >> 24) 0591 minor = (sysu & 0x00F00000) >> 20 0592 bugfix = (sysu & 0x000F0000) >> 16 0593 stage = (sysu & 0x0000FF00) >> 8 0594 nonrel = (sysu & 0x000000FF) 0595 version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix) 0596 nonrel = _bcd2str(nonrel) 0597 stage = {0x20:'development', 0598 0x40:'alpha', 0599 0x60:'beta', 0600 0x80:'final'}.get(stage,'') 0601 versioninfo = (version,stage,nonrel) 0602 if sysa: 0603 machine = {0x1: '68k', 0604 0x2: 'PowerPC'}.get(sysa,'') 0605 return release,versioninfo,machine 0606 0607 def _java_getprop(name,default): 0608 0609 from java.lang import System 0610 try: 0611 return System.getProperty(name) 0612 except: 0613 return default 0614 0615 def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')): 0616 0617 """ Version interface for Jython. 0618 0619 Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being 0620 a tuple (vm_name,vm_release,vm_vendor) and osinfo being a 0621 tuple (os_name,os_version,os_arch). 0622 0623 Values which cannot be determined are set to the defaults 0624 given as parameters (which all default to ''). 0625 0626 """ 0627 # Import the needed APIs 0628 try: 0629 import java.lang 0630 except ImportError: 0631 return release,vendor,vminfo,osinfo 0632 0633 vendor = _java_getprop('java.vendor',vendor) 0634 release = _java_getprop('java.version',release) 0635 vm_name,vm_release,vm_vendor = vminfo 0636 vm_name = _java_getprop('java.vm.name',vm_name) 0637 vm_vendor = _java_getprop('java.vm.vendor',vm_vendor) 0638 vm_release = _java_getprop('java.vm.version',vm_release) 0639 vminfo = vm_name,vm_release,vm_vendor 0640 os_name,os_version,os_arch = osinfo 0641 os_arch = _java_getprop('java.os.arch',os_arch) 0642 os_name = _java_getprop('java.os.name',os_name) 0643 os_version = _java_getprop('java.os.version',os_version) 0644 osinfo = os_name,os_version,os_arch 0645 0646 return release,vendor,vminfo,osinfo 0647 0648 ### System name aliasing 0649 0650 def system_alias(system,release,version): 0651 0652 """ Returns (system,release,version) aliased to common 0653 marketing names used for some systems. 0654 0655 It also does some reordering of the information in some cases 0656 where it would otherwise cause confusion. 0657 0658 """ 0659 if system == 'Rhapsody': 0660 # Apple's BSD derivative 0661 # XXX How can we determine the marketing release number ? 0662 return 'MacOS X Server',system+release,version 0663 0664 elif system == 'SunOS': 0665 # Sun's OS 0666 if release < '5': 0667 # These releases use the old name SunOS 0668 return system,release,version 0669 # Modify release (marketing release = SunOS release - 3) 0670 l = string.split(release,'.') 0671 if l: 0672 try: 0673 major = int(l[0]) 0674 except ValueError: 0675 pass 0676 else: 0677 major = major - 3 0678 l[0] = str(major) 0679 release = string.join(l,'.') 0680 if release < '6': 0681 system = 'Solaris' 0682 else: 0683 # XXX Whatever the new SunOS marketing name is... 0684 system = 'Solaris' 0685 0686 elif system == 'IRIX64': 0687 # IRIX reports IRIX64 on platforms with 64-bit support; yet it 0688 # is really a version and not a different platform, since 32-bit 0689 # apps are also supported.. 0690 system = 'IRIX' 0691 if version: 0692 version = version + ' (64bit)' 0693 else: 0694 version = '64bit' 0695 0696 elif system in ('win32','win16'): 0697 # In case one of the other tricks 0698 system = 'Windows' 0699 0700 return system,release,version 0701 0702 ### Various internal helpers 0703 0704 def _platform(*args): 0705 0706 """ Helper to format the platform string in a filename 0707 compatible format e.g. "system-version-machine". 0708 """ 0709 # Format the platform string 0710 platform = string.join( 0711 map(string.strip, 0712 filter(len,args)), 0713 '-') 0714 0715 # Cleanup some possible filename obstacles... 0716 replace = string.replace 0717 platform = replace(platform,' ','_') 0718 platform = replace(platform,'/','-') 0719 platform = replace(platform,'\\','-') 0720 platform = replace(platform,':','-') 0721 platform = replace(platform,';','-') 0722 platform = replace(platform,'"','-') 0723 platform = replace(platform,'(','-') 0724 platform = replace(platform,')','-') 0725 0726 # No need to report 'unknown' information... 0727 platform = replace(platform,'unknown','') 0728 0729 # Fold '--'s and remove trailing '-' 0730 while 1: 0731 cleaned = replace(platform,'--','-') 0732 if cleaned == platform: 0733 break 0734 platform = cleaned 0735 while platform[-1] == '-': 0736 platform = platform[:-1] 0737 0738 return platform 0739 0740 def _node(default=''): 0741 0742 """ Helper to determine the node name of this machine. 0743 """ 0744 try: 0745 import socket 0746 except ImportError: 0747 # No sockets... 0748 return default 0749 try: 0750 return socket.gethostname() 0751 except socket.error: 0752 # Still not working... 0753 return default 0754 0755 # os.path.abspath is new in Python 1.5.2: 0756 if not hasattr(os.path,'abspath'): 0757 0758 def _abspath(path, 0759 0760 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd, 0761 normpath=os.path.normpath): 0762 0763 if not isabs(path): 0764 path = join(getcwd(), path) 0765 return normpath(path) 0766 0767 else: 0768 0769 _abspath = os.path.abspath 0770 0771 def _follow_symlinks(filepath): 0772 0773 """ In case filepath is a symlink, follow it until a 0774 real file is reached. 0775 """ 0776 filepath = _abspath(filepath) 0777 while os.path.islink(filepath): 0778 filepath = os.path.normpath( 0779 os.path.join(filepath,os.readlink(filepath))) 0780 return filepath 0781 0782 def _syscmd_uname(option,default=''): 0783 0784 """ Interface to the system's uname command. 0785 """ 0786 if sys.platform in ('dos','win32','win16','os2'): 0787 # XXX Others too ? 0788 return default 0789 try: 0790 f = os.popen('uname %s 2> /dev/null' % option) 0791 except (AttributeError,os.error): 0792 return default 0793 output = string.strip(f.read()) 0794 rc = f.close() 0795 if not output or rc: 0796 return default 0797 else: 0798 return output 0799 0800 def _syscmd_file(target,default=''): 0801 0802 """ Interface to the system's file command. 0803 0804 The function uses the -b option of the file command to have it 0805 ommit the filename in its output and if possible the -L option 0806 to have the command follow symlinks. It returns default in 0807 case the command should fail. 0808 0809 """ 0810 target = _follow_symlinks(target) 0811 try: 0812 f = os.popen('file %s 2> /dev/null' % target) 0813 except (AttributeError,os.error): 0814 return default 0815 output = string.strip(f.read()) 0816 rc = f.close() 0817 if not output or rc: 0818 return default 0819 else: 0820 return output 0821 0822 ### Information about the used architecture 0823 0824 # Default values for architecture; non-empty strings override the 0825 # defaults given as parameters 0826 _default_architecture = { 0827 'win32': ('','WindowsPE'), 0828 'win16': ('','Windows'), 0829 'dos': ('','MSDOS'), 0830 } 0831 0832 _architecture_split = re.compile(r'[\s,]').split 0833 0834 def architecture(executable=sys.executable,bits='',linkage=''): 0835 0836 """ Queries the given executable (defaults to the Python interpreter 0837 binary) for various architecture information. 0838 0839 Returns a tuple (bits,linkage) which contains information about 0840 the bit architecture and the linkage format used for the 0841 executable. Both values are returned as strings. 0842 0843 Values that cannot be determined are returned as given by the 0844 parameter presets. If bits is given as '', the sizeof(pointer) 0845 (or sizeof(long) on Python version < 1.5.2) is used as 0846 indicator for the supported pointer size. 0847 0848 The function relies on the system's "file" command to do the 0849 actual work. This is available on most if not all Unix 0850 platforms. On some non-Unix platforms where the "file" command 0851 does not exist and the executable is set to the Python interpreter 0852 binary defaults from _default_architecture are used. 0853 0854 """ 0855 # Use the sizeof(pointer) as default number of bits if nothing 0856 # else is given as default. 0857 if not bits: 0858 import struct 0859 try: 0860 size = struct.calcsize('P') 0861 except struct.error: 0862 # Older installations can only query longs 0863 size = struct.calcsize('l') 0864 bits = str(size*8) + 'bit' 0865 0866 # Get data from the 'file' system command 0867 output = _syscmd_file(executable,'') 0868 0869 if not output and \ 0870 executable == sys.executable: 0871 # "file" command did not return anything; we'll try to provide 0872 # some sensible defaults then... 0873 if _default_architecture.has_key(sys.platform): 0874 b,l = _default_architecture[sys.platform] 0875 if b: 0876 bits = b 0877 if l: 0878 linkage = l 0879 return bits,linkage 0880 0881 # Split the output into a list of strings omitting the filename 0882 fileout = _architecture_split(output)[1:] 0883 0884 if 'executable' not in fileout: 0885 # Format not supported 0886 return bits,linkage 0887 0888 # Bits 0889 if '32-bit' in fileout: 0890 bits = '32bit' 0891 elif 'N32' in fileout: 0892 # On Irix only 0893 bits = 'n32bit' 0894 elif '64-bit' in fileout: 0895 bits = '64bit' 0896 0897 # Linkage 0898 if 'ELF' in fileout: 0899 linkage = 'ELF' 0900 elif 'PE' in fileout: 0901 # E.g. Windows uses this format 0902 if 'Windows' in fileout: 0903 linkage = 'WindowsPE' 0904 else: 0905 linkage = 'PE' 0906 elif 'COFF' in fileout: 0907 linkage = 'COFF' 0908 elif 'MS-DOS' in fileout: 0909 linkage = 'MSDOS' 0910 else: 0911 # XXX the A.OUT format also falls under this class... 0912 pass 0913 0914 return bits,linkage 0915 0916 ### Portable uname() interface 0917 0918 _uname_cache = None 0919 0920 def uname(): 0921 0922 """ Fairly portable uname interface. Returns a tuple 0923 of strings (system,node,release,version,machine,processor) 0924 identifying the underlying platform. 0925 0926 Note that unlike the os.uname function this also returns 0927 possible processor information as an additional tuple entry. 0928 0929 Entries which cannot be determined are set to ''. 0930 0931 """ 0932 global _uname_cache 0933 0934 if _uname_cache is not None: 0935 return _uname_cache 0936 0937 # Get some infos from the builtin os.uname API... 0938 try: 0939 system,node,release,version,machine = os.uname() 0940 0941 except AttributeError: 0942 # Hmm, no uname... we'll have to poke around the system then. 0943 system = sys.platform 0944 release = '' 0945 version = '' 0946 node = _node() 0947 machine = '' 0948 processor = '' 0949 use_syscmd_ver = 1 0950 0951 # Try win32_ver() on win32 platforms 0952 if system == 'win32': 0953 release,version,csd,ptype = win32_ver() 0954 if release and version: 0955 use_syscmd_ver = 0 0956 0957 # Try the 'ver' system command available on some 0958 # platforms 0959 if use_syscmd_ver: 0960 system,release,version = _syscmd_ver(system) 0961 # Normalize system to what win32_ver() normally returns 0962 # (_syscmd_ver() tends to return the vendor name as well) 0963 if system == 'Microsoft Windows': 0964 system = 'Windows' 0965 0966 # In case we still don't know anything useful, we'll try to 0967 # help ourselves 0968 if system in ('win32','win16'): 0969 if not version: 0970 if system == 'win32': 0971 version = '32bit' 0972 else: 0973 version = '16bit' 0974 system = 'Windows' 0975 0976 elif system[:4] == 'java': 0977 release,vendor,vminfo,osinfo = java_ver() 0978 system = 'Java' 0979 version = string.join(vminfo,', ') 0980 if not version: 0981 version = vendor 0982 0983 elif os.name == 'mac': 0984 release,(version,stage,nonrel),machine = mac_ver() 0985 system = 'MacOS' 0986 0987 else: 0988 # System specific extensions 0989 if system == 'OpenVMS': 0990 # OpenVMS seems to have release and version mixed up 0991 if not release or release == '0': 0992 release = version 0993 version = '' 0994 # Get processor information 0995 try: 0996 import vms_lib 0997 except ImportError: 0998 pass 0999 else: 1000 csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0) 1001 if (cpu_number >= 128): 1002 processor = 'Alpha' 1003 else: 1004 processor = 'VAX' 1005 else: 1006 # Get processor information from the uname system command 1007 processor = _syscmd_uname('-p','') 1008 1009 # 'unknown' is not really any useful as information; we'll convert 1010 # it to '' which is more portable 1011 if system == 'unknown': 1012 system = '' 1013 if node == 'unknown': 1014 node = '' 1015 if release == 'unknown': 1016 release = '' 1017 if version == 'unknown': 1018 version = '' 1019 if machine == 'unknown': 1020 machine = '' 1021 if processor == 'unknown': 1022 processor = '' 1023 _uname_cache = system,node,release,version,machine,processor 1024 return _uname_cache 1025 1026 ### Direct interfaces to some of the uname() return values 1027 1028 def system(): 1029 1030 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'. 1031 1032 An empty string is returned if the value cannot be determined. 1033 1034 """ 1035 return uname()[0] 1036 1037 def node(): 1038 1039 """ Returns the computer's network name (which may not be fully 1040 qualified) 1041 1042 An empty string is returned if the value cannot be determined. 1043 1044 """ 1045 return uname()[1] 1046 1047 def release(): 1048 1049 """ Returns the system's release, e.g. '2.2.0' or 'NT' 1050 1051 An empty string is returned if the value cannot be determined. 1052 1053 """ 1054 return uname()[2] 1055 1056 def version(): 1057 1058 """ Returns the system's release version, e.g. '#3 on degas' 1059 1060 An empty string is returned if the value cannot be determined. 1061 1062 """ 1063 return uname()[3] 1064 1065 def machine(): 1066 1067 """ Returns the machine type, e.g. 'i386' 1068 1069 An empty string is returned if the value cannot be determined. 1070 1071 """ 1072 return uname()[4] 1073 1074 def processor(): 1075 1076 """ Returns the (true) processor name, e.g. 'amdk6' 1077 1078 An empty string is returned if the value cannot be 1079 determined. Note that many platforms do not provide this 1080 information or simply return the same value as for machine(), 1081 e.g. NetBSD does this. 1082 1083 """ 1084 return uname()[5] 1085 1086 ### Various APIs for extracting information from sys.version 1087 1088 _sys_version_parser = re.compile(r'([\w.+]+)\s*' 1089 '\(#(\d+),\s*([\w ]+),\s*([\w :]+)\)\s*' 1090 '\[([^\]]+)\]?') 1091 _sys_version_cache = None 1092 1093 def _sys_version(): 1094 1095 """ Returns a parsed version of Python's sys.version as tuple 1096 (version, buildno, builddate, compiler) referring to the Python 1097 version, build number, build date/time as string and the compiler 1098 identification string. 1099 1100 Note that unlike the Python sys.version, the returned value 1101 for the Python version will always include the patchlevel (it 1102 defaults to '.0'). 1103 1104 """ 1105 global _sys_version_cache 1106 1107 if _sys_version_cache is not None: 1108 return _sys_version_cache 1109 version, buildno, builddate, buildtime, compiler = \ 1110 _sys_version_parser.match(sys.version).groups() 1111 buildno = int(buildno) 1112 builddate = builddate + ' ' + buildtime 1113 l = string.split(version, '.') 1114 if len(l) == 2: 1115 l.append('0') 1116 version = string.join(l, '.') 1117 _sys_version_cache = (version, buildno, builddate, compiler) 1118 return _sys_version_cache 1119 1120 def python_version(): 1121 1122 """ Returns the Python version as string 'major.minor.patchlevel' 1123 1124 Note that unlike the Python sys.version, the returned value 1125 will always include the patchlevel (it defaults to 0). 1126 1127 """ 1128 return _sys_version()[0] 1129 1130 def python_version_tuple(): 1131 1132 """ Returns the Python version as tuple (major, minor, patchlevel) 1133 of strings. 1134 1135 Note that unlike the Python sys.version, the returned value 1136 will always include the patchlevel (it defaults to 0). 1137 1138 """ 1139 return string.split(_sys_version()[0], '.') 1140 1141 def python_build(): 1142 1143 """ Returns a tuple (buildno, builddate) stating the Python 1144 build number and date as strings. 1145 1146 """ 1147 return _sys_version()[1:3] 1148 1149 def python_compiler(): 1150 1151 """ Returns a string identifying the compiler used for compiling 1152 Python. 1153 1154 """ 1155 return _sys_version()[3] 1156 1157 ### The Opus Magnum of platform strings :-) 1158 1159 _platform_cache = {} 1160 1161 def platform(aliased=0, terse=0): 1162 1163 """ Returns a single string identifying the underlying platform 1164 with as much useful information as possible (but no more :). 1165 1166 The output is intended to be human readable rather than 1167 machine parseable. It may look different on different 1168 platforms and this is intended. 1169 1170 If "aliased" is true, the function will use aliases for 1171 various platforms that report system names which differ from 1172 their common names, e.g. SunOS will be reported as 1173 Solaris. The system_alias() function is used to implement 1174 this. 1175 1176 Setting terse to true causes the function to return only the 1177 absolute minimum information needed to identify the platform. 1178 1179 """ 1180 result = _platform_cache.get((aliased, terse), None) 1181 if result is not None: 1182 return result 1183 1184 # Get uname information and then apply platform specific cosmetics 1185 # to it... 1186 system,node,release,version,machine,processor = uname() 1187 if machine == processor: 1188 processor = '' 1189 if aliased: 1190 system,release,version = system_alias(system,release,version) 1191 1192 if system == 'Windows': 1193 # MS platforms 1194 rel,vers,csd,ptype = win32_ver(version) 1195 if terse: 1196 platform = _platform(system,release) 1197 else: 1198 platform = _platform(system,release,version,csd) 1199 1200 elif system in ('Linux',): 1201 # Linux based systems 1202 distname,distversion,distid = dist('') 1203 if distname and not terse: 1204 platform = _platform(system,release,machine,processor, 1205 'with', 1206 distname,distversion,distid) 1207 else: 1208 # If the distribution name is unknown check for libc vs. glibc 1209 libcname,libcversion = libc_ver(sys.executable) 1210 platform = _platform(system,release,machine,processor, 1211 'with', 1212 libcname+libcversion) 1213 elif system == 'Java': 1214 # Java platforms 1215 r,v,vminfo,(os_name,os_version,os_arch) = java_ver() 1216 if terse: 1217 platform = _platform(system,release,version) 1218 else: 1219 platform = _platform(system,release,version, 1220 'on', 1221 os_name,os_version,os_arch) 1222 1223 elif system == 'MacOS': 1224 # MacOS platforms 1225 if terse: 1226 platform = _platform(system,release) 1227 else: 1228 platform = _platform(system,release,machine) 1229 1230 else: 1231 # Generic handler 1232 if terse: 1233 platform = _platform(system,release) 1234 else: 1235 bits,linkage = architecture(sys.executable) 1236 platform = _platform(system,release,machine,processor,bits,linkage) 1237 1238 _platform_cache[(aliased, terse)] = platform 1239 return platform 1240 1241 ### Command line interface 1242 1243 if __name__ == '__main__': 1244 # Default is to print the aliased verbose platform string 1245 terse = ('terse' in sys.argv or '--terse' in sys.argv) 1246 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv) 1247 print platform(aliased,terse) 1248 sys.exit(0) 1249
Generated by PyXR 0.9.4