blob: 389e50cb2c7bf5fdbbe6a80088fcfc461d85ce15 [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')
Raymond Hettinger85c20a42003-11-06 14:06:48 +0000204 for n in reversed(xrange(len(verfiles))):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000205 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
Jack Jansena290e3d2003-08-11 11:08:49 +0000528 import MacOS
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000529 l = []
530 append = l.append
531 for selector in selectors:
532 try:
533 append(gestalt(selector))
Jack Jansena290e3d2003-08-11 11:08:49 +0000534 except (RuntimeError, MacOS.Error):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000535 append(default)
536 return l
537
538def _bcd2str(bcd):
539
540 return hex(bcd)[2:]
541
542def mac_ver(release='',versioninfo=('','',''),machine=''):
543
544 """ Get MacOS version information and return it as tuple (release,
545 versioninfo, machine) with versioninfo being a tuple (version,
546 dev_stage, non_release_version).
547
Brett Cannon8ab27df2003-08-05 03:52:04 +0000548 Entries which cannot be determined are set to the paramter values
549 which default to ''. All tuple entries are strings.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000550
551 Thanks to Mark R. Levinson for mailing documentation links and
552 code examples for this function. Documentation for the
553 gestalt() API is available online at:
554
555 http://www.rgaros.nl/gestalt/
556
557 """
558 # Check whether the version info module is available
559 try:
560 import gestalt
Jack Jansena290e3d2003-08-11 11:08:49 +0000561 import MacOS
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000562 except ImportError:
563 return release,versioninfo,machine
564 # Get the infos
565 sysv,sysu,sysa = _mac_ver_lookup(('sysv','sysu','sysa'))
566 # Decode the infos
567 if sysv:
568 major = (sysv & 0xFF00) >> 8
569 minor = (sysv & 0x00F0) >> 4
570 patch = (sysv & 0x000F)
571 release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
572 if sysu:
573 major = int((sysu & 0xFF000000L) >> 24)
574 minor = (sysu & 0x00F00000) >> 20
575 bugfix = (sysu & 0x000F0000) >> 16
576 stage = (sysu & 0x0000FF00) >> 8
577 nonrel = (sysu & 0x000000FF)
578 version = '%s.%i.%i' % (_bcd2str(major),minor,bugfix)
579 nonrel = _bcd2str(nonrel)
580 stage = {0x20:'development',
581 0x40:'alpha',
582 0x60:'beta',
583 0x80:'final'}.get(stage,'')
584 versioninfo = (version,stage,nonrel)
585 if sysa:
Tim Peters0eadaac2003-04-24 16:02:54 +0000586 machine = {0x1: '68k',
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000587 0x2: 'PowerPC'}.get(sysa,'')
588 return release,versioninfo,machine
589
Neal Norwitz9b924c62003-06-29 04:17:45 +0000590def _java_getprop(name,default):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000591
592 from java.lang import System
593 try:
594 return System.getProperty(name)
595 except:
596 return default
597
598def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
Tim Peters0eadaac2003-04-24 16:02:54 +0000599
Brett Cannon8ab27df2003-08-05 03:52:04 +0000600 """ Version interface for Jython.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000601
602 Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
603 a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
604 tuple (os_name,os_version,os_arch).
605
606 Values which cannot be determined are set to the defaults
607 given as parameters (which all default to '').
608
609 """
610 # Import the needed APIs
611 try:
612 import java.lang
613 except ImportError:
614 return release,vendor,vminfo,osinfo
615
616 vendor = _java_getprop('java.vendor',vendor)
617 release = _java_getprop('java.version',release)
618 vm_name,vm_release,vm_vendor = vminfo
619 vm_name = _java_getprop('java.vm.name',vm_name)
620 vm_vendor = _java_getprop('java.vm.vendor',vm_vendor)
621 vm_release = _java_getprop('java.vm.version',vm_release)
622 vminfo = vm_name,vm_release,vm_vendor
623 os_name,os_version,os_arch = osinfo
624 os_arch = _java_getprop('java.os.arch',os_arch)
625 os_name = _java_getprop('java.os.name',os_name)
626 os_version = _java_getprop('java.os.version',os_version)
627 osinfo = os_name,os_version,os_arch
Tim Peters0eadaac2003-04-24 16:02:54 +0000628
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000629 return release,vendor,vminfo,osinfo
630
631### System name aliasing
632
633def system_alias(system,release,version):
634
635 """ Returns (system,release,version) aliased to common
636 marketing names used for some systems.
637
638 It also does some reordering of the information in some cases
639 where it would otherwise cause confusion.
640
641 """
642 if system == 'Rhapsody':
643 # Apple's BSD derivative
644 # XXX How can we determine the marketing release number ?
645 return 'MacOS X Server',system+release,version
646
647 elif system == 'SunOS':
648 # Sun's OS
649 if release < '5':
650 # These releases use the old name SunOS
651 return system,release,version
652 # Modify release (marketing release = SunOS release - 3)
653 l = string.split(release,'.')
654 if l:
655 try:
656 major = int(l[0])
657 except ValueError:
658 pass
659 else:
660 major = major - 3
661 l[0] = str(major)
662 release = string.join(l,'.')
663 if release < '6':
664 system = 'Solaris'
665 else:
666 # XXX Whatever the new SunOS marketing name is...
667 system = 'Solaris'
668
669 elif system == 'IRIX64':
670 # IRIX reports IRIX64 on platforms with 64-bit support; yet it
671 # is really a version and not a different platform, since 32-bit
672 # apps are also supported..
673 system = 'IRIX'
674 if version:
675 version = version + ' (64bit)'
676 else:
677 version = '64bit'
678
679 elif system in ('win32','win16'):
680 # In case one of the other tricks
681 system = 'Windows'
682
683 return system,release,version
684
685### Various internal helpers
686
687def _platform(*args):
688
689 """ Helper to format the platform string in a filename
690 compatible format e.g. "system-version-machine".
691 """
692 # Format the platform string
693 platform = string.join(
694 map(string.strip,
695 filter(len,args)),
696 '-')
697
698 # Cleanup some possible filename obstacles...
699 replace = string.replace
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 platform = replace(platform,'(','-')
707 platform = replace(platform,')','-')
708
709 # No need to report 'unknown' information...
710 platform = replace(platform,'unknown','')
711
712 # Fold '--'s and remove trailing '-'
713 while 1:
714 cleaned = replace(platform,'--','-')
715 if cleaned == platform:
716 break
717 platform = cleaned
718 while platform[-1] == '-':
719 platform = platform[:-1]
720
721 return platform
722
723def _node(default=''):
724
725 """ Helper to determine the node name of this machine.
726 """
727 try:
728 import socket
729 except ImportError:
730 # No sockets...
731 return default
732 try:
733 return socket.gethostname()
734 except socket.error:
735 # Still not working...
736 return default
737
738# os.path.abspath is new in Python 1.5.2:
739if not hasattr(os.path,'abspath'):
740
741 def _abspath(path,
742
743 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,
744 normpath=os.path.normpath):
745
746 if not isabs(path):
747 path = join(getcwd(), path)
748 return normpath(path)
749
750else:
751
752 _abspath = os.path.abspath
753
754def _follow_symlinks(filepath):
755
756 """ In case filepath is a symlink, follow it until a
757 real file is reached.
758 """
759 filepath = _abspath(filepath)
760 while os.path.islink(filepath):
761 filepath = os.path.normpath(
762 os.path.join(filepath,os.readlink(filepath)))
763 return filepath
764
765def _syscmd_uname(option,default=''):
766
767 """ Interface to the system's uname command.
768 """
769 if sys.platform in ('dos','win32','win16','os2'):
770 # XXX Others too ?
771 return default
772 try:
773 f = os.popen('uname %s 2> /dev/null' % option)
774 except (AttributeError,os.error):
775 return default
776 output = string.strip(f.read())
777 rc = f.close()
778 if not output or rc:
779 return default
780 else:
781 return output
782
783def _syscmd_file(target,default=''):
784
785 """ Interface to the system's file command.
786
787 The function uses the -b option of the file command to have it
788 ommit the filename in its output and if possible the -L option
789 to have the command follow symlinks. It returns default in
790 case the command should fail.
791
792 """
793 target = _follow_symlinks(target)
794 try:
795 f = os.popen('file %s 2> /dev/null' % target)
796 except (AttributeError,os.error):
797 return default
798 output = string.strip(f.read())
799 rc = f.close()
800 if not output or rc:
801 return default
802 else:
803 return output
804
805### Information about the used architecture
806
807# Default values for architecture; non-empty strings override the
808# defaults given as parameters
809_default_architecture = {
810 'win32': ('','WindowsPE'),
811 'win16': ('','Windows'),
812 'dos': ('','MSDOS'),
813}
814
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000815_architecture_split = re.compile(r'[\s,]').split
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000816
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000817def architecture(executable=sys.executable,bits='',linkage=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000818
819 """ Queries the given executable (defaults to the Python interpreter
Brett Cannon8ab27df2003-08-05 03:52:04 +0000820 binary) for various architecture information.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000821
Brett Cannon8ab27df2003-08-05 03:52:04 +0000822 Returns a tuple (bits,linkage) which contains information about
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000823 the bit architecture and the linkage format used for the
824 executable. Both values are returned as strings.
825
826 Values that cannot be determined are returned as given by the
827 parameter presets. If bits is given as '', the sizeof(pointer)
828 (or sizeof(long) on Python version < 1.5.2) is used as
829 indicator for the supported pointer size.
830
831 The function relies on the system's "file" command to do the
832 actual work. This is available on most if not all Unix
Brett Cannon8ab27df2003-08-05 03:52:04 +0000833 platforms. On some non-Unix platforms where the "file" command
834 does not exist and the executable is set to the Python interpreter
835 binary defaults from _default_architecture are used.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000836
837 """
838 # Use the sizeof(pointer) as default number of bits if nothing
839 # else is given as default.
840 if not bits:
841 import struct
842 try:
843 size = struct.calcsize('P')
844 except struct.error:
845 # Older installations can only query longs
846 size = struct.calcsize('l')
847 bits = str(size*8) + 'bit'
Tim Peters0eadaac2003-04-24 16:02:54 +0000848
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000849 # Get data from the 'file' system command
850 output = _syscmd_file(executable,'')
851
852 if not output and \
853 executable == sys.executable:
854 # "file" command did not return anything; we'll try to provide
Tim Peters0eadaac2003-04-24 16:02:54 +0000855 # some sensible defaults then...
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000856 if _default_architecture.has_key(sys.platform):
857 b,l = _default_architecture[sys.platform]
858 if b:
859 bits = b
860 if l:
861 linkage = l
862 return bits,linkage
863
864 # Split the output into a list of strings omitting the filename
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000865 fileout = _architecture_split(output)[1:]
Tim Peters0eadaac2003-04-24 16:02:54 +0000866
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000867 if 'executable' not in fileout:
868 # Format not supported
869 return bits,linkage
870
871 # Bits
872 if '32-bit' in fileout:
873 bits = '32bit'
874 elif 'N32' in fileout:
875 # On Irix only
876 bits = 'n32bit'
877 elif '64-bit' in fileout:
878 bits = '64bit'
879
880 # Linkage
881 if 'ELF' in fileout:
882 linkage = 'ELF'
883 elif 'PE' in fileout:
884 # E.g. Windows uses this format
885 if 'Windows' in fileout:
886 linkage = 'WindowsPE'
887 else:
888 linkage = 'PE'
889 elif 'COFF' in fileout:
890 linkage = 'COFF'
891 elif 'MS-DOS' in fileout:
892 linkage = 'MSDOS'
893 else:
894 # XXX the A.OUT format also falls under this class...
895 pass
896
897 return bits,linkage
898
899### Portable uname() interface
Tim Peters0eadaac2003-04-24 16:02:54 +0000900
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000901_uname_cache = None
902
903def uname():
904
905 """ Fairly portable uname interface. Returns a tuple
906 of strings (system,node,release,version,machine,processor)
907 identifying the underlying platform.
908
909 Note that unlike the os.uname function this also returns
Brett Cannon8ab27df2003-08-05 03:52:04 +0000910 possible processor information as an additional tuple entry.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000911
912 Entries which cannot be determined are set to ''.
913
914 """
915 global _uname_cache
916
917 if _uname_cache is not None:
918 return _uname_cache
919
920 # Get some infos from the builtin os.uname API...
921 try:
922 system,node,release,version,machine = os.uname()
923
924 except AttributeError:
925 # Hmm, no uname... we'll have to poke around the system then.
926 system = sys.platform
927 release = ''
928 version = ''
929 node = _node()
930 machine = ''
931 processor = ''
932 use_syscmd_ver = 1
933
934 # Try win32_ver() on win32 platforms
935 if system == 'win32':
936 release,version,csd,ptype = win32_ver()
937 if release and version:
938 use_syscmd_ver = 0
Tim Peters0eadaac2003-04-24 16:02:54 +0000939
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000940 # Try the 'ver' system command available on some
941 # platforms
942 if use_syscmd_ver:
943 system,release,version = _syscmd_ver(system)
944
945 # In case we still don't know anything useful, we'll try to
946 # help ourselves
947 if system in ('win32','win16'):
948 if not version:
949 if system == 'win32':
950 version = '32bit'
951 else:
952 version = '16bit'
953 system = 'Windows'
954
955 elif system[:4] == 'java':
956 release,vendor,vminfo,osinfo = java_ver()
957 system = 'Java'
958 version = string.join(vminfo,', ')
959 if not version:
960 version = vendor
961
962 elif os.name == 'mac':
963 release,(version,stage,nonrel),machine = mac_ver()
964 system = 'MacOS'
965
966 else:
967 # System specific extensions
968 if system == 'OpenVMS':
969 # OpenVMS seems to have release and version mixed up
970 if not release or release == '0':
971 release = version
972 version = ''
973 # Get processor information
974 try:
975 import vms_lib
976 except ImportError:
977 pass
978 else:
979 csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
980 if (cpu_number >= 128):
981 processor = 'Alpha'
982 else:
983 processor = 'VAX'
984 else:
985 # Get processor information from the uname system command
986 processor = _syscmd_uname('-p','')
987
988 # 'unknown' is not really any useful as information; we'll convert
989 # it to '' which is more portable
990 if system == 'unknown':
991 system = ''
992 if node == 'unknown':
993 node = ''
994 if release == 'unknown':
995 release = ''
996 if version == 'unknown':
997 version = ''
998 if machine == 'unknown':
999 machine = ''
1000 if processor == 'unknown':
1001 processor = ''
1002 _uname_cache = system,node,release,version,machine,processor
1003 return _uname_cache
1004
1005### Direct interfaces to some of the uname() return values
1006
1007def system():
1008
1009 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
1010
1011 An empty string is returned if the value cannot be determined.
1012
1013 """
1014 return uname()[0]
1015
1016def node():
1017
Brett Cannon8ab27df2003-08-05 03:52:04 +00001018 """ Returns the computer's network name (which may not be fully
1019 qualified)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001020
1021 An empty string is returned if the value cannot be determined.
1022
1023 """
1024 return uname()[1]
1025
1026def release():
1027
1028 """ Returns the system's release, e.g. '2.2.0' or 'NT'
1029
1030 An empty string is returned if the value cannot be determined.
1031
1032 """
1033 return uname()[2]
1034
1035def version():
1036
1037 """ Returns the system's release version, e.g. '#3 on degas'
1038
1039 An empty string is returned if the value cannot be determined.
1040
1041 """
1042 return uname()[3]
1043
1044def machine():
1045
1046 """ Returns the machine type, e.g. 'i386'
1047
1048 An empty string is returned if the value cannot be determined.
1049
1050 """
1051 return uname()[4]
1052
1053def processor():
1054
1055 """ Returns the (true) processor name, e.g. 'amdk6'
1056
1057 An empty string is returned if the value cannot be
1058 determined. Note that many platforms do not provide this
1059 information or simply return the same value as for machine(),
1060 e.g. NetBSD does this.
1061
1062 """
1063 return uname()[5]
1064
1065### Various APIs for extracting information from sys.version
1066
1067_sys_version_parser = re.compile(r'([\w.+]+)\s*'
1068 '\(#(\d+),\s*([\w ]+),\s*([\w :]+)\)\s*'
1069 '\[([^\]]+)\]?')
1070_sys_version_cache = None
1071
1072def _sys_version():
1073
1074 """ Returns a parsed version of Python's sys.version as tuple
1075 (version, buildno, builddate, compiler) referring to the Python
1076 version, build number, build date/time as string and the compiler
1077 identification string.
1078
1079 Note that unlike the Python sys.version, the returned value
1080 for the Python version will always include the patchlevel (it
1081 defaults to '.0').
1082
1083 """
1084 global _sys_version_cache
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001085
1086 if _sys_version_cache is not None:
1087 return _sys_version_cache
1088 version, buildno, builddate, buildtime, compiler = \
1089 _sys_version_parser.match(sys.version).groups()
1090 buildno = int(buildno)
1091 builddate = builddate + ' ' + buildtime
1092 l = string.split(version, '.')
1093 if len(l) == 2:
1094 l.append('0')
1095 version = string.join(l, '.')
1096 _sys_version_cache = (version, buildno, builddate, compiler)
1097 return _sys_version_cache
1098
1099def python_version():
1100
1101 """ Returns the Python version as string 'major.minor.patchlevel'
1102
1103 Note that unlike the Python sys.version, the returned value
1104 will always include the patchlevel (it defaults to 0).
1105
1106 """
1107 return _sys_version()[0]
1108
1109def python_version_tuple():
1110
1111 """ Returns the Python version as tuple (major, minor, patchlevel)
1112 of strings.
1113
1114 Note that unlike the Python sys.version, the returned value
1115 will always include the patchlevel (it defaults to 0).
1116
1117 """
1118 return string.split(_sys_version()[0], '.')
1119
1120def python_build():
1121
1122 """ Returns a tuple (buildno, builddate) stating the Python
1123 build number and date as strings.
1124
1125 """
1126 return _sys_version()[1:3]
1127
1128def python_compiler():
1129
1130 """ Returns a string identifying the compiler used for compiling
1131 Python.
1132
1133 """
1134 return _sys_version()[3]
1135
1136### The Opus Magnum of platform strings :-)
1137
1138_platform_cache = None
1139_platform_aliased_cache = None
1140
1141def platform(aliased=0, terse=0):
1142
1143 """ Returns a single string identifying the underlying platform
1144 with as much useful information as possible (but no more :).
Tim Peters0eadaac2003-04-24 16:02:54 +00001145
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001146 The output is intended to be human readable rather than
1147 machine parseable. It may look different on different
1148 platforms and this is intended.
1149
1150 If "aliased" is true, the function will use aliases for
1151 various platforms that report system names which differ from
1152 their common names, e.g. SunOS will be reported as
1153 Solaris. The system_alias() function is used to implement
1154 this.
1155
1156 Setting terse to true causes the function to return only the
1157 absolute minimum information needed to identify the platform.
1158
1159 """
1160 global _platform_cache,_platform_aliased_cache
1161
1162 if not aliased and (_platform_cache is not None):
1163 return _platform_cache
1164 elif _platform_aliased_cache is not None:
1165 return _platform_aliased_cache
1166
1167 # Get uname information and then apply platform specific cosmetics
1168 # to it...
1169 system,node,release,version,machine,processor = uname()
1170 if machine == processor:
1171 processor = ''
1172 if aliased:
1173 system,release,version = system_alias(system,release,version)
1174
1175 if system == 'Windows':
1176 # MS platforms
1177 rel,vers,csd,ptype = win32_ver(version)
1178 if terse:
1179 platform = _platform(system,release)
1180 else:
1181 platform = _platform(system,release,version,csd)
1182
1183 elif system in ('Linux',):
1184 # Linux based systems
1185 distname,distversion,distid = dist('')
1186 if distname and not terse:
1187 platform = _platform(system,release,machine,processor,
1188 'with',
1189 distname,distversion,distid)
1190 else:
1191 # If the distribution name is unknown check for libc vs. glibc
1192 libcname,libcversion = libc_ver(sys.executable)
1193 platform = _platform(system,release,machine,processor,
1194 'with',
1195 libcname+libcversion)
1196 elif system == 'Java':
1197 # Java platforms
1198 r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
1199 if terse:
1200 platform = _platform(system,release,version)
1201 else:
1202 platform = _platform(system,release,version,
1203 'on',
1204 os_name,os_version,os_arch)
1205
1206 elif system == 'MacOS':
1207 # MacOS platforms
1208 if terse:
1209 platform = _platform(system,release)
1210 else:
1211 platform = _platform(system,release,machine)
1212
1213 else:
1214 # Generic handler
1215 if terse:
1216 platform = _platform(system,release)
1217 else:
1218 bits,linkage = architecture(sys.executable)
1219 platform = _platform(system,release,machine,processor,bits,linkage)
Tim Peters0eadaac2003-04-24 16:02:54 +00001220
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001221 if aliased:
1222 _platform_aliased_cache = platform
1223 elif terse:
1224 pass
1225 else:
1226 _platform_cache = platform
1227 return platform
1228
1229### Command line interface
1230
1231if __name__ == '__main__':
1232 # Default is to print the aliased verbose platform string
Tim Peters0eadaac2003-04-24 16:02:54 +00001233 terse = ('terse' in sys.argv or '--terse' in sys.argv)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001234 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
1235 print platform(aliased,terse)
1236 sys.exit(0)