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