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