Require implementations for warnings.showwarning() support the 'line' argument.
[python.git] / Lib / platform.py
blob906d918e0ea5fd11ed0862ec3c913b7c71a649b5
1 #!/usr/bin/env python
3 """ This module tries to retrieve as much platform-identifying data as
4 possible. It makes this information available via function APIs.
6 If called from the command line, it prints the platform
7 information concatenated as single string to stdout. The output
8 format is useable as part of a filename.
10 """
11 # This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
12 # If you find problems, please submit bug reports/patches via the
13 # Python SourceForge Project Page and assign them to "lemburg".
15 # Note: Please keep this module compatible to Python 1.5.2.
17 # Still needed:
18 # * more support for WinCE
19 # * support for MS-DOS (PythonDX ?)
20 # * support for Amiga and other still unsupported platforms running Python
21 # * support for additional Linux distributions
23 # Many thanks to all those who helped adding platform-specific
24 # checks (in no particular order):
26 # Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
27 # Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
28 # Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
29 # Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
30 # Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
31 # Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
33 # History:
35 # <see CVS and SVN checkin messages for history>
37 # 1.0.6 - added linux_distribution()
38 # 1.0.5 - fixed Java support to allow running the module on Jython
39 # 1.0.4 - added IronPython support
40 # 1.0.3 - added normalization of Windows system name
41 # 1.0.2 - added more Windows support
42 # 1.0.1 - reformatted to make doc.py happy
43 # 1.0.0 - reformatted a bit and checked into Python CVS
44 # 0.8.0 - added sys.version parser and various new access
45 # APIs (python_version(), python_compiler(), etc.)
46 # 0.7.2 - fixed architecture() to use sizeof(pointer) where available
47 # 0.7.1 - added support for Caldera OpenLinux
48 # 0.7.0 - some fixes for WinCE; untabified the source file
49 # 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
50 # vms_lib.getsyi() configured
51 # 0.6.1 - added code to prevent 'uname -p' on platforms which are
52 # known not to support it
53 # 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
54 # did some cleanup of the interfaces - some APIs have changed
55 # 0.5.5 - fixed another type in the MacOS code... should have
56 # used more coffee today ;-)
57 # 0.5.4 - fixed a few typos in the MacOS code
58 # 0.5.3 - added experimental MacOS support; added better popen()
59 # workarounds in _syscmd_ver() -- still not 100% elegant
60 # though
61 # 0.5.2 - fixed uname() to return '' instead of 'unknown' in all
62 # return values (the system uname command tends to return
63 # 'unknown' instead of just leaving the field emtpy)
64 # 0.5.1 - included code for slackware dist; added exception handlers
65 # to cover up situations where platforms don't have os.popen
66 # (e.g. Mac) or fail on socket.gethostname(); fixed libc
67 # detection RE
68 # 0.5.0 - changed the API names referring to system commands to *syscmd*;
69 # added java_ver(); made syscmd_ver() a private
70 # API (was system_ver() in previous versions) -- use uname()
71 # instead; extended the win32_ver() to also return processor
72 # type information
73 # 0.4.0 - added win32_ver() and modified the platform() output for WinXX
74 # 0.3.4 - fixed a bug in _follow_symlinks()
75 # 0.3.3 - fixed popen() and "file" command invokation bugs
76 # 0.3.2 - added architecture() API and support for it in platform()
77 # 0.3.1 - fixed syscmd_ver() RE to support Windows NT
78 # 0.3.0 - added system alias support
79 # 0.2.3 - removed 'wince' again... oh well.
80 # 0.2.2 - added 'wince' to syscmd_ver() supported platforms
81 # 0.2.1 - added cache logic and changed the platform string format
82 # 0.2.0 - changed the API to use functions instead of module globals
83 # since some action take too long to be run on module import
84 # 0.1.0 - first release
86 # You can always get the latest version of this module at:
88 # http://www.egenix.com/files/python/platform.py
90 # If that URL should fail, try contacting the author.
92 __copyright__ = """
93 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
94 Copyright (c) 2000-2008, eGenix.com Software GmbH; mailto:info@egenix.com
96 Permission to use, copy, modify, and distribute this software and its
97 documentation for any purpose and without fee or royalty is hereby granted,
98 provided that the above copyright notice appear in all copies and that
99 both that copyright notice and this permission notice appear in
100 supporting documentation or portions thereof, including modifications,
101 that you make.
103 EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
104 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
105 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
106 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
107 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
108 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
109 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
113 __version__ = '1.0.6'
115 import sys,string,os,re
117 ### Platform specific APIs
119 _libc_search = re.compile(r'(__libc_init)'
121 '(GLIBC_([0-9.]+))'
123 '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)')
125 def libc_ver(executable=sys.executable,lib='',version='',
127 chunksize=2048):
129 """ Tries to determine the libc version that the file executable
130 (which defaults to the Python interpreter) is linked against.
132 Returns a tuple of strings (lib,version) which default to the
133 given parameters in case the lookup fails.
135 Note that the function has intimate knowledge of how different
136 libc versions add symbols to the executable and thus is probably
137 only useable for executables compiled using gcc.
139 The file is read and scanned in chunks of chunksize bytes.
142 if hasattr(os.path, 'realpath'):
143 # Python 2.2 introduced os.path.realpath(); it is used
144 # here to work around problems with Cygwin not being
145 # able to open symlinks for reading
146 executable = os.path.realpath(executable)
147 f = open(executable,'rb')
148 binary = f.read(chunksize)
149 pos = 0
150 while 1:
151 m = _libc_search.search(binary,pos)
152 if not m:
153 binary = f.read(chunksize)
154 if not binary:
155 break
156 pos = 0
157 continue
158 libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
159 if libcinit and not lib:
160 lib = 'libc'
161 elif glibc:
162 if lib != 'glibc':
163 lib = 'glibc'
164 version = glibcversion
165 elif glibcversion > version:
166 version = glibcversion
167 elif so:
168 if lib != 'glibc':
169 lib = 'libc'
170 if soversion > version:
171 version = soversion
172 if threads and version[-len(threads):] != threads:
173 version = version + threads
174 pos = m.end()
175 f.close()
176 return lib,version
178 def _dist_try_harder(distname,version,id):
180 """ Tries some special tricks to get the distribution
181 information in case the default method fails.
183 Currently supports older SuSE Linux, Caldera OpenLinux and
184 Slackware Linux distributions.
187 if os.path.exists('/var/adm/inst-log/info'):
188 # SuSE Linux stores distribution information in that file
189 info = open('/var/adm/inst-log/info').readlines()
190 distname = 'SuSE'
191 for line in info:
192 tv = string.split(line)
193 if len(tv) == 2:
194 tag,value = tv
195 else:
196 continue
197 if tag == 'MIN_DIST_VERSION':
198 version = string.strip(value)
199 elif tag == 'DIST_IDENT':
200 values = string.split(value,'-')
201 id = values[2]
202 return distname,version,id
204 if os.path.exists('/etc/.installed'):
205 # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
206 info = open('/etc/.installed').readlines()
207 for line in info:
208 pkg = string.split(line,'-')
209 if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
210 # XXX does Caldera support non Intel platforms ? If yes,
211 # where can we find the needed id ?
212 return 'OpenLinux',pkg[1],id
214 if os.path.isdir('/usr/lib/setup'):
215 # Check for slackware verson tag file (thanks to Greg Andruk)
216 verfiles = os.listdir('/usr/lib/setup')
217 for n in range(len(verfiles)-1, -1, -1):
218 if verfiles[n][:14] != 'slack-version-':
219 del verfiles[n]
220 if verfiles:
221 verfiles.sort()
222 distname = 'slackware'
223 version = verfiles[-1][14:]
224 return distname,version,id
226 return distname,version,id
228 _release_filename = re.compile(r'(\w+)[-_](release|version)')
229 _lsb_release_version = re.compile(r'(.+)'
230 ' release '
231 '([\d.]+)'
232 '[^(]*(?:\((.+)\))?')
233 _release_version = re.compile(r'([^0-9]+)'
234 '(?: release )?'
235 '([\d.]+)'
236 '[^(]*(?:\((.+)\))?')
238 # See also http://www.novell.com/coolsolutions/feature/11251.html
239 # and http://linuxmafia.com/faq/Admin/release-files.html
240 # and http://data.linux-ntfs.org/rpm/whichrpm
241 # and http://www.die.net/doc/linux/man/man1/lsb_release.1.html
243 _supported_dists = (
244 'SuSE', 'debian', 'fedora', 'redhat', 'centos',
245 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',
246 'UnitedLinux', 'turbolinux')
248 def _parse_release_file(firstline):
250 # Parse the first line
251 m = _lsb_release_version.match(firstline)
252 if m is not None:
253 # LSB format: "distro release x.x (codename)"
254 return tuple(m.groups())
256 # Pre-LSB format: "distro x.x (codename)"
257 m = _release_version.match(firstline)
258 if m is not None:
259 return tuple(m.groups())
261 # Unkown format... take the first two words
262 l = string.split(string.strip(firstline))
263 if l:
264 version = l[0]
265 if len(l) > 1:
266 id = l[1]
267 else:
268 id = ''
269 return '', version, id
271 def linux_distribution(distname='', version='', id='',
273 supported_dists=_supported_dists,
274 full_distribution_name=1):
276 """ Tries to determine the name of the Linux OS distribution name.
278 The function first looks for a distribution release file in
279 /etc and then reverts to _dist_try_harder() in case no
280 suitable files are found.
282 supported_dists may be given to define the set of Linux
283 distributions to look for. It defaults to a list of currently
284 supported Linux distributions identified by their release file
285 name.
287 If full_distribution_name is true (default), the full
288 distribution read from the OS is returned. Otherwise the short
289 name taken from supported_dists is used.
291 Returns a tuple (distname,version,id) which default to the
292 args given as parameters.
295 try:
296 etc = os.listdir('/etc')
297 except os.error:
298 # Probably not a Unix system
299 return distname,version,id
300 etc.sort()
301 for file in etc:
302 m = _release_filename.match(file)
303 if m is not None:
304 _distname,dummy = m.groups()
305 if _distname in supported_dists:
306 distname = _distname
307 break
308 else:
309 return _dist_try_harder(distname,version,id)
311 # Read the first line
312 f = open('/etc/'+file, 'r')
313 firstline = f.readline()
314 f.close()
315 _distname, _version, _id = _parse_release_file(firstline)
317 if _distname and full_distribution_name:
318 distname = _distname
319 if _version:
320 version = _version
321 if _id:
322 id = _id
323 return distname, version, id
325 # To maintain backwards compatibility:
327 def dist(distname='',version='',id='',
329 supported_dists=_supported_dists):
331 """ Tries to determine the name of the Linux OS distribution name.
333 The function first looks for a distribution release file in
334 /etc and then reverts to _dist_try_harder() in case no
335 suitable files are found.
337 Returns a tuple (distname,version,id) which default to the
338 args given as parameters.
341 return linux_distribution(distname, version, id,
342 supported_dists=supported_dists,
343 full_distribution_name=0)
345 class _popen:
347 """ Fairly portable (alternative) popen implementation.
349 This is mostly needed in case os.popen() is not available, or
350 doesn't work as advertised, e.g. in Win9X GUI programs like
351 PythonWin or IDLE.
353 Writing to the pipe is currently not supported.
356 tmpfile = ''
357 pipe = None
358 bufsize = None
359 mode = 'r'
361 def __init__(self,cmd,mode='r',bufsize=None):
363 if mode != 'r':
364 raise ValueError,'popen()-emulation only supports read mode'
365 import tempfile
366 self.tmpfile = tmpfile = tempfile.mktemp()
367 os.system(cmd + ' > %s' % tmpfile)
368 self.pipe = open(tmpfile,'rb')
369 self.bufsize = bufsize
370 self.mode = mode
372 def read(self):
374 return self.pipe.read()
376 def readlines(self):
378 if self.bufsize is not None:
379 return self.pipe.readlines()
381 def close(self,
383 remove=os.unlink,error=os.error):
385 if self.pipe:
386 rc = self.pipe.close()
387 else:
388 rc = 255
389 if self.tmpfile:
390 try:
391 remove(self.tmpfile)
392 except error:
393 pass
394 return rc
396 # Alias
397 __del__ = close
399 def popen(cmd, mode='r', bufsize=None):
401 """ Portable popen() interface.
403 # Find a working popen implementation preferring win32pipe.popen
404 # over os.popen over _popen
405 popen = None
406 if os.environ.get('OS','') == 'Windows_NT':
407 # On NT win32pipe should work; on Win9x it hangs due to bugs
408 # in the MS C lib (see MS KnowledgeBase article Q150956)
409 try:
410 import win32pipe
411 except ImportError:
412 pass
413 else:
414 popen = win32pipe.popen
415 if popen is None:
416 if hasattr(os,'popen'):
417 popen = os.popen
418 # Check whether it works... it doesn't in GUI programs
419 # on Windows platforms
420 if sys.platform == 'win32': # XXX Others too ?
421 try:
422 popen('')
423 except os.error:
424 popen = _popen
425 else:
426 popen = _popen
427 if bufsize is None:
428 return popen(cmd,mode)
429 else:
430 return popen(cmd,mode,bufsize)
432 def _norm_version(version, build=''):
434 """ Normalize the version and build strings and return a single
435 version string using the format major.minor.build (or patchlevel).
437 l = string.split(version,'.')
438 if build:
439 l.append(build)
440 try:
441 ints = map(int,l)
442 except ValueError:
443 strings = l
444 else:
445 strings = map(str,ints)
446 version = string.join(strings[:3],'.')
447 return version
449 _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
450 '.*'
451 'Version ([\d.]+))')
453 def _syscmd_ver(system='', release='', version='',
455 supported_platforms=('win32','win16','dos','os2')):
457 """ Tries to figure out the OS version used and returns
458 a tuple (system,release,version).
460 It uses the "ver" shell command for this which is known
461 to exists on Windows, DOS and OS/2. XXX Others too ?
463 In case this fails, the given parameters are used as
464 defaults.
467 if sys.platform not in supported_platforms:
468 return system,release,version
470 # Try some common cmd strings
471 for cmd in ('ver','command /c ver','cmd /c ver'):
472 try:
473 pipe = popen(cmd)
474 info = pipe.read()
475 if pipe.close():
476 raise os.error,'command failed'
477 # XXX How can I supress shell errors from being written
478 # to stderr ?
479 except os.error,why:
480 #print 'Command %s failed: %s' % (cmd,why)
481 continue
482 except IOError,why:
483 #print 'Command %s failed: %s' % (cmd,why)
484 continue
485 else:
486 break
487 else:
488 return system,release,version
490 # Parse the output
491 info = string.strip(info)
492 m = _ver_output.match(info)
493 if m is not None:
494 system,release,version = m.groups()
495 # Strip trailing dots from version and release
496 if release[-1] == '.':
497 release = release[:-1]
498 if version[-1] == '.':
499 version = version[:-1]
500 # Normalize the version and build strings (eliminating additional
501 # zeros)
502 version = _norm_version(version)
503 return system,release,version
505 def _win32_getvalue(key,name,default=''):
507 """ Read a value for name from the registry key.
509 In case this fails, default is returned.
512 try:
513 # Use win32api if available
514 from win32api import RegQueryValueEx
515 except ImportError:
516 # On Python 2.0 and later, emulate using _winreg
517 import _winreg
518 RegQueryValueEx = _winreg.QueryValueEx
519 try:
520 return RegQueryValueEx(key,name)
521 except:
522 return default
524 def win32_ver(release='',version='',csd='',ptype=''):
526 """ Get additional version information from the Windows Registry
527 and return a tuple (version,csd,ptype) referring to version
528 number, CSD level and OS type (multi/single
529 processor).
531 As a hint: ptype returns 'Uniprocessor Free' on single
532 processor NT machines and 'Multiprocessor Free' on multi
533 processor machines. The 'Free' refers to the OS version being
534 free of debugging code. It could also state 'Checked' which
535 means the OS version uses debugging code, i.e. code that
536 checks arguments, ranges, etc. (Thomas Heller).
538 Note: this function works best with Mark Hammond's win32
539 package installed, but also on Python 2.3 and later. It
540 obviously only runs on Win32 compatible platforms.
543 # XXX Is there any way to find out the processor type on WinXX ?
544 # XXX Is win32 available on Windows CE ?
546 # Adapted from code posted by Karl Putland to comp.lang.python.
548 # The mappings between reg. values and release names can be found
549 # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
551 # Import the needed APIs
552 try:
553 import win32api
554 from win32api import RegQueryValueEx, RegOpenKeyEx, \
555 RegCloseKey, GetVersionEx
556 from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
557 VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
558 except ImportError:
559 # Emulate the win32api module using Python APIs
560 try:
561 sys.getwindowsversion
562 except AttributeError:
563 # No emulation possible, so return the defaults...
564 return release,version,csd,ptype
565 else:
566 # Emulation using _winreg (added in Python 2.0) and
567 # sys.getwindowsversion() (added in Python 2.3)
568 import _winreg
569 GetVersionEx = sys.getwindowsversion
570 RegQueryValueEx = _winreg.QueryValueEx
571 RegOpenKeyEx = _winreg.OpenKeyEx
572 RegCloseKey = _winreg.CloseKey
573 HKEY_LOCAL_MACHINE = _winreg.HKEY_LOCAL_MACHINE
574 VER_PLATFORM_WIN32_WINDOWS = 1
575 VER_PLATFORM_WIN32_NT = 2
576 VER_NT_WORKSTATION = 1
578 # Find out the registry key and some general version infos
579 maj,min,buildno,plat,csd = GetVersionEx()
580 version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
581 if csd[:13] == 'Service Pack ':
582 csd = 'SP' + csd[13:]
583 if plat == VER_PLATFORM_WIN32_WINDOWS:
584 regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
585 # Try to guess the release name
586 if maj == 4:
587 if min == 0:
588 release = '95'
589 elif min == 10:
590 release = '98'
591 elif min == 90:
592 release = 'Me'
593 else:
594 release = 'postMe'
595 elif maj == 5:
596 release = '2000'
597 elif plat == VER_PLATFORM_WIN32_NT:
598 regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
599 if maj <= 4:
600 release = 'NT'
601 elif maj == 5:
602 if min == 0:
603 release = '2000'
604 elif min == 1:
605 release = 'XP'
606 elif min == 2:
607 release = '2003Server'
608 else:
609 release = 'post2003'
610 elif maj == 6:
611 if min == 0:
612 # Per http://msdn2.microsoft.com/en-us/library/ms724429.aspx
613 try:
614 productType = GetVersionEx(1)[8]
615 except TypeError:
616 # sys.getwindowsversion() doesn't take any arguments, so
617 # we cannot detect 2008 Server that way.
618 # XXX Add some other means of detecting 2008 Server ?!
619 release = 'Vista'
620 else:
621 if productType == VER_NT_WORKSTATION:
622 release = 'Vista'
623 else:
624 release = '2008Server'
625 else:
626 release = 'post2008Server'
627 else:
628 if not release:
629 # E.g. Win3.1 with win32s
630 release = '%i.%i' % (maj,min)
631 return release,version,csd,ptype
633 # Open the registry key
634 try:
635 keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
636 # Get a value to make sure the key exists...
637 RegQueryValueEx(keyCurVer, 'SystemRoot')
638 except:
639 return release,version,csd,ptype
641 # Parse values
642 #subversion = _win32_getvalue(keyCurVer,
643 # 'SubVersionNumber',
644 # ('',1))[0]
645 #if subversion:
646 # release = release + subversion # 95a, 95b, etc.
647 build = _win32_getvalue(keyCurVer,
648 'CurrentBuildNumber',
649 ('',1))[0]
650 ptype = _win32_getvalue(keyCurVer,
651 'CurrentType',
652 (ptype,1))[0]
654 # Normalize version
655 version = _norm_version(version,build)
657 # Close key
658 RegCloseKey(keyCurVer)
659 return release,version,csd,ptype
661 def _mac_ver_lookup(selectors,default=None):
663 from gestalt import gestalt
664 import MacOS
665 l = []
666 append = l.append
667 for selector in selectors:
668 try:
669 append(gestalt(selector))
670 except (RuntimeError, MacOS.Error):
671 append(default)
672 return l
674 def _bcd2str(bcd):
676 return hex(bcd)[2:]
678 def mac_ver(release='',versioninfo=('','',''),machine=''):
680 """ Get MacOS version information and return it as tuple (release,
681 versioninfo, machine) with versioninfo being a tuple (version,
682 dev_stage, non_release_version).
684 Entries which cannot be determined are set to the paramter values
685 which default to ''. All tuple entries are strings.
687 Thanks to Mark R. Levinson for mailing documentation links and
688 code examples for this function. Documentation for the
689 gestalt() API is available online at:
691 http://www.rgaros.nl/gestalt/
694 # Check whether the version info module is available
695 try:
696 import gestalt
697 import MacOS
698 except ImportError:
699 return release,versioninfo,machine
700 # Get the infos
701 sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa'))
702 # Decode the infos
703 if sysv:
704 major = (sysv & 0xFF00) >> 8
705 minor = (sysv & 0x00F0) >> 4
706 patch = (sysv & 0x000F)
708 if (major, minor) >= (10, 4):
709 # the 'sysv' gestald cannot return patchlevels
710 # higher than 9. Apple introduced 3 new
711 # gestalt codes in 10.4 to deal with this
712 # issue (needed because patch levels can
713 # run higher than 9, such as 10.4.11)
714 major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
715 release = '%i.%i.%i' %(major, minor, patch)
716 else:
717 release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
719 if sysu:
720 # NOTE: this block is left as documentation of the
721 # intention of this function, the 'sysu' gestalt is no
722 # longer available and there are no alternatives.
723 major = int((sysu & 0xFF000000L) >> 24)
724 minor = (sysu & 0x00F00000) >> 20
725 bugfix = (sysu & 0x000F0000) >> 16
726 stage = (sysu & 0x0000FF00) >> 8
727 nonrel = (sysu & 0x000000FF)
728 version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix)
729 nonrel = _bcd2str(nonrel)
730 stage = {0x20:'development',
731 0x40:'alpha',
732 0x60:'beta',
733 0x80:'final'}.get(stage,'')
734 versioninfo = (version,stage,nonrel)
737 if sysa:
738 machine = {0x1: '68k',
739 0x2: 'PowerPC',
740 0xa: 'i386'}.get(sysa,'')
741 return release,versioninfo,machine
743 def _java_getprop(name,default):
745 from java.lang import System
746 try:
747 value = System.getProperty(name)
748 if value is None:
749 return default
750 return value
751 except AttributeError:
752 return default
754 def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
756 """ Version interface for Jython.
758 Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
759 a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
760 tuple (os_name,os_version,os_arch).
762 Values which cannot be determined are set to the defaults
763 given as parameters (which all default to '').
766 # Import the needed APIs
767 try:
768 import java.lang
769 except ImportError:
770 return release,vendor,vminfo,osinfo
772 vendor = _java_getprop('java.vendor', vendor)
773 release = _java_getprop('java.version', release)
774 vm_name, vm_release, vm_vendor = vminfo
775 vm_name = _java_getprop('java.vm.name', vm_name)
776 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
777 vm_release = _java_getprop('java.vm.version', vm_release)
778 vminfo = vm_name, vm_release, vm_vendor
779 os_name, os_version, os_arch = osinfo
780 os_arch = _java_getprop('java.os.arch', os_arch)
781 os_name = _java_getprop('java.os.name', os_name)
782 os_version = _java_getprop('java.os.version', os_version)
783 osinfo = os_name, os_version, os_arch
785 return release, vendor, vminfo, osinfo
787 ### System name aliasing
789 def system_alias(system,release,version):
791 """ Returns (system,release,version) aliased to common
792 marketing names used for some systems.
794 It also does some reordering of the information in some cases
795 where it would otherwise cause confusion.
798 if system == 'Rhapsody':
799 # Apple's BSD derivative
800 # XXX How can we determine the marketing release number ?
801 return 'MacOS X Server',system+release,version
803 elif system == 'SunOS':
804 # Sun's OS
805 if release < '5':
806 # These releases use the old name SunOS
807 return system,release,version
808 # Modify release (marketing release = SunOS release - 3)
809 l = string.split(release,'.')
810 if l:
811 try:
812 major = int(l[0])
813 except ValueError:
814 pass
815 else:
816 major = major - 3
817 l[0] = str(major)
818 release = string.join(l,'.')
819 if release < '6':
820 system = 'Solaris'
821 else:
822 # XXX Whatever the new SunOS marketing name is...
823 system = 'Solaris'
825 elif system == 'IRIX64':
826 # IRIX reports IRIX64 on platforms with 64-bit support; yet it
827 # is really a version and not a different platform, since 32-bit
828 # apps are also supported..
829 system = 'IRIX'
830 if version:
831 version = version + ' (64bit)'
832 else:
833 version = '64bit'
835 elif system in ('win32','win16'):
836 # In case one of the other tricks
837 system = 'Windows'
839 return system,release,version
841 ### Various internal helpers
843 def _platform(*args):
845 """ Helper to format the platform string in a filename
846 compatible format e.g. "system-version-machine".
848 # Format the platform string
849 platform = string.join(
850 map(string.strip,
851 filter(len, args)),
852 '-')
854 # Cleanup some possible filename obstacles...
855 replace = string.replace
856 platform = replace(platform,' ','_')
857 platform = replace(platform,'/','-')
858 platform = replace(platform,'\\','-')
859 platform = replace(platform,':','-')
860 platform = replace(platform,';','-')
861 platform = replace(platform,'"','-')
862 platform = replace(platform,'(','-')
863 platform = replace(platform,')','-')
865 # No need to report 'unknown' information...
866 platform = replace(platform,'unknown','')
868 # Fold '--'s and remove trailing '-'
869 while 1:
870 cleaned = replace(platform,'--','-')
871 if cleaned == platform:
872 break
873 platform = cleaned
874 while platform[-1] == '-':
875 platform = platform[:-1]
877 return platform
879 def _node(default=''):
881 """ Helper to determine the node name of this machine.
883 try:
884 import socket
885 except ImportError:
886 # No sockets...
887 return default
888 try:
889 return socket.gethostname()
890 except socket.error:
891 # Still not working...
892 return default
894 # os.path.abspath is new in Python 1.5.2:
895 if not hasattr(os.path,'abspath'):
897 def _abspath(path,
899 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
900 normpath=os.path.normpath):
902 if not isabs(path):
903 path = join(getcwd(), path)
904 return normpath(path)
906 else:
908 _abspath = os.path.abspath
910 def _follow_symlinks(filepath):
912 """ In case filepath is a symlink, follow it until a
913 real file is reached.
915 filepath = _abspath(filepath)
916 while os.path.islink(filepath):
917 filepath = os.path.normpath(
918 os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
919 return filepath
921 def _syscmd_uname(option,default=''):
923 """ Interface to the system's uname command.
925 if sys.platform in ('dos','win32','win16','os2'):
926 # XXX Others too ?
927 return default
928 try:
929 f = os.popen('uname %s 2> /dev/null' % option)
930 except (AttributeError,os.error):
931 return default
932 output = string.strip(f.read())
933 rc = f.close()
934 if not output or rc:
935 return default
936 else:
937 return output
939 def _syscmd_file(target,default=''):
941 """ Interface to the system's file command.
943 The function uses the -b option of the file command to have it
944 ommit the filename in its output and if possible the -L option
945 to have the command follow symlinks. It returns default in
946 case the command should fail.
949 if sys.platform in ('dos','win32','win16','os2'):
950 # XXX Others too ?
951 return default
952 target = _follow_symlinks(target)
953 try:
954 f = os.popen('file "%s" 2> /dev/null' % target)
955 except (AttributeError,os.error):
956 return default
957 output = string.strip(f.read())
958 rc = f.close()
959 if not output or rc:
960 return default
961 else:
962 return output
964 ### Information about the used architecture
966 # Default values for architecture; non-empty strings override the
967 # defaults given as parameters
968 _default_architecture = {
969 'win32': ('','WindowsPE'),
970 'win16': ('','Windows'),
971 'dos': ('','MSDOS'),
974 _architecture_split = re.compile(r'[\s,]').split
976 def architecture(executable=sys.executable,bits='',linkage=''):
978 """ Queries the given executable (defaults to the Python interpreter
979 binary) for various architecture information.
981 Returns a tuple (bits,linkage) which contains information about
982 the bit architecture and the linkage format used for the
983 executable. Both values are returned as strings.
985 Values that cannot be determined are returned as given by the
986 parameter presets. If bits is given as '', the sizeof(pointer)
987 (or sizeof(long) on Python version < 1.5.2) is used as
988 indicator for the supported pointer size.
990 The function relies on the system's "file" command to do the
991 actual work. This is available on most if not all Unix
992 platforms. On some non-Unix platforms where the "file" command
993 does not exist and the executable is set to the Python interpreter
994 binary defaults from _default_architecture are used.
997 # Use the sizeof(pointer) as default number of bits if nothing
998 # else is given as default.
999 if not bits:
1000 import struct
1001 try:
1002 size = struct.calcsize('P')
1003 except struct.error:
1004 # Older installations can only query longs
1005 size = struct.calcsize('l')
1006 bits = str(size*8) + 'bit'
1008 # Get data from the 'file' system command
1009 if executable:
1010 output = _syscmd_file(executable, '')
1011 else:
1012 output = ''
1014 if not output and \
1015 executable == sys.executable:
1016 # "file" command did not return anything; we'll try to provide
1017 # some sensible defaults then...
1018 if _default_architecture.has_key(sys.platform):
1019 b,l = _default_architecture[sys.platform]
1020 if b:
1021 bits = b
1022 if l:
1023 linkage = l
1024 return bits,linkage
1026 # Split the output into a list of strings omitting the filename
1027 fileout = _architecture_split(output)[1:]
1029 if 'executable' not in fileout:
1030 # Format not supported
1031 return bits,linkage
1033 # Bits
1034 if '32-bit' in fileout:
1035 bits = '32bit'
1036 elif 'N32' in fileout:
1037 # On Irix only
1038 bits = 'n32bit'
1039 elif '64-bit' in fileout:
1040 bits = '64bit'
1042 # Linkage
1043 if 'ELF' in fileout:
1044 linkage = 'ELF'
1045 elif 'PE' in fileout:
1046 # E.g. Windows uses this format
1047 if 'Windows' in fileout:
1048 linkage = 'WindowsPE'
1049 else:
1050 linkage = 'PE'
1051 elif 'COFF' in fileout:
1052 linkage = 'COFF'
1053 elif 'MS-DOS' in fileout:
1054 linkage = 'MSDOS'
1055 else:
1056 # XXX the A.OUT format also falls under this class...
1057 pass
1059 return bits,linkage
1061 ### Portable uname() interface
1063 _uname_cache = None
1065 def uname():
1067 """ Fairly portable uname interface. Returns a tuple
1068 of strings (system,node,release,version,machine,processor)
1069 identifying the underlying platform.
1071 Note that unlike the os.uname function this also returns
1072 possible processor information as an additional tuple entry.
1074 Entries which cannot be determined are set to ''.
1077 global _uname_cache
1078 no_os_uname = 0
1080 if _uname_cache is not None:
1081 return _uname_cache
1083 processor = ''
1085 # Get some infos from the builtin os.uname API...
1086 try:
1087 system,node,release,version,machine = os.uname()
1088 except AttributeError:
1089 no_os_uname = 1
1091 if no_os_uname or not filter(None, (system, node, release, version, machine)):
1092 # Hmm, no there is either no uname or uname has returned
1093 #'unknowns'... we'll have to poke around the system then.
1094 if no_os_uname:
1095 system = sys.platform
1096 release = ''
1097 version = ''
1098 node = _node()
1099 machine = ''
1101 use_syscmd_ver = 01
1103 # Try win32_ver() on win32 platforms
1104 if system == 'win32':
1105 release,version,csd,ptype = win32_ver()
1106 if release and version:
1107 use_syscmd_ver = 0
1108 # Try to use the PROCESSOR_* environment variables
1109 # available on Win XP and later; see
1110 # http://support.microsoft.com/kb/888731 and
1111 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
1112 if not machine:
1113 machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
1114 if not processor:
1115 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
1117 # Try the 'ver' system command available on some
1118 # platforms
1119 if use_syscmd_ver:
1120 system,release,version = _syscmd_ver(system)
1121 # Normalize system to what win32_ver() normally returns
1122 # (_syscmd_ver() tends to return the vendor name as well)
1123 if system == 'Microsoft Windows':
1124 system = 'Windows'
1125 elif system == 'Microsoft' and release == 'Windows':
1126 # Under Windows Vista and Windows Server 2008,
1127 # Microsoft changed the output of the ver command. The
1128 # release is no longer printed. This causes the
1129 # system and release to be misidentified.
1130 system = 'Windows'
1131 if '6.0' == version[:3]:
1132 release = 'Vista'
1133 else:
1134 release = ''
1136 # In case we still don't know anything useful, we'll try to
1137 # help ourselves
1138 if system in ('win32','win16'):
1139 if not version:
1140 if system == 'win32':
1141 version = '32bit'
1142 else:
1143 version = '16bit'
1144 system = 'Windows'
1146 elif system[:4] == 'java':
1147 release,vendor,vminfo,osinfo = java_ver()
1148 system = 'Java'
1149 version = string.join(vminfo,', ')
1150 if not version:
1151 version = vendor
1153 elif os.name == 'mac':
1154 release,(version,stage,nonrel),machine = mac_ver()
1155 system = 'MacOS'
1157 # System specific extensions
1158 if system == 'OpenVMS':
1159 # OpenVMS seems to have release and version mixed up
1160 if not release or release == '0':
1161 release = version
1162 version = ''
1163 # Get processor information
1164 try:
1165 import vms_lib
1166 except ImportError:
1167 pass
1168 else:
1169 csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
1170 if (cpu_number >= 128):
1171 processor = 'Alpha'
1172 else:
1173 processor = 'VAX'
1174 if not processor:
1175 # Get processor information from the uname system command
1176 processor = _syscmd_uname('-p','')
1178 #If any unknowns still exist, replace them with ''s, which are more portable
1179 if system == 'unknown':
1180 system = ''
1181 if node == 'unknown':
1182 node = ''
1183 if release == 'unknown':
1184 release = ''
1185 if version == 'unknown':
1186 version = ''
1187 if machine == 'unknown':
1188 machine = ''
1189 if processor == 'unknown':
1190 processor = ''
1192 # normalize name
1193 if system == 'Microsoft' and release == 'Windows':
1194 system = 'Windows'
1195 release = 'Vista'
1197 _uname_cache = system,node,release,version,machine,processor
1198 return _uname_cache
1200 ### Direct interfaces to some of the uname() return values
1202 def system():
1204 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
1206 An empty string is returned if the value cannot be determined.
1209 return uname()[0]
1211 def node():
1213 """ Returns the computer's network name (which may not be fully
1214 qualified)
1216 An empty string is returned if the value cannot be determined.
1219 return uname()[1]
1221 def release():
1223 """ Returns the system's release, e.g. '2.2.0' or 'NT'
1225 An empty string is returned if the value cannot be determined.
1228 return uname()[2]
1230 def version():
1232 """ Returns the system's release version, e.g. '#3 on degas'
1234 An empty string is returned if the value cannot be determined.
1237 return uname()[3]
1239 def machine():
1241 """ Returns the machine type, e.g. 'i386'
1243 An empty string is returned if the value cannot be determined.
1246 return uname()[4]
1248 def processor():
1250 """ Returns the (true) processor name, e.g. 'amdk6'
1252 An empty string is returned if the value cannot be
1253 determined. Note that many platforms do not provide this
1254 information or simply return the same value as for machine(),
1255 e.g. NetBSD does this.
1258 return uname()[5]
1260 ### Various APIs for extracting information from sys.version
1262 _sys_version_parser = re.compile(
1263 r'([\w.+]+)\s*'
1264 '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1265 '\[([^\]]+)\]?')
1267 _jython_sys_version_parser = re.compile(
1268 r'([\d\.]+)')
1270 _ironpython_sys_version_parser = re.compile(
1271 r'IronPython\s*'
1272 '([\d\.]+)'
1273 '(?: \(([\d\.]+)\))?'
1274 ' on (.NET [\d\.]+)')
1276 _sys_version_cache = {}
1278 def _sys_version(sys_version=None):
1280 """ Returns a parsed version of Python's sys.version as tuple
1281 (name, version, branch, revision, buildno, builddate, compiler)
1282 referring to the Python implementation name, version, branch,
1283 revision, build number, build date/time as string and the compiler
1284 identification string.
1286 Note that unlike the Python sys.version, the returned value
1287 for the Python version will always include the patchlevel (it
1288 defaults to '.0').
1290 The function returns empty strings for tuple entries that
1291 cannot be determined.
1293 sys_version may be given to parse an alternative version
1294 string, e.g. if the version was read from a different Python
1295 interpreter.
1298 # Get the Python version
1299 if sys_version is None:
1300 sys_version = sys.version
1302 # Try the cache first
1303 result = _sys_version_cache.get(sys_version, None)
1304 if result is not None:
1305 return result
1307 # Parse it
1308 if sys_version[:10] == 'IronPython':
1309 # IronPython
1310 name = 'IronPython'
1311 match = _ironpython_sys_version_parser.match(sys_version)
1312 if match is None:
1313 raise ValueError(
1314 'failed to parse IronPython sys.version: %s' %
1315 repr(sys_version))
1316 version, alt_version, compiler = match.groups()
1317 branch = ''
1318 revision = ''
1319 buildno = ''
1320 builddate = ''
1322 elif sys.platform[:4] == 'java':
1323 # Jython
1324 name = 'Jython'
1325 match = _jython_sys_version_parser.match(sys_version)
1326 if match is None:
1327 raise ValueError(
1328 'failed to parse Jython sys.version: %s' %
1329 repr(sys_version))
1330 version, = match.groups()
1331 branch = ''
1332 revision = ''
1333 compiler = sys.platform
1334 buildno = ''
1335 builddate = ''
1337 else:
1338 # CPython
1339 match = _sys_version_parser.match(sys_version)
1340 if match is None:
1341 raise ValueError(
1342 'failed to parse CPython sys.version: %s' %
1343 repr(sys_version))
1344 version, buildno, builddate, buildtime, compiler = \
1345 match.groups()
1346 if hasattr(sys, 'subversion'):
1347 # sys.subversion was added in Python 2.5
1348 name, branch, revision = sys.subversion
1349 else:
1350 name = 'CPython'
1351 branch = ''
1352 revision = ''
1353 builddate = builddate + ' ' + buildtime
1355 # Add the patchlevel version if missing
1356 l = string.split(version, '.')
1357 if len(l) == 2:
1358 l.append('0')
1359 version = string.join(l, '.')
1361 # Build and cache the result
1362 result = (name, version, branch, revision, buildno, builddate, compiler)
1363 _sys_version_cache[sys_version] = result
1364 return result
1366 def python_implementation():
1368 """ Returns a string identifying the Python implementation.
1370 Currently, the following implementations are identified:
1371 'CPython' (C implementation of Python),
1372 'IronPython' (.NET implementation of Python),
1373 'Jython' (Java implementation of Python).
1376 return _sys_version()[0]
1378 def python_version():
1380 """ Returns the Python version as string 'major.minor.patchlevel'
1382 Note that unlike the Python sys.version, the returned value
1383 will always include the patchlevel (it defaults to 0).
1386 if hasattr(sys, 'version_info'):
1387 return '%i.%i.%i' % sys.version_info[:3]
1388 return _sys_version()[1]
1390 def python_version_tuple():
1392 """ Returns the Python version as tuple (major, minor, patchlevel)
1393 of strings.
1395 Note that unlike the Python sys.version, the returned value
1396 will always include the patchlevel (it defaults to 0).
1399 if hasattr(sys, 'version_info'):
1400 return sys.version_info[:3]
1401 return tuple(string.split(_sys_version()[1], '.'))
1403 def python_branch():
1405 """ Returns a string identifying the Python implementation
1406 branch.
1408 For CPython this is the Subversion branch from which the
1409 Python binary was built.
1411 If not available, an empty string is returned.
1415 return _sys_version()[2]
1417 def python_revision():
1419 """ Returns a string identifying the Python implementation
1420 revision.
1422 For CPython this is the Subversion revision from which the
1423 Python binary was built.
1425 If not available, an empty string is returned.
1428 return _sys_version()[3]
1430 def python_build():
1432 """ Returns a tuple (buildno, builddate) stating the Python
1433 build number and date as strings.
1436 return _sys_version()[4:6]
1438 def python_compiler():
1440 """ Returns a string identifying the compiler used for compiling
1441 Python.
1444 return _sys_version()[6]
1446 ### The Opus Magnum of platform strings :-)
1448 _platform_cache = {}
1450 def platform(aliased=0, terse=0):
1452 """ Returns a single string identifying the underlying platform
1453 with as much useful information as possible (but no more :).
1455 The output is intended to be human readable rather than
1456 machine parseable. It may look different on different
1457 platforms and this is intended.
1459 If "aliased" is true, the function will use aliases for
1460 various platforms that report system names which differ from
1461 their common names, e.g. SunOS will be reported as
1462 Solaris. The system_alias() function is used to implement
1463 this.
1465 Setting terse to true causes the function to return only the
1466 absolute minimum information needed to identify the platform.
1469 result = _platform_cache.get((aliased, terse), None)
1470 if result is not None:
1471 return result
1473 # Get uname information and then apply platform specific cosmetics
1474 # to it...
1475 system,node,release,version,machine,processor = uname()
1476 if machine == processor:
1477 processor = ''
1478 if aliased:
1479 system,release,version = system_alias(system,release,version)
1481 if system == 'Windows':
1482 # MS platforms
1483 rel,vers,csd,ptype = win32_ver(version)
1484 if terse:
1485 platform = _platform(system,release)
1486 else:
1487 platform = _platform(system,release,version,csd)
1489 elif system in ('Linux',):
1490 # Linux based systems
1491 distname,distversion,distid = dist('')
1492 if distname and not terse:
1493 platform = _platform(system,release,machine,processor,
1494 'with',
1495 distname,distversion,distid)
1496 else:
1497 # If the distribution name is unknown check for libc vs. glibc
1498 libcname,libcversion = libc_ver(sys.executable)
1499 platform = _platform(system,release,machine,processor,
1500 'with',
1501 libcname+libcversion)
1502 elif system == 'Java':
1503 # Java platforms
1504 r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
1505 if terse or not os_name:
1506 platform = _platform(system,release,version)
1507 else:
1508 platform = _platform(system,release,version,
1509 'on',
1510 os_name,os_version,os_arch)
1512 elif system == 'MacOS':
1513 # MacOS platforms
1514 if terse:
1515 platform = _platform(system,release)
1516 else:
1517 platform = _platform(system,release,machine)
1519 else:
1520 # Generic handler
1521 if terse:
1522 platform = _platform(system,release)
1523 else:
1524 bits,linkage = architecture(sys.executable)
1525 platform = _platform(system,release,machine,processor,bits,linkage)
1527 _platform_cache[(aliased, terse)] = platform
1528 return platform
1530 ### Command line interface
1532 if __name__ == '__main__':
1533 # Default is to print the aliased verbose platform string
1534 terse = ('terse' in sys.argv or '--terse' in sys.argv)
1535 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
1536 print platform(aliased,terse)
1537 sys.exit(0)