1#!/usr/bin/env python 2 3""" This module tries to retrieve as much platform-identifying data as 4 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 <[email protected]>. 12# If you find problems, please submit bug reports/patches via the 13# Python bug tracker (http://bugs.python.org) 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# 23# Many thanks to all those who helped adding platform-specific 24# 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, Guido van Rossum, Anthony Baxter, Steve 32# Dower 33# 34# History: 35# 36# <see CVS and SVN checkin messages for history> 37# 38# 1.0.8 - changed Windows support to read version from kernel32.dll 39# 1.0.7 - added DEV_NULL 40# 1.0.6 - added linux_distribution() 41# 1.0.5 - fixed Java support to allow running the module on Jython 42# 1.0.4 - added IronPython support 43# 1.0.3 - added normalization of Windows system name 44# 1.0.2 - added more Windows support 45# 1.0.1 - reformatted to make doc.py happy 46# 1.0.0 - reformatted a bit and checked into Python CVS 47# 0.8.0 - added sys.version parser and various new access 48# APIs (python_version(), python_compiler(), etc.) 49# 0.7.2 - fixed architecture() to use sizeof(pointer) where available 50# 0.7.1 - added support for Caldera OpenLinux 51# 0.7.0 - some fixes for WinCE; untabified the source file 52# 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and 53# vms_lib.getsyi() configured 54# 0.6.1 - added code to prevent 'uname -p' on platforms which are 55# known not to support it 56# 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k; 57# did some cleanup of the interfaces - some APIs have changed 58# 0.5.5 - fixed another type in the MacOS code... should have 59# used more coffee today ;-) 60# 0.5.4 - fixed a few typos in the MacOS code 61# 0.5.3 - added experimental MacOS support; added better popen() 62# workarounds in _syscmd_ver() -- still not 100% elegant 63# though 64# 0.5.2 - fixed uname() to return '' instead of 'unknown' in all 65# return values (the system uname command tends to return 66# 'unknown' instead of just leaving the field empty) 67# 0.5.1 - included code for slackware dist; added exception handlers 68# to cover up situations where platforms don't have os.popen 69# (e.g. Mac) or fail on socket.gethostname(); fixed libc 70# detection RE 71# 0.5.0 - changed the API names referring to system commands to *syscmd*; 72# added java_ver(); made syscmd_ver() a private 73# API (was system_ver() in previous versions) -- use uname() 74# instead; extended the win32_ver() to also return processor 75# type information 76# 0.4.0 - added win32_ver() and modified the platform() output for WinXX 77# 0.3.4 - fixed a bug in _follow_symlinks() 78# 0.3.3 - fixed popen() and "file" command invokation bugs 79# 0.3.2 - added architecture() API and support for it in platform() 80# 0.3.1 - fixed syscmd_ver() RE to support Windows NT 81# 0.3.0 - added system alias support 82# 0.2.3 - removed 'wince' again... oh well. 83# 0.2.2 - added 'wince' to syscmd_ver() supported platforms 84# 0.2.1 - added cache logic and changed the platform string format 85# 0.2.0 - changed the API to use functions instead of module globals 86# since some action take too long to be run on module import 87# 0.1.0 - first release 88# 89# You can always get the latest version of this module at: 90# 91# http://www.egenix.com/files/python/platform.py 92# 93# If that URL should fail, try contacting the author. 94 95__copyright__ = """ 96 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:[email protected] 97 Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:[email protected] 98 99 Permission to use, copy, modify, and distribute this software and its 100 documentation for any purpose and without fee or royalty is hereby granted, 101 provided that the above copyright notice appear in all copies and that 102 both that copyright notice and this permission notice appear in 103 supporting documentation or portions thereof, including modifications, 104 that you make. 105 106 EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO 107 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 108 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, 109 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 110 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 111 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 112 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! 113 114""" 115 116__version__ = '1.0.7' 117 118import sys,string,os,re 119 120### Globals & Constants 121 122# Determine the platform's /dev/null device 123try: 124 DEV_NULL = os.devnull 125except AttributeError: 126 # os.devnull was added in Python 2.4, so emulate it for earlier 127 # Python versions 128 if sys.platform in ('dos','win32','win16','os2'): 129 # Use the old CP/M NUL as device name 130 DEV_NULL = 'NUL' 131 else: 132 # Standard Unix uses /dev/null 133 DEV_NULL = '/dev/null' 134 135# Helper for comparing two version number strings. 136# Based on the description of the PHP's version_compare(): 137# http://php.net/manual/en/function.version-compare.php 138 139_ver_stages = { 140 # any string not found in this dict, will get 0 assigned 141 'dev': 10, 142 'alpha': 20, 'a': 20, 143 'beta': 30, 'b': 30, 144 'c': 40, 145 'RC': 50, 'rc': 50, 146 # number, will get 100 assigned 147 'pl': 200, 'p': 200, 148} 149 150_component_re = re.compile(r'([0-9]+|[._+-])') 151 152def _comparable_version(version): 153 result = [] 154 for v in _component_re.split(version): 155 if v not in '._+-': 156 try: 157 v = int(v, 10) 158 t = 100 159 except ValueError: 160 t = _ver_stages.get(v, 0) 161 result.extend((t, v)) 162 return result 163 164### Platform specific APIs 165 166_libc_search = re.compile(r'(__libc_init)' 167 '|' 168 '(GLIBC_([0-9.]+))' 169 '|' 170 '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)') 171 172def libc_ver(executable=sys.executable,lib='',version='', chunksize=2048): 173 174 """ Tries to determine the libc version that the file executable 175 (which defaults to the Python interpreter) is linked against. 176 177 Returns a tuple of strings (lib,version) which default to the 178 given parameters in case the lookup fails. 179 180 Note that the function has intimate knowledge of how different 181 libc versions add symbols to the executable and thus is probably 182 only useable for executables compiled using gcc. 183 184 The file is read and scanned in chunks of chunksize bytes. 185 186 """ 187 V = _comparable_version 188 if hasattr(os.path, 'realpath'): 189 # Python 2.2 introduced os.path.realpath(); it is used 190 # here to work around problems with Cygwin not being 191 # able to open symlinks for reading 192 executable = os.path.realpath(executable) 193 with open(executable, 'rb') as f: 194 binary = f.read(chunksize) 195 pos = 0 196 while pos < len(binary): 197 m = _libc_search.search(binary,pos) 198 if not m or m.end() == len(binary): 199 chunk = f.read(chunksize) 200 if chunk: 201 binary = binary[max(pos, len(binary) - 1000):] + chunk 202 pos = 0 203 continue 204 if not m: 205 break 206 libcinit,glibc,glibcversion,so,threads,soversion = m.groups() 207 if libcinit and not lib: 208 lib = 'libc' 209 elif glibc: 210 if lib != 'glibc': 211 lib = 'glibc' 212 version = glibcversion 213 elif V(glibcversion) > V(version): 214 version = glibcversion 215 elif so: 216 if lib != 'glibc': 217 lib = 'libc' 218 if soversion and (not version or V(soversion) > V(version)): 219 version = soversion 220 if threads and version[-len(threads):] != threads: 221 version = version + threads 222 pos = m.end() 223 return lib,version 224 225def _dist_try_harder(distname,version,id): 226 227 """ Tries some special tricks to get the distribution 228 information in case the default method fails. 229 230 Currently supports older SuSE Linux, Caldera OpenLinux and 231 Slackware Linux distributions. 232 233 """ 234 if os.path.exists('/var/adm/inst-log/info'): 235 # SuSE Linux stores distribution information in that file 236 info = open('/var/adm/inst-log/info').readlines() 237 distname = 'SuSE' 238 for line in info: 239 tv = string.split(line) 240 if len(tv) == 2: 241 tag,value = tv 242 else: 243 continue 244 if tag == 'MIN_DIST_VERSION': 245 version = string.strip(value) 246 elif tag == 'DIST_IDENT': 247 values = string.split(value,'-') 248 id = values[2] 249 return distname,version,id 250 251 if os.path.exists('/etc/.installed'): 252 # Caldera OpenLinux has some infos in that file (thanks to Colin Kong) 253 info = open('/etc/.installed').readlines() 254 for line in info: 255 pkg = string.split(line,'-') 256 if len(pkg) >= 2 and pkg[0] == 'OpenLinux': 257 # XXX does Caldera support non Intel platforms ? If yes, 258 # where can we find the needed id ? 259 return 'OpenLinux',pkg[1],id 260 261 if os.path.isdir('/usr/lib/setup'): 262 # Check for slackware version tag file (thanks to Greg Andruk) 263 verfiles = os.listdir('/usr/lib/setup') 264 for n in range(len(verfiles)-1, -1, -1): 265 if verfiles[n][:14] != 'slack-version-': 266 del verfiles[n] 267 if verfiles: 268 verfiles.sort() 269 distname = 'slackware' 270 version = verfiles[-1][14:] 271 return distname,version,id 272 273 return distname,version,id 274 275_release_filename = re.compile(r'(\w+)[-_](release|version)') 276_lsb_release_version = re.compile(r'(.+)' 277 ' release ' 278 '([\d.]+)' 279 '[^(]*(?:\((.+)\))?') 280_release_version = re.compile(r'([^0-9]+)' 281 '(?: release )?' 282 '([\d.]+)' 283 '[^(]*(?:\((.+)\))?') 284 285# See also http://www.novell.com/coolsolutions/feature/11251.html 286# and http://linuxmafia.com/faq/Admin/release-files.html 287# and http://data.linux-ntfs.org/rpm/whichrpm 288# and http://www.die.net/doc/linux/man/man1/lsb_release.1.html 289 290_supported_dists = ( 291 'SuSE', 'debian', 'fedora', 'redhat', 'centos', 292 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', 293 'UnitedLinux', 'turbolinux') 294 295def _parse_release_file(firstline): 296 297 # Default to empty 'version' and 'id' strings. Both defaults are used 298 # when 'firstline' is empty. 'id' defaults to empty when an id can not 299 # be deduced. 300 version = '' 301 id = '' 302 303 # Parse the first line 304 m = _lsb_release_version.match(firstline) 305 if m is not None: 306 # LSB format: "distro release x.x (codename)" 307 return tuple(m.groups()) 308 309 # Pre-LSB format: "distro x.x (codename)" 310 m = _release_version.match(firstline) 311 if m is not None: 312 return tuple(m.groups()) 313 314 # Unknown format... take the first two words 315 l = string.split(string.strip(firstline)) 316 if l: 317 version = l[0] 318 if len(l) > 1: 319 id = l[1] 320 return '', version, id 321 322def linux_distribution(distname='', version='', id='', 323 324 supported_dists=_supported_dists, 325 full_distribution_name=1): 326 327 """ Tries to determine the name of the Linux OS distribution name. 328 329 The function first looks for a distribution release file in 330 /etc and then reverts to _dist_try_harder() in case no 331 suitable files are found. 332 333 supported_dists may be given to define the set of Linux 334 distributions to look for. It defaults to a list of currently 335 supported Linux distributions identified by their release file 336 name. 337 338 If full_distribution_name is true (default), the full 339 distribution read from the OS is returned. Otherwise the short 340 name taken from supported_dists is used. 341 342 Returns a tuple (distname,version,id) which default to the 343 args given as parameters. 344 345 """ 346 try: 347 etc = os.listdir('/etc') 348 except os.error: 349 # Probably not a Unix system 350 return distname,version,id 351 etc.sort() 352 for file in etc: 353 m = _release_filename.match(file) 354 if m is not None: 355 _distname,dummy = m.groups() 356 if _distname in supported_dists: 357 distname = _distname 358 break 359 else: 360 return _dist_try_harder(distname,version,id) 361 362 # Read the first line 363 f = open('/etc/'+file, 'r') 364 firstline = f.readline() 365 f.close() 366 _distname, _version, _id = _parse_release_file(firstline) 367 368 if _distname and full_distribution_name: 369 distname = _distname 370 if _version: 371 version = _version 372 if _id: 373 id = _id 374 return distname, version, id 375 376# To maintain backwards compatibility: 377 378def dist(distname='',version='',id='', 379 380 supported_dists=_supported_dists): 381 382 """ Tries to determine the name of the Linux OS distribution name. 383 384 The function first looks for a distribution release file in 385 /etc and then reverts to _dist_try_harder() in case no 386 suitable files are found. 387 388 Returns a tuple (distname,version,id) which default to the 389 args given as parameters. 390 391 """ 392 return linux_distribution(distname, version, id, 393 supported_dists=supported_dists, 394 full_distribution_name=0) 395 396class _popen: 397 398 """ Fairly portable (alternative) popen implementation. 399 400 This is mostly needed in case os.popen() is not available, or 401 doesn't work as advertised, e.g. in Win9X GUI programs like 402 PythonWin or IDLE. 403 404 Writing to the pipe is currently not supported. 405 406 """ 407 tmpfile = '' 408 pipe = None 409 bufsize = None 410 mode = 'r' 411 412 def __init__(self,cmd,mode='r',bufsize=None): 413 414 if mode != 'r': 415 raise ValueError,'popen()-emulation only supports read mode' 416 import tempfile 417 self.tmpfile = tmpfile = tempfile.mktemp() 418 os.system(cmd + ' > %s' % tmpfile) 419 self.pipe = open(tmpfile,'rb') 420 self.bufsize = bufsize 421 self.mode = mode 422 423 def read(self): 424 425 return self.pipe.read() 426 427 def readlines(self): 428 429 if self.bufsize is not None: 430 return self.pipe.readlines() 431 432 def close(self, 433 434 remove=os.unlink,error=os.error): 435 436 if self.pipe: 437 rc = self.pipe.close() 438 else: 439 rc = 255 440 if self.tmpfile: 441 try: 442 remove(self.tmpfile) 443 except error: 444 pass 445 return rc 446 447 # Alias 448 __del__ = close 449 450def popen(cmd, mode='r', bufsize=None): 451 452 """ Portable popen() interface. 453 """ 454 # Find a working popen implementation preferring win32pipe.popen 455 # over os.popen over _popen 456 popen = None 457 if os.environ.get('OS','') == 'Windows_NT': 458 # On NT win32pipe should work; on Win9x it hangs due to bugs 459 # in the MS C lib (see MS KnowledgeBase article Q150956) 460 try: 461 import win32pipe 462 except ImportError: 463 pass 464 else: 465 popen = win32pipe.popen 466 if popen is None: 467 if hasattr(os,'popen'): 468 popen = os.popen 469 # Check whether it works... it doesn't in GUI programs 470 # on Windows platforms 471 if sys.platform == 'win32': # XXX Others too ? 472 try: 473 popen('') 474 except os.error: 475 popen = _popen 476 else: 477 popen = _popen 478 if bufsize is None: 479 return popen(cmd,mode) 480 else: 481 return popen(cmd,mode,bufsize) 482 483 484def _norm_version(version, build=''): 485 486 """ Normalize the version and build strings and return a single 487 version string using the format major.minor.build (or patchlevel). 488 """ 489 l = string.split(version,'.') 490 if build: 491 l.append(build) 492 try: 493 ints = map(int,l) 494 except ValueError: 495 strings = l 496 else: 497 strings = map(str,ints) 498 version = string.join(strings[:3],'.') 499 return version 500 501_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' 502 '.*' 503 '\[.* ([\d.]+)\])') 504 505# Examples of VER command output: 506# 507# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195] 508# Windows XP: Microsoft Windows XP [Version 5.1.2600] 509# Windows Vista: Microsoft Windows [Version 6.0.6002] 510# 511# Note that the "Version" string gets localized on different 512# Windows versions. 513 514def _syscmd_ver(system='', release='', version='', 515 516 supported_platforms=('win32','win16','dos','os2')): 517 518 """ Tries to figure out the OS version used and returns 519 a tuple (system,release,version). 520 521 It uses the "ver" shell command for this which is known 522 to exists on Windows, DOS and OS/2. XXX Others too ? 523 524 In case this fails, the given parameters are used as 525 defaults. 526 527 """ 528 if sys.platform not in supported_platforms: 529 return system,release,version 530 531 # Try some common cmd strings 532 for cmd in ('ver','command /c ver','cmd /c ver'): 533 try: 534 pipe = popen(cmd) 535 info = pipe.read() 536 if pipe.close(): 537 raise os.error,'command failed' 538 # XXX How can I suppress shell errors from being written 539 # to stderr ? 540 except os.error,why: 541 #print 'Command %s failed: %s' % (cmd,why) 542 continue 543 except IOError,why: 544 #print 'Command %s failed: %s' % (cmd,why) 545 continue 546 else: 547 break 548 else: 549 return system,release,version 550 551 # Parse the output 552 info = string.strip(info) 553 m = _ver_output.match(info) 554 if m is not None: 555 system,release,version = m.groups() 556 # Strip trailing dots from version and release 557 if release[-1] == '.': 558 release = release[:-1] 559 if version[-1] == '.': 560 version = version[:-1] 561 # Normalize the version and build strings (eliminating additional 562 # zeros) 563 version = _norm_version(version) 564 return system,release,version 565 566_WIN32_CLIENT_RELEASES = { 567 (5, 0): "2000", 568 (5, 1): "XP", 569 # Strictly, 5.2 client is XP 64-bit, but platform.py historically 570 # has always called it 2003 Server 571 (5, 2): "2003Server", 572 (5, None): "post2003", 573 574 (6, 0): "Vista", 575 (6, 1): "7", 576 (6, 2): "8", 577 (6, 3): "8.1", 578 (6, None): "post8.1", 579 580 (10, 0): "10", 581 (10, None): "post10", 582} 583 584# Server release name lookup will default to client names if necessary 585_WIN32_SERVER_RELEASES = { 586 (5, 2): "2003Server", 587 588 (6, 0): "2008Server", 589 (6, 1): "2008ServerR2", 590 (6, 2): "2012Server", 591 (6, 3): "2012ServerR2", 592 (6, None): "post2012ServerR2", 593} 594 595def _get_real_winver(maj, min, build): 596 if maj < 6 or (maj == 6 and min < 2): 597 return maj, min, build 598 599 from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer, 600 Structure, WinDLL, _Pointer) 601 from ctypes.wintypes import DWORD, HANDLE 602 603 class VS_FIXEDFILEINFO(Structure): 604 _fields_ = [ 605 ("dwSignature", DWORD), 606 ("dwStrucVersion", DWORD), 607 ("dwFileVersionMS", DWORD), 608 ("dwFileVersionLS", DWORD), 609 ("dwProductVersionMS", DWORD), 610 ("dwProductVersionLS", DWORD), 611 ("dwFileFlagsMask", DWORD), 612 ("dwFileFlags", DWORD), 613 ("dwFileOS", DWORD), 614 ("dwFileType", DWORD), 615 ("dwFileSubtype", DWORD), 616 ("dwFileDateMS", DWORD), 617 ("dwFileDateLS", DWORD), 618 ] 619 class PVS_FIXEDFILEINFO(_Pointer): 620 _type_ = VS_FIXEDFILEINFO 621 622 kernel32 = WinDLL('kernel32') 623 version = WinDLL('version') 624 625 # We will immediately double the length up to MAX_PATH, but the 626 # path may be longer, so we retry until the returned string is 627 # shorter than our buffer. 628 name_len = actual_len = 130 629 while actual_len == name_len: 630 name_len *= 2 631 name = create_unicode_buffer(name_len) 632 actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle), 633 name, len(name)) 634 if not actual_len: 635 return maj, min, build 636 637 size = version.GetFileVersionInfoSizeW(name, None) 638 if not size: 639 return maj, min, build 640 641 ver_block = c_buffer(size) 642 if (not version.GetFileVersionInfoW(name, None, size, ver_block) or 643 not ver_block): 644 return maj, min, build 645 646 pvi = PVS_FIXEDFILEINFO() 647 if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())): 648 return maj, min, build 649 650 maj = pvi.contents.dwProductVersionMS >> 16 651 min = pvi.contents.dwProductVersionMS & 0xFFFF 652 build = pvi.contents.dwProductVersionLS >> 16 653 654 return maj, min, build 655 656def win32_ver(release='', version='', csd='', ptype=''): 657 try: 658 from sys import getwindowsversion 659 except ImportError: 660 return release, version, csd, ptype 661 try: 662 from winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE 663 except ImportError: 664 from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE 665 666 winver = getwindowsversion() 667 maj, min, build = _get_real_winver(*winver[:3]) 668 version = '{0}.{1}.{2}'.format(maj, min, build) 669 670 release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or 671 _WIN32_CLIENT_RELEASES.get((maj, None)) or 672 release) 673 674 # getwindowsversion() reflect the compatibility mode Python is 675 # running under, and so the service pack value is only going to be 676 # valid if the versions match. 677 if winver[:2] == (maj, min): 678 try: 679 csd = 'SP{}'.format(winver.service_pack_major) 680 except AttributeError: 681 if csd[:13] == 'Service Pack ': 682 csd = 'SP' + csd[13:] 683 684 # VER_NT_SERVER = 3 685 if getattr(winver, 'product_type', None) == 3: 686 release = (_WIN32_SERVER_RELEASES.get((maj, min)) or 687 _WIN32_SERVER_RELEASES.get((maj, None)) or 688 release) 689 690 key = None 691 try: 692 key = OpenKeyEx(HKEY_LOCAL_MACHINE, 693 r'SOFTWARE\Microsoft\Windows NT\CurrentVersion') 694 ptype = QueryValueEx(key, 'CurrentType')[0] 695 except: 696 pass 697 finally: 698 if key: 699 CloseKey(key) 700 701 return release, version, csd, ptype 702 703def _mac_ver_lookup(selectors,default=None): 704 705 from gestalt import gestalt 706 import MacOS 707 l = [] 708 append = l.append 709 for selector in selectors: 710 try: 711 append(gestalt(selector)) 712 except (RuntimeError, MacOS.Error): 713 append(default) 714 return l 715 716def _bcd2str(bcd): 717 718 return hex(bcd)[2:] 719 720def _mac_ver_gestalt(): 721 """ 722 Thanks to Mark R. Levinson for mailing documentation links and 723 code examples for this function. Documentation for the 724 gestalt() API is available online at: 725 726 http://www.rgaros.nl/gestalt/ 727 """ 728 # Check whether the version info module is available 729 try: 730 import gestalt 731 import MacOS 732 except ImportError: 733 return None 734 # Get the infos 735 sysv,sysa = _mac_ver_lookup(('sysv','sysa')) 736 # Decode the infos 737 if sysv: 738 major = (sysv & 0xFF00) >> 8 739 minor = (sysv & 0x00F0) >> 4 740 patch = (sysv & 0x000F) 741 742 if (major, minor) >= (10, 4): 743 # the 'sysv' gestald cannot return patchlevels 744 # higher than 9. Apple introduced 3 new 745 # gestalt codes in 10.4 to deal with this 746 # issue (needed because patch levels can 747 # run higher than 9, such as 10.4.11) 748 major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3')) 749 release = '%i.%i.%i' %(major, minor, patch) 750 else: 751 release = '%s.%i.%i' % (_bcd2str(major),minor,patch) 752 753 if sysa: 754 machine = {0x1: '68k', 755 0x2: 'PowerPC', 756 0xa: 'i386'}.get(sysa,'') 757 758 versioninfo=('', '', '') 759 return release,versioninfo,machine 760 761def _mac_ver_xml(): 762 fn = '/System/Library/CoreServices/SystemVersion.plist' 763 if not os.path.exists(fn): 764 return None 765 766 try: 767 import plistlib 768 except ImportError: 769 return None 770 771 pl = plistlib.readPlist(fn) 772 release = pl['ProductVersion'] 773 versioninfo=('', '', '') 774 machine = os.uname()[4] 775 if machine in ('ppc', 'Power Macintosh'): 776 # for compatibility with the gestalt based code 777 machine = 'PowerPC' 778 779 return release,versioninfo,machine 780 781 782def mac_ver(release='',versioninfo=('','',''),machine=''): 783 784 """ Get MacOS version information and return it as tuple (release, 785 versioninfo, machine) with versioninfo being a tuple (version, 786 dev_stage, non_release_version). 787 788 Entries which cannot be determined are set to the parameter values 789 which default to ''. All tuple entries are strings. 790 """ 791 792 # First try reading the information from an XML file which should 793 # always be present 794 info = _mac_ver_xml() 795 if info is not None: 796 return info 797 798 # If that doesn't work for some reason fall back to reading the 799 # information using gestalt calls. 800 info = _mac_ver_gestalt() 801 if info is not None: 802 return info 803 804 # If that also doesn't work return the default values 805 return release,versioninfo,machine 806 807def _java_getprop(name,default): 808 809 from java.lang import System 810 try: 811 value = System.getProperty(name) 812 if value is None: 813 return default 814 return value 815 except AttributeError: 816 return default 817 818def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')): 819 820 """ Version interface for Jython. 821 822 Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being 823 a tuple (vm_name,vm_release,vm_vendor) and osinfo being a 824 tuple (os_name,os_version,os_arch). 825 826 Values which cannot be determined are set to the defaults 827 given as parameters (which all default to ''). 828 829 """ 830 # Import the needed APIs 831 try: 832 import java.lang 833 except ImportError: 834 return release,vendor,vminfo,osinfo 835 836 vendor = _java_getprop('java.vendor', vendor) 837 release = _java_getprop('java.version', release) 838 vm_name, vm_release, vm_vendor = vminfo 839 vm_name = _java_getprop('java.vm.name', vm_name) 840 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor) 841 vm_release = _java_getprop('java.vm.version', vm_release) 842 vminfo = vm_name, vm_release, vm_vendor 843 os_name, os_version, os_arch = osinfo 844 os_arch = _java_getprop('java.os.arch', os_arch) 845 os_name = _java_getprop('java.os.name', os_name) 846 os_version = _java_getprop('java.os.version', os_version) 847 osinfo = os_name, os_version, os_arch 848 849 return release, vendor, vminfo, osinfo 850 851### System name aliasing 852 853def system_alias(system,release,version): 854 855 """ Returns (system,release,version) aliased to common 856 marketing names used for some systems. 857 858 It also does some reordering of the information in some cases 859 where it would otherwise cause confusion. 860 861 """ 862 if system == 'Rhapsody': 863 # Apple's BSD derivative 864 # XXX How can we determine the marketing release number ? 865 return 'MacOS X Server',system+release,version 866 867 elif system == 'SunOS': 868 # Sun's OS 869 if release < '5': 870 # These releases use the old name SunOS 871 return system,release,version 872 # Modify release (marketing release = SunOS release - 3) 873 l = string.split(release,'.') 874 if l: 875 try: 876 major = int(l[0]) 877 except ValueError: 878 pass 879 else: 880 major = major - 3 881 l[0] = str(major) 882 release = string.join(l,'.') 883 if release < '6': 884 system = 'Solaris' 885 else: 886 # XXX Whatever the new SunOS marketing name is... 887 system = 'Solaris' 888 889 elif system == 'IRIX64': 890 # IRIX reports IRIX64 on platforms with 64-bit support; yet it 891 # is really a version and not a different platform, since 32-bit 892 # apps are also supported.. 893 system = 'IRIX' 894 if version: 895 version = version + ' (64bit)' 896 else: 897 version = '64bit' 898 899 elif system in ('win32','win16'): 900 # In case one of the other tricks 901 system = 'Windows' 902 903 return system,release,version 904 905### Various internal helpers 906 907def _platform(*args): 908 909 """ Helper to format the platform string in a filename 910 compatible format e.g. "system-version-machine". 911 """ 912 # Format the platform string 913 platform = string.join( 914 map(string.strip, 915 filter(len, args)), 916 '-') 917 918 # Cleanup some possible filename obstacles... 919 replace = string.replace 920 platform = replace(platform,' ','_') 921 platform = replace(platform,'/','-') 922 platform = replace(platform,'\\','-') 923 platform = replace(platform,':','-') 924 platform = replace(platform,';','-') 925 platform = replace(platform,'"','-') 926 platform = replace(platform,'(','-') 927 platform = replace(platform,')','-') 928 929 # No need to report 'unknown' information... 930 platform = replace(platform,'unknown','') 931 932 # Fold '--'s and remove trailing '-' 933 while 1: 934 cleaned = replace(platform,'--','-') 935 if cleaned == platform: 936 break 937 platform = cleaned 938 while platform[-1] == '-': 939 platform = platform[:-1] 940 941 return platform 942 943def _node(default=''): 944 945 """ Helper to determine the node name of this machine. 946 """ 947 try: 948 import socket 949 except ImportError: 950 # No sockets... 951 return default 952 try: 953 return socket.gethostname() 954 except socket.error: 955 # Still not working... 956 return default 957 958# os.path.abspath is new in Python 1.5.2: 959if not hasattr(os.path,'abspath'): 960 961 def _abspath(path, 962 963 isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd, 964 normpath=os.path.normpath): 965 966 if not isabs(path): 967 path = join(getcwd(), path) 968 return normpath(path) 969 970else: 971 972 _abspath = os.path.abspath 973 974def _follow_symlinks(filepath): 975 976 """ In case filepath is a symlink, follow it until a 977 real file is reached. 978 """ 979 filepath = _abspath(filepath) 980 while os.path.islink(filepath): 981 filepath = os.path.normpath( 982 os.path.join(os.path.dirname(filepath),os.readlink(filepath))) 983 return filepath 984 985def _syscmd_uname(option,default=''): 986 987 """ Interface to the system's uname command. 988 """ 989 if sys.platform in ('dos','win32','win16','os2'): 990 # XXX Others too ? 991 return default 992 try: 993 f = os.popen('uname %s 2> %s' % (option, DEV_NULL)) 994 except (AttributeError,os.error): 995 return default 996 output = string.strip(f.read()) 997 rc = f.close() 998 if not output or rc: 999 return default 1000 else: 1001 return output 1002 1003def _syscmd_file(target,default=''): 1004 1005 """ Interface to the system's file command. 1006 1007 The function uses the -b option of the file command to have it 1008 ommit the filename in its output and if possible the -L option 1009 to have the command follow symlinks. It returns default in 1010 case the command should fail. 1011 1012 """ 1013 1014 # We do the import here to avoid a bootstrap issue. 1015 # See c73b90b6dadd changeset. 1016 # 1017 # [..] 1018 # ranlib libpython2.7.a 1019 # gcc -o python \ 1020 # Modules/python.o \ 1021 # libpython2.7.a -lsocket -lnsl -ldl -lm 1022 # Traceback (most recent call last): 1023 # File "./setup.py", line 8, in <module> 1024 # from platform import machine as platform_machine 1025 # File "[..]/build/Lib/platform.py", line 116, in <module> 1026 # import sys,string,os,re,subprocess 1027 # File "[..]/build/Lib/subprocess.py", line 429, in <module> 1028 # import select 1029 # ImportError: No module named select 1030 1031 import subprocess 1032 1033 if sys.platform in ('dos','win32','win16','os2'): 1034 # XXX Others too ? 1035 return default 1036 target = _follow_symlinks(target) 1037 try: 1038 proc = subprocess.Popen(['file', target], 1039 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 1040 1041 except (AttributeError,os.error): 1042 return default 1043 output = proc.communicate()[0] 1044 rc = proc.wait() 1045 if not output or rc: 1046 return default 1047 else: 1048 return output 1049 1050### Information about the used architecture 1051 1052# Default values for architecture; non-empty strings override the 1053# defaults given as parameters 1054_default_architecture = { 1055 'win32': ('','WindowsPE'), 1056 'win16': ('','Windows'), 1057 'dos': ('','MSDOS'), 1058} 1059 1060_architecture_split = re.compile(r'[\s,]').split 1061 1062def architecture(executable=sys.executable,bits='',linkage=''): 1063 1064 """ Queries the given executable (defaults to the Python interpreter 1065 binary) for various architecture information. 1066 1067 Returns a tuple (bits,linkage) which contains information about 1068 the bit architecture and the linkage format used for the 1069 executable. Both values are returned as strings. 1070 1071 Values that cannot be determined are returned as given by the 1072 parameter presets. If bits is given as '', the sizeof(pointer) 1073 (or sizeof(long) on Python version < 1.5.2) is used as 1074 indicator for the supported pointer size. 1075 1076 The function relies on the system's "file" command to do the 1077 actual work. This is available on most if not all Unix 1078 platforms. On some non-Unix platforms where the "file" command 1079 does not exist and the executable is set to the Python interpreter 1080 binary defaults from _default_architecture are used. 1081 1082 """ 1083 # Use the sizeof(pointer) as default number of bits if nothing 1084 # else is given as default. 1085 if not bits: 1086 import struct 1087 try: 1088 size = struct.calcsize('P') 1089 except struct.error: 1090 # Older installations can only query longs 1091 size = struct.calcsize('l') 1092 bits = str(size*8) + 'bit' 1093 1094 # Get data from the 'file' system command 1095 if executable: 1096 output = _syscmd_file(executable, '') 1097 else: 1098 output = '' 1099 1100 if not output and \ 1101 executable == sys.executable: 1102 # "file" command did not return anything; we'll try to provide 1103 # some sensible defaults then... 1104 if sys.platform in _default_architecture: 1105 b, l = _default_architecture[sys.platform] 1106 if b: 1107 bits = b 1108 if l: 1109 linkage = l 1110 return bits, linkage 1111 1112 # Split the output into a list of strings omitting the filename 1113 fileout = _architecture_split(output)[1:] 1114 1115 if 'executable' not in fileout: 1116 # Format not supported 1117 return bits,linkage 1118 1119 # Bits 1120 if '32-bit' in fileout: 1121 bits = '32bit' 1122 elif 'N32' in fileout: 1123 # On Irix only 1124 bits = 'n32bit' 1125 elif '64-bit' in fileout: 1126 bits = '64bit' 1127 1128 # Linkage 1129 if 'ELF' in fileout: 1130 linkage = 'ELF' 1131 elif 'PE' in fileout: 1132 # E.g. Windows uses this format 1133 if 'Windows' in fileout: 1134 linkage = 'WindowsPE' 1135 else: 1136 linkage = 'PE' 1137 elif 'COFF' in fileout: 1138 linkage = 'COFF' 1139 elif 'MS-DOS' in fileout: 1140 linkage = 'MSDOS' 1141 else: 1142 # XXX the A.OUT format also falls under this class... 1143 pass 1144 1145 return bits,linkage 1146 1147### Portable uname() interface 1148 1149_uname_cache = None 1150 1151def uname(): 1152 1153 """ Fairly portable uname interface. Returns a tuple 1154 of strings (system,node,release,version,machine,processor) 1155 identifying the underlying platform. 1156 1157 Note that unlike the os.uname function this also returns 1158 possible processor information as an additional tuple entry. 1159 1160 Entries which cannot be determined are set to ''. 1161 1162 """ 1163 global _uname_cache 1164 no_os_uname = 0 1165 1166 if _uname_cache is not None: 1167 return _uname_cache 1168 1169 processor = '' 1170 1171 # Get some infos from the builtin os.uname API... 1172 try: 1173 system,node,release,version,machine = os.uname() 1174 except AttributeError: 1175 no_os_uname = 1 1176 1177 if no_os_uname or not filter(None, (system, node, release, version, machine)): 1178 # Hmm, no there is either no uname or uname has returned 1179 #'unknowns'... we'll have to poke around the system then. 1180 if no_os_uname: 1181 system = sys.platform 1182 release = '' 1183 version = '' 1184 node = _node() 1185 machine = '' 1186 1187 use_syscmd_ver = 1 1188 1189 # Try win32_ver() on win32 platforms 1190 if system == 'win32': 1191 release,version,csd,ptype = win32_ver() 1192 if release and version: 1193 use_syscmd_ver = 0 1194 # Try to use the PROCESSOR_* environment variables 1195 # available on Win XP and later; see 1196 # http://support.microsoft.com/kb/888731 and 1197 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM 1198 if not machine: 1199 # WOW64 processes mask the native architecture 1200 if "PROCESSOR_ARCHITEW6432" in os.environ: 1201 machine = os.environ.get("PROCESSOR_ARCHITEW6432", '') 1202 else: 1203 machine = os.environ.get('PROCESSOR_ARCHITECTURE', '') 1204 if not processor: 1205 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine) 1206 1207 # Try the 'ver' system command available on some 1208 # platforms 1209 if use_syscmd_ver: 1210 system,release,version = _syscmd_ver(system) 1211 # Normalize system to what win32_ver() normally returns 1212 # (_syscmd_ver() tends to return the vendor name as well) 1213 if system == 'Microsoft Windows': 1214 system = 'Windows' 1215 elif system == 'Microsoft' and release == 'Windows': 1216 # Under Windows Vista and Windows Server 2008, 1217 # Microsoft changed the output of the ver command. The 1218 # release is no longer printed. This causes the 1219 # system and release to be misidentified. 1220 system = 'Windows' 1221 if '6.0' == version[:3]: 1222 release = 'Vista' 1223 else: 1224 release = '' 1225 1226 # In case we still don't know anything useful, we'll try to 1227 # help ourselves 1228 if system in ('win32','win16'): 1229 if not version: 1230 if system == 'win32': 1231 version = '32bit' 1232 else: 1233 version = '16bit' 1234 system = 'Windows' 1235 1236 elif system[:4] == 'java': 1237 release,vendor,vminfo,osinfo = java_ver() 1238 system = 'Java' 1239 version = string.join(vminfo,', ') 1240 if not version: 1241 version = vendor 1242 1243 # System specific extensions 1244 if system == 'OpenVMS': 1245 # OpenVMS seems to have release and version mixed up 1246 if not release or release == '0': 1247 release = version 1248 version = '' 1249 # Get processor information 1250 try: 1251 import vms_lib 1252 except ImportError: 1253 pass 1254 else: 1255 csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0) 1256 if (cpu_number >= 128): 1257 processor = 'Alpha' 1258 else: 1259 processor = 'VAX' 1260 if not processor: 1261 # Get processor information from the uname system command 1262 processor = _syscmd_uname('-p','') 1263 1264 #If any unknowns still exist, replace them with ''s, which are more portable 1265 if system == 'unknown': 1266 system = '' 1267 if node == 'unknown': 1268 node = '' 1269 if release == 'unknown': 1270 release = '' 1271 if version == 'unknown': 1272 version = '' 1273 if machine == 'unknown': 1274 machine = '' 1275 if processor == 'unknown': 1276 processor = '' 1277 1278 # normalize name 1279 if system == 'Microsoft' and release == 'Windows': 1280 system = 'Windows' 1281 release = 'Vista' 1282 1283 _uname_cache = system,node,release,version,machine,processor 1284 return _uname_cache 1285 1286### Direct interfaces to some of the uname() return values 1287 1288def system(): 1289 1290 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'. 1291 1292 An empty string is returned if the value cannot be determined. 1293 1294 """ 1295 return uname()[0] 1296 1297def node(): 1298 1299 """ Returns the computer's network name (which may not be fully 1300 qualified) 1301 1302 An empty string is returned if the value cannot be determined. 1303 1304 """ 1305 return uname()[1] 1306 1307def release(): 1308 1309 """ Returns the system's release, e.g. '2.2.0' or 'NT' 1310 1311 An empty string is returned if the value cannot be determined. 1312 1313 """ 1314 return uname()[2] 1315 1316def version(): 1317 1318 """ Returns the system's release version, e.g. '#3 on degas' 1319 1320 An empty string is returned if the value cannot be determined. 1321 1322 """ 1323 return uname()[3] 1324 1325def machine(): 1326 1327 """ Returns the machine type, e.g. 'i386' 1328 1329 An empty string is returned if the value cannot be determined. 1330 1331 """ 1332 return uname()[4] 1333 1334def processor(): 1335 1336 """ Returns the (true) processor name, e.g. 'amdk6' 1337 1338 An empty string is returned if the value cannot be 1339 determined. Note that many platforms do not provide this 1340 information or simply return the same value as for machine(), 1341 e.g. NetBSD does this. 1342 1343 """ 1344 return uname()[5] 1345 1346### Various APIs for extracting information from sys.version 1347 1348_sys_version_parser = re.compile( 1349 r'([\w.+]+)\s*' # "version<space>" 1350 r'\(#?([^,]+)' # "(#buildno" 1351 r'(?:,\s*([\w ]*)' # ", builddate" 1352 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>" 1353 r'\[([^\]]+)\]?') # "[compiler]" 1354 1355_ironpython_sys_version_parser = re.compile( 1356 r'IronPython\s*' 1357 '([\d\.]+)' 1358 '(?: \(([\d\.]+)\))?' 1359 ' on (.NET [\d\.]+)') 1360 1361# IronPython covering 2.6 and 2.7 1362_ironpython26_sys_version_parser = re.compile( 1363 r'([\d.]+)\s*' 1364 '\(IronPython\s*' 1365 '[\d.]+\s*' 1366 '\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)' 1367) 1368 1369_pypy_sys_version_parser = re.compile( 1370 r'([\w.+]+)\s*' 1371 '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' 1372 '\[PyPy [^\]]+\]?') 1373 1374_sys_version_cache = {} 1375 1376def _sys_version(sys_version=None): 1377 1378 """ Returns a parsed version of Python's sys.version as tuple 1379 (name, version, branch, revision, buildno, builddate, compiler) 1380 referring to the Python implementation name, version, branch, 1381 revision, build number, build date/time as string and the compiler 1382 identification string. 1383 1384 Note that unlike the Python sys.version, the returned value 1385 for the Python version will always include the patchlevel (it 1386 defaults to '.0'). 1387 1388 The function returns empty strings for tuple entries that 1389 cannot be determined. 1390 1391 sys_version may be given to parse an alternative version 1392 string, e.g. if the version was read from a different Python 1393 interpreter. 1394 1395 """ 1396 # Get the Python version 1397 if sys_version is None: 1398 sys_version = sys.version 1399 1400 # Try the cache first 1401 result = _sys_version_cache.get(sys_version, None) 1402 if result is not None: 1403 return result 1404 1405 # Parse it 1406 if 'IronPython' in sys_version: 1407 # IronPython 1408 name = 'IronPython' 1409 if sys_version.startswith('IronPython'): 1410 match = _ironpython_sys_version_parser.match(sys_version) 1411 else: 1412 match = _ironpython26_sys_version_parser.match(sys_version) 1413 1414 if match is None: 1415 raise ValueError( 1416 'failed to parse IronPython sys.version: %s' % 1417 repr(sys_version)) 1418 1419 version, alt_version, compiler = match.groups() 1420 buildno = '' 1421 builddate = '' 1422 1423 elif sys.platform.startswith('java'): 1424 # Jython 1425 name = 'Jython' 1426 match = _sys_version_parser.match(sys_version) 1427 if match is None: 1428 raise ValueError( 1429 'failed to parse Jython sys.version: %s' % 1430 repr(sys_version)) 1431 version, buildno, builddate, buildtime, _ = match.groups() 1432 if builddate is None: 1433 builddate = '' 1434 compiler = sys.platform 1435 1436 elif "PyPy" in sys_version: 1437 # PyPy 1438 name = "PyPy" 1439 match = _pypy_sys_version_parser.match(sys_version) 1440 if match is None: 1441 raise ValueError("failed to parse PyPy sys.version: %s" % 1442 repr(sys_version)) 1443 version, buildno, builddate, buildtime = match.groups() 1444 compiler = "" 1445 1446 else: 1447 # CPython 1448 match = _sys_version_parser.match(sys_version) 1449 if match is None: 1450 raise ValueError( 1451 'failed to parse CPython sys.version: %s' % 1452 repr(sys_version)) 1453 version, buildno, builddate, buildtime, compiler = \ 1454 match.groups() 1455 name = 'CPython' 1456 if builddate is None: 1457 builddate = '' 1458 elif buildtime: 1459 builddate = builddate + ' ' + buildtime 1460 1461 if hasattr(sys, 'subversion'): 1462 # sys.subversion was added in Python 2.5 1463 _, branch, revision = sys.subversion 1464 else: 1465 branch = '' 1466 revision = '' 1467 1468 # Add the patchlevel version if missing 1469 l = string.split(version, '.') 1470 if len(l) == 2: 1471 l.append('0') 1472 version = string.join(l, '.') 1473 1474 # Build and cache the result 1475 result = (name, version, branch, revision, buildno, builddate, compiler) 1476 _sys_version_cache[sys_version] = result 1477 return result 1478 1479def python_implementation(): 1480 1481 """ Returns a string identifying the Python implementation. 1482 1483 Currently, the following implementations are identified: 1484 'CPython' (C implementation of Python), 1485 'IronPython' (.NET implementation of Python), 1486 'Jython' (Java implementation of Python), 1487 'PyPy' (Python implementation of Python). 1488 1489 """ 1490 return _sys_version()[0] 1491 1492def python_version(): 1493 1494 """ Returns the Python version as string 'major.minor.patchlevel' 1495 1496 Note that unlike the Python sys.version, the returned value 1497 will always include the patchlevel (it defaults to 0). 1498 1499 """ 1500 return _sys_version()[1] 1501 1502def python_version_tuple(): 1503 1504 """ Returns the Python version as tuple (major, minor, patchlevel) 1505 of strings. 1506 1507 Note that unlike the Python sys.version, the returned value 1508 will always include the patchlevel (it defaults to 0). 1509 1510 """ 1511 return tuple(string.split(_sys_version()[1], '.')) 1512 1513def python_branch(): 1514 1515 """ Returns a string identifying the Python implementation 1516 branch. 1517 1518 For CPython this is the Subversion branch from which the 1519 Python binary was built. 1520 1521 If not available, an empty string is returned. 1522 1523 """ 1524 1525 return _sys_version()[2] 1526 1527def python_revision(): 1528 1529 """ Returns a string identifying the Python implementation 1530 revision. 1531 1532 For CPython this is the Subversion revision from which the 1533 Python binary was built. 1534 1535 If not available, an empty string is returned. 1536 1537 """ 1538 return _sys_version()[3] 1539 1540def python_build(): 1541 1542 """ Returns a tuple (buildno, builddate) stating the Python 1543 build number and date as strings. 1544 1545 """ 1546 return _sys_version()[4:6] 1547 1548def python_compiler(): 1549 1550 """ Returns a string identifying the compiler used for compiling 1551 Python. 1552 1553 """ 1554 return _sys_version()[6] 1555 1556### The Opus Magnum of platform strings :-) 1557 1558_platform_cache = {} 1559 1560def platform(aliased=0, terse=0): 1561 1562 """ Returns a single string identifying the underlying platform 1563 with as much useful information as possible (but no more :). 1564 1565 The output is intended to be human readable rather than 1566 machine parseable. It may look different on different 1567 platforms and this is intended. 1568 1569 If "aliased" is true, the function will use aliases for 1570 various platforms that report system names which differ from 1571 their common names, e.g. SunOS will be reported as 1572 Solaris. The system_alias() function is used to implement 1573 this. 1574 1575 Setting terse to true causes the function to return only the 1576 absolute minimum information needed to identify the platform. 1577 1578 """ 1579 result = _platform_cache.get((aliased, terse), None) 1580 if result is not None: 1581 return result 1582 1583 # Get uname information and then apply platform specific cosmetics 1584 # to it... 1585 system,node,release,version,machine,processor = uname() 1586 if machine == processor: 1587 processor = '' 1588 if aliased: 1589 system,release,version = system_alias(system,release,version) 1590 1591 if system == 'Windows': 1592 # MS platforms 1593 rel,vers,csd,ptype = win32_ver(version) 1594 if terse: 1595 platform = _platform(system,release) 1596 else: 1597 platform = _platform(system,release,version,csd) 1598 1599 elif system in ('Linux',): 1600 # Linux based systems 1601 distname,distversion,distid = dist('') 1602 if distname and not terse: 1603 platform = _platform(system,release,machine,processor, 1604 'with', 1605 distname,distversion,distid) 1606 else: 1607 # If the distribution name is unknown check for libc vs. glibc 1608 libcname,libcversion = libc_ver(sys.executable) 1609 platform = _platform(system,release,machine,processor, 1610 'with', 1611 libcname+libcversion) 1612 elif system == 'Java': 1613 # Java platforms 1614 r,v,vminfo,(os_name,os_version,os_arch) = java_ver() 1615 if terse or not os_name: 1616 platform = _platform(system,release,version) 1617 else: 1618 platform = _platform(system,release,version, 1619 'on', 1620 os_name,os_version,os_arch) 1621 1622 elif system == 'MacOS': 1623 # MacOS platforms 1624 if terse: 1625 platform = _platform(system,release) 1626 else: 1627 platform = _platform(system,release,machine) 1628 1629 else: 1630 # Generic handler 1631 if terse: 1632 platform = _platform(system,release) 1633 else: 1634 bits,linkage = architecture(sys.executable) 1635 platform = _platform(system,release,machine,processor,bits,linkage) 1636 1637 _platform_cache[(aliased, terse)] = platform 1638 return platform 1639 1640### Command line interface 1641 1642if __name__ == '__main__': 1643 # Default is to print the aliased verbose platform string 1644 terse = ('terse' in sys.argv or '--terse' in sys.argv) 1645 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv) 1646 print platform(aliased,terse) 1647 sys.exit(0) 1648