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