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