source: vendor/python/2.5/Lib/platform.py@ 3225

Last change on this file since 3225 was 3225, checked in by bird, 19 years ago

Python 2.5

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