• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <mal@egenix.com>.
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:mal@lemburg.com
97    Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
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