~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/platform.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

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