blob: 764c0117a739a02280d7b8a59d3641f641171275 [file] [log] [blame]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001#!/usr/bin/env python
2
3""" This module tries to retrieve as much platform identifying data as
4 possible. It makes this information available via function APIs.
5
6 If called from the command line, it prints the platform
7 information concatenated as single string to stdout. The output
8 format is useable as part of a filename.
9
10"""
11# This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
12# If you find problems, please submit bug reports/patches via the
13# Python SourceForge Project Page and assign them to "lemburg".
14#
15# Note: Please keep this module compatible to Python 1.5.2.
16#
17# Still needed:
18# * more support for WinCE
19# * support for MS-DOS (PythonDX ?)
20# * support for Amiga and other still unsupported platforms running Python
21# * support for additional Linux distributions
22#
23# Many thanks to all those who helped adding platform specific
24# checks (in no particular order):
25#
26# Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
27# Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
28# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
29# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
30# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
31# Colin Kong, Trent Mick
32#
33# History:
34# 1.0.0 - reformatted a bit and checked into Python CVS
35# 0.8.0 - added sys.version parser and various new access
36# APIs (python_version(), python_compiler(), etc.)
37# 0.7.2 - fixed architecture() to use sizeof(pointer) where available
38# 0.7.1 - added support for Caldera OpenLinux
39# 0.7.0 - some fixes for WinCE; untabified the source file
40# 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
41# vms_lib.getsyi() configured
42# 0.6.1 - added code to prevent 'uname -p' on platforms which are
43# known not to support it
44# 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
45# did some cleanup of the interfaces - some APIs have changed
46# 0.5.5 - fixed another type in the MacOS code... should have
47# used more coffee today ;-)
48# 0.5.4 - fixed a few typos in the MacOS code
49# 0.5.3 - added experimental MacOS support; added better popen()
50# workarounds in _syscmd_ver() -- still not 100% elegant
51# though
52# 0.5.2 - fixed uname() to return '' instead of 'unknown' in all
53# return values (the system uname command tends to return
54# 'unknown' instead of just leaving the field emtpy)
55# 0.5.1 - included code for slackware dist; added exception handlers
56# to cover up situations where platforms don't have os.popen
57# (e.g. Mac) or fail on socket.gethostname(); fixed libc
58# detection RE
59# 0.5.0 - changed the API names referring to system commands to *syscmd*;
60# added java_ver(); made syscmd_ver() a private
61# API (was system_ver() in previous versions) -- use uname()
62# instead; extended the win32_ver() to also return processor
63# type information
64# 0.4.0 - added win32_ver() and modified the platform() output for WinXX
65# 0.3.4 - fixed a bug in _follow_symlinks()
66# 0.3.3 - fixed popen() and "file" command invokation bugs
67# 0.3.2 - added architecture() API and support for it in platform()
68# 0.3.1 - fixed syscmd_ver() RE to support Windows NT
69# 0.3.0 - added system alias support
70# 0.2.3 - removed 'wince' again... oh well.
71# 0.2.2 - added 'wince' to syscmd_ver() supported platforms
72# 0.2.1 - added cache logic and changed the platform string format
73# 0.2.0 - changed the API to use functions instead of module globals
74# since some action take too long to be run on module import
75# 0.1.0 - first release
76#
77# You can always get the latest version of this module at:
78#
79# http://www.egenix.com/files/python/platform.py
80#
81# If that URL should fail, try contacting the author.
82
83__copyright__ = """
84 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
85 Copyright (c) 2000-2003, eGenix.com Software GmbH; mailto:info@egenix.com
86
87 Permission to use, copy, modify, and distribute this software and its
88 documentation for any purpose and without fee or royalty is hereby granted,
89 provided that the above copyright notice appear in all copies and that
90 both that copyright notice and this permission notice appear in
91 supporting documentation or portions thereof, including modifications,
92 that you make.
93
94 EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
95 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
96 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
97 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
98 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
99 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
100 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
101
102"""
103
104__version__ = '1.0.0'
105
106import sys,string,os,re
107
108### Platform specific APIs
109
110def libc_ver(executable=sys.executable,lib='',version='',
111
112 chunksize=2048,
113 libc_search=re.compile(r'(__libc_init)'
114 '|'
115 '(GLIBC_([0-9.]+))'
116 '|'
117 '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)'
118 )
119 ):
120
121 """ Tries to determine the libc version against which the
122 file executable (defaults to the Python interpreter) is linked.
123
124 Returns a tuple of strings (lib,version) which default to the
125 given parameters in case the lookup fails.
126
127 Note that the function has intimate knowledge of how different
128 libc versions add symbols to the executable is probably only
129 useable for executables compiled using gcc.
130
131 The file is read and scanned in chunks of chunksize bytes.
132
133 """
134 f = open(executable,'rb')
135 binary = f.read(chunksize)
136 pos = 0
137 while 1:
138 m = libc_search.search(binary,pos)
139 if not m:
140 binary = f.read(chunksize)
141 if not binary:
142 break
143 pos = 0
144 continue
145 libcinit,glibc,glibcversion,so,threads,soversion = m.groups()
146 if libcinit and not lib:
147 lib = 'libc'
148 elif glibc:
149 if lib != 'glibc':
150 lib = 'glibc'
151 version = glibcversion
152 elif glibcversion > version:
153 version = glibcversion
154 elif so:
155 if lib != 'glibc':
156 lib = 'libc'
157 if soversion > version:
158 version = soversion
159 if threads and version[-len(threads):] != threads:
160 version = version + threads
161 pos = m.end()
162 f.close()
163 return lib,version
164
165def _dist_try_harder(distname,version,id):
166
167 """ Tries some special tricks to get the distribution
168 information in case the default method fails.
169
170 Currently supports older SuSE Linux, Caldera OpenLinux and
171 Slackware Linux distributions.
172
173 """
174 if os.path.exists('/var/adm/inst-log/info'):
175 # SuSE Linux stores distribution information in that file
176 info = open('/var/adm/inst-log/info').readlines()
177 distname = 'SuSE'
178 for line in info:
179 tv = string.split(line)
180 if len(tv) == 2:
181 tag,value = tv
182 else:
183 continue
184 if tag == 'MIN_DIST_VERSION':
185 version = string.strip(value)
186 elif tag == 'DIST_IDENT':
187 values = string.split(value,'-')
188 id = values[2]
189 return distname,version,id
190
191 if os.path.exists('/etc/.installed'):
192 # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
193 info = open('/etc/.installed').readlines()
194 for line in info:
195 pkg = string.split(line,'-')
196 if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
197 # XXX does Caldera support non Intel platforms ? If yes,
198 # where can we find the needed id ?
199 return 'OpenLinux',pkg[1],id
200
201 if os.path.isdir('/usr/lib/setup'):
202 # Check for slackware verson tag file (thanks to Greg Andruk)
203 verfiles = os.listdir('/usr/lib/setup')
204 for n in range(len(verfiles)-1, -1, -1):
205 if verfiles[n][:14] != 'slack-version-':
206 del verfiles[n]
207 if verfiles:
208 verfiles.sort()
209 distname = 'slackware'
210 version = verfiles[-1][14:]
211 return distname,version,id
212
213 return distname,version,id
214
215def dist(distname='',version='',id='',
216
217 supported_dists=('SuSE','debian','redhat','mandrake'),
218 release_filename=re.compile(r'(\w+)[-_](release|version)'),
219 release_version=re.compile(r'([\d.]+)[^(]*(?:\((.+)\))?')):
220
221 """ Tries to determine the name of the OS distribution name
222
223 The function first looks for a distribution release file in
224 /etc and then reverts to _dist_try_harder() in case no
225 suitable files are found.
226
227 Returns a tuple distname,version,id which default to the
228 args given as parameters.
229
230 """
231 try:
232 etc = os.listdir('/etc')
233 except os.error:
234 # Probably not a Unix system
235 return distname,version,id
236 for file in etc:
237 m = release_filename.match(file)
238 if m:
239 _distname,dummy = m.groups()
240 if _distname in supported_dists:
241 distname = _distname
242 break
243 else:
244 return _dist_try_harder(distname,version,id)
245 f = open('/etc/'+file,'r')
246 firstline = f.readline()
247 f.close()
248 m = release_version.search(firstline)
249 if m:
250 _version,_id = m.groups()
251 if _version:
252 version = _version
253 if _id:
254 id = _id
255 else:
256 # Unkown format... take the first two words
257 l = string.split(string.strip(firstline))
258 if l:
259 version = l[0]
260 if len(l) > 1:
261 id = l[1]
262 return distname,version,id
263
264class _popen:
265
266 """ Fairly portable (alternative) popen implementation.
267
268 This is mostly needed in case os.popen() is not available, or
269 doesn't work as advertised, e.g. in Win9X GUI programs like
270 PythonWin or IDLE.
271
272 Writing to the pipe is currently not supported.
273
274 """
275 tmpfile = ''
276 pipe = None
277 bufsize = None
278 mode = 'r'
279
280 def __init__(self,cmd,mode='r',bufsize=None):
281
282 if mode != 'r':
283 raise ValueError,'popen()-emulation only supports read mode'
284 import tempfile
285 self.tmpfile = tmpfile = tempfile.mktemp()
286 os.system(cmd + ' > %s' % tmpfile)
287 self.pipe = open(tmpfile,'rb')
288 self.bufsize = bufsize
289 self.mode = mode
290
291 def read(self):
292
293 return self.pipe.read()
294
295 def readlines(self):
296
297 if self.bufsize is not None:
298 return self.pipe.readlines()
299
300 def close(self,
301
302 remove=os.unlink,error=os.error):
303
304 if self.pipe:
305 rc = self.pipe.close()
306 else:
307 rc = 255
308 if self.tmpfile:
309 try:
310 remove(self.tmpfile)
311 except error:
312 pass
313 return rc
314
315 # Alias
316 __del__ = close
317
318def popen(cmd, mode='r', bufsize=None):
319
320 """ Portable popen() interface.
321 """
322 # Find a working popen implementation preferring win32pipe.popen
323 # over os.popen over _popen
324 popen = None
325 if os.environ.get('OS','') == 'Windows_NT':
326 # On NT win32pipe should work; on Win9x it hangs due to bugs
327 # in the MS C lib (see MS KnowledgeBase article Q150956)
328 try:
329 import win32pipe
330 except ImportError:
331 pass
332 else:
333 popen = win32pipe.popen
334 if popen is None:
335 if hasattr(os,'popen'):
336 popen = os.popen
337 # Check whether it works... it doesn't in GUI programs
338 # on Windows platforms
339 if sys.platform == 'win32': # XXX Others too ?
340 try:
341 popen('')
342 except os.error:
343 popen = _popen
344 else:
345 popen = _popen
346 if bufsize is None:
347 return popen(cmd,mode)
348 else:
349 return popen(cmd,mode,bufsize)
350
351def _norm_version(version,build=''):
352
353 """ Normalize the version and build strings and return a sinlge
354 vesion string using the format major.minor.build (or patchlevel).
355 """
356 l = string.split(version,'.')
357 if build:
358 l.append(build)
359 try:
360 ints = map(int,l)
361 except ValueError:
362 strings = l
363 else:
364 strings = map(str,ints)
365 version = string.join(strings[:3],'.')
366 return version
367
368def _syscmd_ver(system='',release='',version='',
369
370 supported_platforms=('win32','win16','dos','os2'),
371 ver_output=re.compile(r'(?:([\w ]+) ([\w.]+) '
372 '.*'
373 'Version ([\d.]+))')):
374
375 """ Tries to figure out the OS version used and returns
376 a tuple (system,release,version).
377
378 It uses the "ver" shell command for this which is known
379 to exists on Windows, DOS and OS/2. XXX Others too ?
380
381 In case this fails, the given parameters are used as
382 defaults.
383
384 """
385 if sys.platform not in supported_platforms:
386 return system,release,version
387
388 # Try some common cmd strings
389 for cmd in ('ver','command /c ver','cmd /c ver'):
390 try:
391 pipe = popen(cmd)
392 info = pipe.read()
393 if pipe.close():
394 raise os.error,'command failed'
395 # XXX How can I supress shell errors from being written
396 # to stderr ?
397 except os.error,why:
398 #print 'Command %s failed: %s' % (cmd,why)
399 continue
400 except IOError,why:
401 #print 'Command %s failed: %s' % (cmd,why)
402 continue
403 else:
404 break
405 else:
406 return system,release,version
407
408 # Parse the output
409 info = string.strip(info)
410 m = ver_output.match(info)
411 if m:
412 system,release,version = m.groups()
413 # Strip trailing dots from version and release
414 if release[-1] == '.':
415 release = release[:-1]
416 if version[-1] == '.':
417 version = version[:-1]
418 # Normalize the version and build strings (eliminating additional
419 # zeros)
420 version = _norm_version(version)
421 return system,release,version
422
423def _win32_getvalue(key,name,default=''):
424
425 """ Read a value for name from the registry key.
426
427 In case this fails, default is returned.
428
429 """
430 from win32api import RegQueryValueEx
431 try:
432 return RegQueryValueEx(key,name)
433 except:
434 return default
435
436def win32_ver(release='',version='',csd='',ptype=''):
437
438 """ Get additional version information from the Windows Registry
439 and return a tuple (version,csd,ptype) referring to version
440 number, CSD level and OS type (multi/single
441 processor).
442
443 As a hint: ptype returns 'Uniprocessor Free' on single
444 processor NT machines and 'Multiprocessor Free' on multi
445 processor machines. The 'Free' refers to the OS version being
446 free of debugging code. It could also state 'Checked' which
447 means the OS version uses debugging code, i.e. code that
448 checks arguments, ranges, etc. (Thomas Heller).
449
450 Note: this functions only works if Mark Hammond's win32
451 package is installed and obviously only runs on Win32
452 compatible platforms.
453
454 """
455 # XXX Is there any way to find out the processor type on WinXX ?
456 # XXX Is win32 available on Windows CE ?
457 # Adapted from code posted by Karl Putland to comp.lang.python.
458
459 # Import the needed APIs
460 try:
461 import win32api
462 except ImportError:
463 return release,version,csd,ptype
464 from win32api import RegQueryValueEx,RegOpenKeyEx,RegCloseKey,GetVersionEx
465 from win32con import HKEY_LOCAL_MACHINE,VER_PLATFORM_WIN32_NT,\
466 VER_PLATFORM_WIN32_WINDOWS
467
468 # Find out the registry key and some general version infos
469 maj,min,buildno,plat,csd = GetVersionEx()
470 version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
471 if csd[:13] == 'Service Pack ':
472 csd = 'SP' + csd[13:]
473 if plat == VER_PLATFORM_WIN32_WINDOWS:
474 regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
475 # Try to guess the release name
476 if maj == 4:
477 if min == 0:
478 release = '95'
479 else:
480 release = '98'
481 elif maj == 5:
482 release = '2000'
483 elif plat == VER_PLATFORM_WIN32_NT:
484 regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
485 if maj <= 4:
486 release = 'NT'
487 elif maj == 5:
488 release = '2000'
489 else:
490 if not release:
491 # E.g. Win3.1 with win32s
492 release = '%i.%i' % (maj,min)
493 return release,version,csd,ptype
494
495 # Open the registry key
496 try:
497 keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE,regkey)
498 # Get a value to make sure the key exists...
499 RegQueryValueEx(keyCurVer,'SystemRoot')
500 except:
501 return release,version,csd,ptype
502
503 # Parse values
504 #subversion = _win32_getvalue(keyCurVer,
505 # 'SubVersionNumber',
506 # ('',1))[0]
507 #if subversion:
508 # release = release + subversion # 95a, 95b, etc.
509 build = _win32_getvalue(keyCurVer,
510 'CurrentBuildNumber',
511 ('',1))[0]
512 ptype = _win32_getvalue(keyCurVer,
513 'CurrentType',
514 (ptype,1))[0]
515
516 # Normalize version
517 version = _norm_version(version,build)
518
519 # Close key
520 RegCloseKey(keyCurVer)
521 return release,version,csd,ptype
522
523def _mac_ver_lookup(selectors,default=None):
524
525 from gestalt import gestalt
526 l = []
527 append = l.append
528 for selector in selectors:
529 try:
530 append(gestalt(selector))
531 except RuntimeError:
532 append(default)
533 return l
534
535def _bcd2str(bcd):
536
537 return hex(bcd)[2:]
538
539def mac_ver(release='',versioninfo=('','',''),machine=''):
540
541 """ Get MacOS version information and return it as tuple (release,
542 versioninfo, machine) with versioninfo being a tuple (version,
543 dev_stage, non_release_version).
544
545 Entries which cannot be determined are set to ''. All tuple
546 entries are strings.
547
548 Thanks to Mark R. Levinson for mailing documentation links and
549 code examples for this function. Documentation for the
550 gestalt() API is available online at:
551
552 http://www.rgaros.nl/gestalt/
553
554 """
555 # Check whether the version info module is available
556 try:
557 import gestalt
558 except ImportError:
559 return release,versioninfo,machine
560 # Get the infos
561 sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa'))
562 # Decode the infos
563 if sysv:
564 major = (sysv & 0xFF00) >> 8
565 minor = (sysv & 0x00F0) >> 4
566 patch = (sysv & 0x000F)
567 release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
568 if sysu:
569 major = int((sysu & 0xFF000000L) >> 24)
570 minor = (sysu & 0x00F00000) >> 20
571 bugfix = (sysu & 0x000F0000) >> 16
572 stage = (sysu & 0x0000FF00) >> 8
573 nonrel = (sysu & 0x000000FF)
574 version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix)
575 nonrel = _bcd2str(nonrel)
576 stage = {0x20:'development',
577 0x40:'alpha',
578 0x60:'beta',
579 0x80:'final'}.get(stage,'')
580 versioninfo = (version,stage,nonrel)
581 if sysa:
582 machine = {0x1: '68k',
583 0x2: 'PowerPC'}.get(sysa,'')
584 return release,versioninfo,machine
585
586def _java_getprop(self,name,default):
587
588 from java.lang import System
589 try:
590 return System.getProperty(name)
591 except:
592 return default
593
594def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
595
596 """ Version interface for JPython.
597
598 Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
599 a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
600 tuple (os_name,os_version,os_arch).
601
602 Values which cannot be determined are set to the defaults
603 given as parameters (which all default to '').
604
605 """
606 # Import the needed APIs
607 try:
608 import java.lang
609 except ImportError:
610 return release,vendor,vminfo,osinfo
611
612 vendor = _java_getprop('java.vendor',vendor)
613 release = _java_getprop('java.version',release)
614 vm_name,vm_release,vm_vendor = vminfo
615 vm_name = _java_getprop('java.vm.name',vm_name)
616 vm_vendor = _java_getprop('java.vm.vendor',vm_vendor)
617 vm_release = _java_getprop('java.vm.version',vm_release)
618 vminfo = vm_name,vm_release,vm_vendor
619 os_name,os_version,os_arch = osinfo
620 os_arch = _java_getprop('java.os.arch',os_arch)
621 os_name = _java_getprop('java.os.name',os_name)
622 os_version = _java_getprop('java.os.version',os_version)
623 osinfo = os_name,os_version,os_arch
624
625 return release,vendor,vminfo,osinfo
626
627### System name aliasing
628
629def system_alias(system,release,version):
630
631 """ Returns (system,release,version) aliased to common
632 marketing names used for some systems.
633
634 It also does some reordering of the information in some cases
635 where it would otherwise cause confusion.
636
637 """
638 if system == 'Rhapsody':
639 # Apple's BSD derivative
640 # XXX How can we determine the marketing release number ?
641 return 'MacOS X Server',system+release,version
642
643 elif system == 'SunOS':
644 # Sun's OS
645 if release < '5':
646 # These releases use the old name SunOS
647 return system,release,version
648 # Modify release (marketing release = SunOS release - 3)
649 l = string.split(release,'.')
650 if l:
651 try:
652 major = int(l[0])
653 except ValueError:
654 pass
655 else:
656 major = major - 3
657 l[0] = str(major)
658 release = string.join(l,'.')
659 if release < '6':
660 system = 'Solaris'
661 else:
662 # XXX Whatever the new SunOS marketing name is...
663 system = 'Solaris'
664
665 elif system == 'IRIX64':
666 # IRIX reports IRIX64 on platforms with 64-bit support; yet it
667 # is really a version and not a different platform, since 32-bit
668 # apps are also supported..
669 system = 'IRIX'
670 if version:
671 version = version + ' (64bit)'
672 else:
673 version = '64bit'
674
675 elif system in ('win32','win16'):
676 # In case one of the other tricks
677 system = 'Windows'
678
679 return system,release,version
680
681### Various internal helpers
682
683def _platform(*args):
684
685 """ Helper to format the platform string in a filename
686 compatible format e.g. "system-version-machine".
687 """
688 # Format the platform string
689 platform = string.join(
690 map(string.strip,
691 filter(len,args)),
692 '-')
693
694 # Cleanup some possible filename obstacles...
695 replace = string.replace
696 platform = replace(platform,' ','_')
697 platform = replace(platform,'/','-')
698 platform = replace(platform,'\\','-')
699 platform = replace(platform,':','-')
700 platform = replace(platform,';','-')
701 platform = replace(platform,'"','-')
702 platform = replace(platform,'(','-')
703 platform = replace(platform,')','-')
704
705 # No need to report 'unknown' information...
706 platform = replace(platform,'unknown','')
707
708 # Fold '--'s and remove trailing '-'
709 while 1:
710 cleaned = replace(platform,'--','-')
711 if cleaned == platform:
712 break
713 platform = cleaned
714 while platform[-1] == '-':
715 platform = platform[:-1]
716
717 return platform
718
719def _node(default=''):
720
721 """ Helper to determine the node name of this machine.
722 """
723 try:
724 import socket
725 except ImportError:
726 # No sockets...
727 return default
728 try:
729 return socket.gethostname()
730 except socket.error:
731 # Still not working...
732 return default
733
734# os.path.abspath is new in Python 1.5.2:
735if not hasattr(os.path,'abspath'):
736
737 def _abspath(path,
738
739 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
740 normpath=os.path.normpath):
741
742 if not isabs(path):
743 path = join(getcwd(), path)
744 return normpath(path)
745
746else:
747
748 _abspath = os.path.abspath
749
750def _follow_symlinks(filepath):
751
752 """ In case filepath is a symlink, follow it until a
753 real file is reached.
754 """
755 filepath = _abspath(filepath)
756 while os.path.islink(filepath):
757 filepath = os.path.normpath(
758 os.path.join(filepath,os.readlink(filepath)))
759 return filepath
760
761def _syscmd_uname(option,default=''):
762
763 """ Interface to the system's uname command.
764 """
765 if sys.platform in ('dos','win32','win16','os2'):
766 # XXX Others too ?
767 return default
768 try:
769 f = os.popen('uname %s 2> /dev/null' % option)
770 except (AttributeError,os.error):
771 return default
772 output = string.strip(f.read())
773 rc = f.close()
774 if not output or rc:
775 return default
776 else:
777 return output
778
779def _syscmd_file(target,default=''):
780
781 """ Interface to the system's file command.
782
783 The function uses the -b option of the file command to have it
784 ommit the filename in its output and if possible the -L option
785 to have the command follow symlinks. It returns default in
786 case the command should fail.
787
788 """
789 target = _follow_symlinks(target)
790 try:
791 f = os.popen('file %s 2> /dev/null' % target)
792 except (AttributeError,os.error):
793 return default
794 output = string.strip(f.read())
795 rc = f.close()
796 if not output or rc:
797 return default
798 else:
799 return output
800
801### Information about the used architecture
802
803# Default values for architecture; non-empty strings override the
804# defaults given as parameters
805_default_architecture = {
806 'win32': ('','WindowsPE'),
807 'win16': ('','Windows'),
808 'dos': ('','MSDOS'),
809}
810
811def architecture(executable=sys.executable,bits='',linkage='',
812
813 split=re.compile(r'[\s,]').split):
814
815 """ Queries the given executable (defaults to the Python interpreter
816 binary) for various architecture informations.
817
818 Returns a tuple (bits,linkage) which contain information about
819 the bit architecture and the linkage format used for the
820 executable. Both values are returned as strings.
821
822 Values that cannot be determined are returned as given by the
823 parameter presets. If bits is given as '', the sizeof(pointer)
824 (or sizeof(long) on Python version < 1.5.2) is used as
825 indicator for the supported pointer size.
826
827 The function relies on the system's "file" command to do the
828 actual work. This is available on most if not all Unix
829 platforms. On some non-Unix platforms and then only if the
830 executable points to the Python interpreter defaults from
831 _default_architecture are used.
832
833 """
834 # Use the sizeof(pointer) as default number of bits if nothing
835 # else is given as default.
836 if not bits:
837 import struct
838 try:
839 size = struct.calcsize('P')
840 except struct.error:
841 # Older installations can only query longs
842 size = struct.calcsize('l')
843 bits = str(size*8) + 'bit'
844
845 # Get data from the 'file' system command
846 output = _syscmd_file(executable,'')
847
848 if not output and \
849 executable == sys.executable:
850 # "file" command did not return anything; we'll try to provide
851 # some sensible defaults then...
852 if _default_architecture.has_key(sys.platform):
853 b,l = _default_architecture[sys.platform]
854 if b:
855 bits = b
856 if l:
857 linkage = l
858 return bits,linkage
859
860 # Split the output into a list of strings omitting the filename
861 fileout = split(output)[1:]
862
863 if 'executable' not in fileout:
864 # Format not supported
865 return bits,linkage
866
867 # Bits
868 if '32-bit' in fileout:
869 bits = '32bit'
870 elif 'N32' in fileout:
871 # On Irix only
872 bits = 'n32bit'
873 elif '64-bit' in fileout:
874 bits = '64bit'
875
876 # Linkage
877 if 'ELF' in fileout:
878 linkage = 'ELF'
879 elif 'PE' in fileout:
880 # E.g. Windows uses this format
881 if 'Windows' in fileout:
882 linkage = 'WindowsPE'
883 else:
884 linkage = 'PE'
885 elif 'COFF' in fileout:
886 linkage = 'COFF'
887 elif 'MS-DOS' in fileout:
888 linkage = 'MSDOS'
889 else:
890 # XXX the A.OUT format also falls under this class...
891 pass
892
893 return bits,linkage
894
895### Portable uname() interface
896
897_uname_cache = None
898
899def uname():
900
901 """ Fairly portable uname interface. Returns a tuple
902 of strings (system,node,release,version,machine,processor)
903 identifying the underlying platform.
904
905 Note that unlike the os.uname function this also returns
906 possible processor information as additional tuple entry.
907
908 Entries which cannot be determined are set to ''.
909
910 """
911 global _uname_cache
912
913 if _uname_cache is not None:
914 return _uname_cache
915
916 # Get some infos from the builtin os.uname API...
917 try:
918 system,node,release,version,machine = os.uname()
919
920 except AttributeError:
921 # Hmm, no uname... we'll have to poke around the system then.
922 system = sys.platform
923 release = ''
924 version = ''
925 node = _node()
926 machine = ''
927 processor = ''
928 use_syscmd_ver = 1
929
930 # Try win32_ver() on win32 platforms
931 if system == 'win32':
932 release,version,csd,ptype = win32_ver()
933 if release and version:
934 use_syscmd_ver = 0
935
936 # Try the 'ver' system command available on some
937 # platforms
938 if use_syscmd_ver:
939 system,release,version = _syscmd_ver(system)
940
941 # In case we still don't know anything useful, we'll try to
942 # help ourselves
943 if system in ('win32','win16'):
944 if not version:
945 if system == 'win32':
946 version = '32bit'
947 else:
948 version = '16bit'
949 system = 'Windows'
950
951 elif system[:4] == 'java':
952 release,vendor,vminfo,osinfo = java_ver()
953 system = 'Java'
954 version = string.join(vminfo,', ')
955 if not version:
956 version = vendor
957
958 elif os.name == 'mac':
959 release,(version,stage,nonrel),machine = mac_ver()
960 system = 'MacOS'
961
962 else:
963 # System specific extensions
964 if system == 'OpenVMS':
965 # OpenVMS seems to have release and version mixed up
966 if not release or release == '0':
967 release = version
968 version = ''
969 # Get processor information
970 try:
971 import vms_lib
972 except ImportError:
973 pass
974 else:
975 csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
976 if (cpu_number >= 128):
977 processor = 'Alpha'
978 else:
979 processor = 'VAX'
980 else:
981 # Get processor information from the uname system command
982 processor = _syscmd_uname('-p','')
983
984 # 'unknown' is not really any useful as information; we'll convert
985 # it to '' which is more portable
986 if system == 'unknown':
987 system = ''
988 if node == 'unknown':
989 node = ''
990 if release == 'unknown':
991 release = ''
992 if version == 'unknown':
993 version = ''
994 if machine == 'unknown':
995 machine = ''
996 if processor == 'unknown':
997 processor = ''
998 _uname_cache = system,node,release,version,machine,processor
999 return _uname_cache
1000
1001### Direct interfaces to some of the uname() return values
1002
1003def system():
1004
1005 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
1006
1007 An empty string is returned if the value cannot be determined.
1008
1009 """
1010 return uname()[0]
1011
1012def node():
1013
1014 """ Returns the computer's network name (may not be fully qualified !)
1015
1016 An empty string is returned if the value cannot be determined.
1017
1018 """
1019 return uname()[1]
1020
1021def release():
1022
1023 """ Returns the system's release, e.g. '2.2.0' or 'NT'
1024
1025 An empty string is returned if the value cannot be determined.
1026
1027 """
1028 return uname()[2]
1029
1030def version():
1031
1032 """ Returns the system's release version, e.g. '#3 on degas'
1033
1034 An empty string is returned if the value cannot be determined.
1035
1036 """
1037 return uname()[3]
1038
1039def machine():
1040
1041 """ Returns the machine type, e.g. 'i386'
1042
1043 An empty string is returned if the value cannot be determined.
1044
1045 """
1046 return uname()[4]
1047
1048def processor():
1049
1050 """ Returns the (true) processor name, e.g. 'amdk6'
1051
1052 An empty string is returned if the value cannot be
1053 determined. Note that many platforms do not provide this
1054 information or simply return the same value as for machine(),
1055 e.g. NetBSD does this.
1056
1057 """
1058 return uname()[5]
1059
1060### Various APIs for extracting information from sys.version
1061
1062_sys_version_parser = re.compile(r'([\w.+]+)\s*'
1063 '\(#(\d+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1064 '\[([^\]]+)\]?')
1065_sys_version_cache = None
1066
1067def _sys_version():
1068
1069 """ Returns a parsed version of Python's sys.version as tuple
1070 (version, buildno, builddate, compiler) referring to the Python
1071 version, build number, build date/time as string and the compiler
1072 identification string.
1073
1074 Note that unlike the Python sys.version, the returned value
1075 for the Python version will always include the patchlevel (it
1076 defaults to '.0').
1077
1078 """
1079 global _sys_version_cache
1080 import sys, re, time
1081
1082 if _sys_version_cache is not None:
1083 return _sys_version_cache
1084 version, buildno, builddate, buildtime, compiler = \
1085 _sys_version_parser.match(sys.version).groups()
1086 buildno = int(buildno)
1087 builddate = builddate + ' ' + buildtime
1088 l = string.split(version, '.')
1089 if len(l) == 2:
1090 l.append('0')
1091 version = string.join(l, '.')
1092 _sys_version_cache = (version, buildno, builddate, compiler)
1093 return _sys_version_cache
1094
1095def python_version():
1096
1097 """ Returns the Python version as string 'major.minor.patchlevel'
1098
1099 Note that unlike the Python sys.version, the returned value
1100 will always include the patchlevel (it defaults to 0).
1101
1102 """
1103 return _sys_version()[0]
1104
1105def python_version_tuple():
1106
1107 """ Returns the Python version as tuple (major, minor, patchlevel)
1108 of strings.
1109
1110 Note that unlike the Python sys.version, the returned value
1111 will always include the patchlevel (it defaults to 0).
1112
1113 """
1114 return string.split(_sys_version()[0], '.')
1115
1116def python_build():
1117
1118 """ Returns a tuple (buildno, builddate) stating the Python
1119 build number and date as strings.
1120
1121 """
1122 return _sys_version()[1:3]
1123
1124def python_compiler():
1125
1126 """ Returns a string identifying the compiler used for compiling
1127 Python.
1128
1129 """
1130 return _sys_version()[3]
1131
1132### The Opus Magnum of platform strings :-)
1133
1134_platform_cache = None
1135_platform_aliased_cache = None
1136
1137def platform(aliased=0, terse=0):
1138
1139 """ Returns a single string identifying the underlying platform
1140 with as much useful information as possible (but no more :).
1141
1142 The output is intended to be human readable rather than
1143 machine parseable. It may look different on different
1144 platforms and this is intended.
1145
1146 If "aliased" is true, the function will use aliases for
1147 various platforms that report system names which differ from
1148 their common names, e.g. SunOS will be reported as
1149 Solaris. The system_alias() function is used to implement
1150 this.
1151
1152 Setting terse to true causes the function to return only the
1153 absolute minimum information needed to identify the platform.
1154
1155 """
1156 global _platform_cache,_platform_aliased_cache
1157
1158 if not aliased and (_platform_cache is not None):
1159 return _platform_cache
1160 elif _platform_aliased_cache is not None:
1161 return _platform_aliased_cache
1162
1163 # Get uname information and then apply platform specific cosmetics
1164 # to it...
1165 system,node,release,version,machine,processor = uname()
1166 if machine == processor:
1167 processor = ''
1168 if aliased:
1169 system,release,version = system_alias(system,release,version)
1170
1171 if system == 'Windows':
1172 # MS platforms
1173 rel,vers,csd,ptype = win32_ver(version)
1174 if terse:
1175 platform = _platform(system,release)
1176 else:
1177 platform = _platform(system,release,version,csd)
1178
1179 elif system in ('Linux',):
1180 # Linux based systems
1181 distname,distversion,distid = dist('')
1182 if distname and not terse:
1183 platform = _platform(system,release,machine,processor,
1184 'with',
1185 distname,distversion,distid)
1186 else:
1187 # If the distribution name is unknown check for libc vs. glibc
1188 libcname,libcversion = libc_ver(sys.executable)
1189 platform = _platform(system,release,machine,processor,
1190 'with',
1191 libcname+libcversion)
1192 elif system == 'Java':
1193 # Java platforms
1194 r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
1195 if terse:
1196 platform = _platform(system,release,version)
1197 else:
1198 platform = _platform(system,release,version,
1199 'on',
1200 os_name,os_version,os_arch)
1201
1202 elif system == 'MacOS':
1203 # MacOS platforms
1204 if terse:
1205 platform = _platform(system,release)
1206 else:
1207 platform = _platform(system,release,machine)
1208
1209 else:
1210 # Generic handler
1211 if terse:
1212 platform = _platform(system,release)
1213 else:
1214 bits,linkage = architecture(sys.executable)
1215 platform = _platform(system,release,machine,processor,bits,linkage)
1216
1217 if aliased:
1218 _platform_aliased_cache = platform
1219 elif terse:
1220 pass
1221 else:
1222 _platform_cache = platform
1223 return platform
1224
1225### Command line interface
1226
1227if __name__ == '__main__':
1228 # Default is to print the aliased verbose platform string
1229 terse = ('terse' in sys.argv or '--terse' in sys.argv)
1230 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
1231 print platform(aliased,terse)
1232 sys.exit(0)