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