comparison platform.py @ 0:4385a7d0efd1 grumpy-goblin

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