Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/platform.py: 11%
517 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1#!/usr/bin/env python3
3""" This module tries to retrieve as much platform-identifying data as
4 possible. It makes this information available via function APIs.
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.
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# * support for MS-DOS (PythonDX ?)
17# * support for Amiga and other still unsupported platforms running Python
18# * support for additional Linux distributions
19#
20# Many thanks to all those who helped adding platform-specific
21# checks (in no particular order):
22#
23# Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,
24# Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef
25# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
26# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
27# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
28# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
29# Dower
30#
31# History:
32#
33# <see CVS and SVN checkin messages for history>
34#
35# 1.0.8 - changed Windows support to read version from kernel32.dll
36# 1.0.7 - added DEV_NULL
37# 1.0.6 - added linux_distribution()
38# 1.0.5 - fixed Java support to allow running the module on Jython
39# 1.0.4 - added IronPython support
40# 1.0.3 - added normalization of Windows system name
41# 1.0.2 - added more Windows support
42# 1.0.1 - reformatted to make doc.py happy
43# 1.0.0 - reformatted a bit and checked into Python CVS
44# 0.8.0 - added sys.version parser and various new access
45# APIs (python_version(), python_compiler(), etc.)
46# 0.7.2 - fixed architecture() to use sizeof(pointer) where available
47# 0.7.1 - added support for Caldera OpenLinux
48# 0.7.0 - some fixes for WinCE; untabified the source file
49# 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and
50# vms_lib.getsyi() configured
51# 0.6.1 - added code to prevent 'uname -p' on platforms which are
52# known not to support it
53# 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;
54# did some cleanup of the interfaces - some APIs have changed
55# 0.5.5 - fixed another type in the MacOS code... should have
56# used more coffee today ;-)
57# 0.5.4 - fixed a few typos in the MacOS code
58# 0.5.3 - added experimental MacOS support; added better popen()
59# workarounds in _syscmd_ver() -- still not 100% elegant
60# though
61# 0.5.2 - fixed uname() to return '' instead of 'unknown' in all
62# return values (the system uname command tends to return
63# 'unknown' instead of just leaving the field empty)
64# 0.5.1 - included code for slackware dist; added exception handlers
65# to cover up situations where platforms don't have os.popen
66# (e.g. Mac) or fail on socket.gethostname(); fixed libc
67# detection RE
68# 0.5.0 - changed the API names referring to system commands to *syscmd*;
69# added java_ver(); made syscmd_ver() a private
70# API (was system_ver() in previous versions) -- use uname()
71# instead; extended the win32_ver() to also return processor
72# type information
73# 0.4.0 - added win32_ver() and modified the platform() output for WinXX
74# 0.3.4 - fixed a bug in _follow_symlinks()
75# 0.3.3 - fixed popen() and "file" command invocation bugs
76# 0.3.2 - added architecture() API and support for it in platform()
77# 0.3.1 - fixed syscmd_ver() RE to support Windows NT
78# 0.3.0 - added system alias support
79# 0.2.3 - removed 'wince' again... oh well.
80# 0.2.2 - added 'wince' to syscmd_ver() supported platforms
81# 0.2.1 - added cache logic and changed the platform string format
82# 0.2.0 - changed the API to use functions instead of module globals
83# since some action take too long to be run on module import
84# 0.1.0 - first release
85#
86# You can always get the latest version of this module at:
87#
88# http://www.egenix.com/files/python/platform.py
89#
90# If that URL should fail, try contacting the author.
92__copyright__ = """
93 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
94 Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
96 Permission to use, copy, modify, and distribute this software and its
97 documentation for any purpose and without fee or royalty is hereby granted,
98 provided that the above copyright notice appear in all copies and that
99 both that copyright notice and this permission notice appear in
100 supporting documentation or portions thereof, including modifications,
101 that you make.
103 EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
104 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
105 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
106 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
107 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
108 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
109 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
111"""
113__version__ = '1.0.8'
115import collections
116import os
117import re
118import sys
120### Globals & Constants
122# Helper for comparing two version number strings.
123# Based on the description of the PHP's version_compare():
124# http://php.net/manual/en/function.version-compare.php
126_ver_stages = {
127 # any string not found in this dict, will get 0 assigned
128 'dev': 10,
129 'alpha': 20, 'a': 20,
130 'beta': 30, 'b': 30,
131 'c': 40,
132 'RC': 50, 'rc': 50,
133 # number, will get 100 assigned
134 'pl': 200, 'p': 200,
135}
137_component_re = re.compile(r'([0-9]+|[._+-])')
139def _comparable_version(version):
140 result = []
141 for v in _component_re.split(version):
142 if v not in '._+-':
143 try:
144 v = int(v, 10)
145 t = 100
146 except ValueError:
147 t = _ver_stages.get(v, 0)
148 result.extend((t, v))
149 return result
151### Platform specific APIs
153_libc_search = re.compile(b'(__libc_init)'
154 b'|'
155 b'(GLIBC_([0-9.]+))'
156 b'|'
157 br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
159def libc_ver(executable=None, lib='', version='', chunksize=16384):
161 """ Tries to determine the libc version that the file executable
162 (which defaults to the Python interpreter) is linked against.
164 Returns a tuple of strings (lib,version) which default to the
165 given parameters in case the lookup fails.
167 Note that the function has intimate knowledge of how different
168 libc versions add symbols to the executable and thus is probably
169 only useable for executables compiled using gcc.
171 The file is read and scanned in chunks of chunksize bytes.
173 """
174 if executable is None:
175 try:
176 ver = os.confstr('CS_GNU_LIBC_VERSION')
177 # parse 'glibc 2.28' as ('glibc', '2.28')
178 parts = ver.split(maxsplit=1)
179 if len(parts) == 2:
180 return tuple(parts)
181 except (AttributeError, ValueError, OSError):
182 # os.confstr() or CS_GNU_LIBC_VERSION value not available
183 pass
185 executable = sys.executable
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 if b'libc' in binary or b'GLIBC' in binary:
198 m = _libc_search.search(binary, pos)
199 else:
200 m = None
201 if not m or m.end() == len(binary):
202 chunk = f.read(chunksize)
203 if chunk:
204 binary = binary[max(pos, len(binary) - 1000):] + chunk
205 pos = 0
206 continue
207 if not m:
208 break
209 libcinit, glibc, glibcversion, so, threads, soversion = [
210 s.decode('latin1') if s is not None else s
211 for s in m.groups()]
212 if libcinit and not lib:
213 lib = 'libc'
214 elif glibc:
215 if lib != 'glibc':
216 lib = 'glibc'
217 version = glibcversion
218 elif V(glibcversion) > V(version):
219 version = glibcversion
220 elif so:
221 if lib != 'glibc':
222 lib = 'libc'
223 if soversion and (not version or V(soversion) > V(version)):
224 version = soversion
225 if threads and version[-len(threads):] != threads:
226 version = version + threads
227 pos = m.end()
228 return lib, version
230def _norm_version(version, build=''):
232 """ Normalize the version and build strings and return a single
233 version string using the format major.minor.build (or patchlevel).
234 """
235 l = version.split('.')
236 if build:
237 l.append(build)
238 try:
239 ints = map(int, l)
240 except ValueError:
241 strings = l
242 else:
243 strings = list(map(str, ints))
244 version = '.'.join(strings[:3])
245 return version
247_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
248 r'.*'
249 r'\[.* ([\d.]+)\])')
251# Examples of VER command output:
252#
253# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195]
254# Windows XP: Microsoft Windows XP [Version 5.1.2600]
255# Windows Vista: Microsoft Windows [Version 6.0.6002]
256#
257# Note that the "Version" string gets localized on different
258# Windows versions.
260def _syscmd_ver(system='', release='', version='',
262 supported_platforms=('win32', 'win16', 'dos')):
264 """ Tries to figure out the OS version used and returns
265 a tuple (system, release, version).
267 It uses the "ver" shell command for this which is known
268 to exists on Windows, DOS. XXX Others too ?
270 In case this fails, the given parameters are used as
271 defaults.
273 """
274 if sys.platform not in supported_platforms:
275 return system, release, version
277 # Try some common cmd strings
278 import subprocess
279 for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
280 try:
281 info = subprocess.check_output(cmd,
282 stderr=subprocess.DEVNULL,
283 text=True,
284 shell=True)
285 except (OSError, subprocess.CalledProcessError) as why:
286 #print('Command %s failed: %s' % (cmd, why))
287 continue
288 else:
289 break
290 else:
291 return system, release, version
293 # Parse the output
294 info = info.strip()
295 m = _ver_output.match(info)
296 if m is not None:
297 system, release, version = m.groups()
298 # Strip trailing dots from version and release
299 if release[-1] == '.':
300 release = release[:-1]
301 if version[-1] == '.':
302 version = version[:-1]
303 # Normalize the version and build strings (eliminating additional
304 # zeros)
305 version = _norm_version(version)
306 return system, release, version
308_WIN32_CLIENT_RELEASES = {
309 (5, 0): "2000",
310 (5, 1): "XP",
311 # Strictly, 5.2 client is XP 64-bit, but platform.py historically
312 # has always called it 2003 Server
313 (5, 2): "2003Server",
314 (5, None): "post2003",
316 (6, 0): "Vista",
317 (6, 1): "7",
318 (6, 2): "8",
319 (6, 3): "8.1",
320 (6, None): "post8.1",
322 (10, 0): "10",
323 (10, None): "post10",
324}
326# Server release name lookup will default to client names if necessary
327_WIN32_SERVER_RELEASES = {
328 (5, 2): "2003Server",
330 (6, 0): "2008Server",
331 (6, 1): "2008ServerR2",
332 (6, 2): "2012Server",
333 (6, 3): "2012ServerR2",
334 (6, None): "post2012ServerR2",
335}
337def win32_is_iot():
338 return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
340def win32_edition():
341 try:
342 try:
343 import winreg
344 except ImportError:
345 import _winreg as winreg
346 except ImportError:
347 pass
348 else:
349 try:
350 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
351 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
352 return winreg.QueryValueEx(key, 'EditionId')[0]
353 except OSError:
354 pass
356 return None
358def win32_ver(release='', version='', csd='', ptype=''):
359 try:
360 from sys import getwindowsversion
361 except ImportError:
362 return release, version, csd, ptype
364 winver = getwindowsversion()
365 maj, min, build = winver.platform_version or winver[:3]
366 version = '{0}.{1}.{2}'.format(maj, min, build)
368 release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
369 _WIN32_CLIENT_RELEASES.get((maj, None)) or
370 release)
372 # getwindowsversion() reflect the compatibility mode Python is
373 # running under, and so the service pack value is only going to be
374 # valid if the versions match.
375 if winver[:2] == (maj, min):
376 try:
377 csd = 'SP{}'.format(winver.service_pack_major)
378 except AttributeError:
379 if csd[:13] == 'Service Pack ':
380 csd = 'SP' + csd[13:]
382 # VER_NT_SERVER = 3
383 if getattr(winver, 'product_type', None) == 3:
384 release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
385 _WIN32_SERVER_RELEASES.get((maj, None)) or
386 release)
388 try:
389 try:
390 import winreg
391 except ImportError:
392 import _winreg as winreg
393 except ImportError:
394 pass
395 else:
396 try:
397 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
398 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
399 ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
400 except OSError:
401 pass
403 return release, version, csd, ptype
406def _mac_ver_xml():
407 fn = '/System/Library/CoreServices/SystemVersion.plist'
408 if not os.path.exists(fn):
409 return None
411 try:
412 import plistlib
413 except ImportError:
414 return None
416 with open(fn, 'rb') as f:
417 pl = plistlib.load(f)
418 release = pl['ProductVersion']
419 versioninfo = ('', '', '')
420 machine = os.uname().machine
421 if machine in ('ppc', 'Power Macintosh'):
422 # Canonical name
423 machine = 'PowerPC'
425 return release, versioninfo, machine
428def mac_ver(release='', versioninfo=('', '', ''), machine=''):
430 """ Get macOS version information and return it as tuple (release,
431 versioninfo, machine) with versioninfo being a tuple (version,
432 dev_stage, non_release_version).
434 Entries which cannot be determined are set to the parameter values
435 which default to ''. All tuple entries are strings.
436 """
438 # First try reading the information from an XML file which should
439 # always be present
440 info = _mac_ver_xml()
441 if info is not None:
442 return info
444 # If that also doesn't work return the default values
445 return release, versioninfo, machine
447def _java_getprop(name, default):
449 from java.lang import System
450 try:
451 value = System.getProperty(name)
452 if value is None:
453 return default
454 return value
455 except AttributeError:
456 return default
458def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
460 """ Version interface for Jython.
462 Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
463 a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
464 tuple (os_name, os_version, os_arch).
466 Values which cannot be determined are set to the defaults
467 given as parameters (which all default to '').
469 """
470 # Import the needed APIs
471 try:
472 import java.lang
473 except ImportError:
474 return release, vendor, vminfo, osinfo
476 vendor = _java_getprop('java.vendor', vendor)
477 release = _java_getprop('java.version', release)
478 vm_name, vm_release, vm_vendor = vminfo
479 vm_name = _java_getprop('java.vm.name', vm_name)
480 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
481 vm_release = _java_getprop('java.vm.version', vm_release)
482 vminfo = vm_name, vm_release, vm_vendor
483 os_name, os_version, os_arch = osinfo
484 os_arch = _java_getprop('java.os.arch', os_arch)
485 os_name = _java_getprop('java.os.name', os_name)
486 os_version = _java_getprop('java.os.version', os_version)
487 osinfo = os_name, os_version, os_arch
489 return release, vendor, vminfo, osinfo
491### System name aliasing
493def system_alias(system, release, version):
495 """ Returns (system, release, version) aliased to common
496 marketing names used for some systems.
498 It also does some reordering of the information in some cases
499 where it would otherwise cause confusion.
501 """
502 if system == 'SunOS':
503 # Sun's OS
504 if release < '5':
505 # These releases use the old name SunOS
506 return system, release, version
507 # Modify release (marketing release = SunOS release - 3)
508 l = release.split('.')
509 if l:
510 try:
511 major = int(l[0])
512 except ValueError:
513 pass
514 else:
515 major = major - 3
516 l[0] = str(major)
517 release = '.'.join(l)
518 if release < '6':
519 system = 'Solaris'
520 else:
521 # XXX Whatever the new SunOS marketing name is...
522 system = 'Solaris'
524 elif system == 'IRIX64':
525 # IRIX reports IRIX64 on platforms with 64-bit support; yet it
526 # is really a version and not a different platform, since 32-bit
527 # apps are also supported..
528 system = 'IRIX'
529 if version:
530 version = version + ' (64bit)'
531 else:
532 version = '64bit'
534 elif system in ('win32', 'win16'):
535 # In case one of the other tricks
536 system = 'Windows'
538 # bpo-35516: Don't replace Darwin with macOS since input release and
539 # version arguments can be different than the currently running version.
541 return system, release, version
543### Various internal helpers
545def _platform(*args):
547 """ Helper to format the platform string in a filename
548 compatible format e.g. "system-version-machine".
549 """
550 # Format the platform string
551 platform = '-'.join(x.strip() for x in filter(len, args))
553 # Cleanup some possible filename obstacles...
554 platform = platform.replace(' ', '_')
555 platform = platform.replace('/', '-')
556 platform = platform.replace('\\', '-')
557 platform = platform.replace(':', '-')
558 platform = platform.replace(';', '-')
559 platform = platform.replace('"', '-')
560 platform = platform.replace('(', '-')
561 platform = platform.replace(')', '-')
563 # No need to report 'unknown' information...
564 platform = platform.replace('unknown', '')
566 # Fold '--'s and remove trailing '-'
567 while 1:
568 cleaned = platform.replace('--', '-')
569 if cleaned == platform:
570 break
571 platform = cleaned
572 while platform[-1] == '-':
573 platform = platform[:-1]
575 return platform
577def _node(default=''):
579 """ Helper to determine the node name of this machine.
580 """
581 try:
582 import socket
583 except ImportError:
584 # No sockets...
585 return default
586 try:
587 return socket.gethostname()
588 except OSError:
589 # Still not working...
590 return default
592def _follow_symlinks(filepath):
594 """ In case filepath is a symlink, follow it until a
595 real file is reached.
596 """
597 filepath = os.path.abspath(filepath)
598 while os.path.islink(filepath):
599 filepath = os.path.normpath(
600 os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
601 return filepath
603def _syscmd_uname(option, default=''):
605 """ Interface to the system's uname command.
606 """
607 if sys.platform in ('dos', 'win32', 'win16'):
608 # XXX Others too ?
609 return default
611 import subprocess
612 try:
613 output = subprocess.check_output(('uname', option),
614 stderr=subprocess.DEVNULL,
615 text=True)
616 except (OSError, subprocess.CalledProcessError):
617 return default
618 return (output.strip() or default)
620def _syscmd_file(target, default=''):
622 """ Interface to the system's file command.
624 The function uses the -b option of the file command to have it
625 omit the filename in its output. Follow the symlinks. It returns
626 default in case the command should fail.
628 """
629 if sys.platform in ('dos', 'win32', 'win16'):
630 # XXX Others too ?
631 return default
633 import subprocess
634 target = _follow_symlinks(target)
635 # "file" output is locale dependent: force the usage of the C locale
636 # to get deterministic behavior.
637 env = dict(os.environ, LC_ALL='C')
638 try:
639 # -b: do not prepend filenames to output lines (brief mode)
640 output = subprocess.check_output(['file', '-b', target],
641 stderr=subprocess.DEVNULL,
642 env=env)
643 except (OSError, subprocess.CalledProcessError):
644 return default
645 if not output:
646 return default
647 # With the C locale, the output should be mostly ASCII-compatible.
648 # Decode from Latin-1 to prevent Unicode decode error.
649 return output.decode('latin-1')
651### Information about the used architecture
653# Default values for architecture; non-empty strings override the
654# defaults given as parameters
655_default_architecture = {
656 'win32': ('', 'WindowsPE'),
657 'win16': ('', 'Windows'),
658 'dos': ('', 'MSDOS'),
659}
661def architecture(executable=sys.executable, bits='', linkage=''):
663 """ Queries the given executable (defaults to the Python interpreter
664 binary) for various architecture information.
666 Returns a tuple (bits, linkage) which contains information about
667 the bit architecture and the linkage format used for the
668 executable. Both values are returned as strings.
670 Values that cannot be determined are returned as given by the
671 parameter presets. If bits is given as '', the sizeof(pointer)
672 (or sizeof(long) on Python version < 1.5.2) is used as
673 indicator for the supported pointer size.
675 The function relies on the system's "file" command to do the
676 actual work. This is available on most if not all Unix
677 platforms. On some non-Unix platforms where the "file" command
678 does not exist and the executable is set to the Python interpreter
679 binary defaults from _default_architecture are used.
681 """
682 # Use the sizeof(pointer) as default number of bits if nothing
683 # else is given as default.
684 if not bits:
685 import struct
686 size = struct.calcsize('P')
687 bits = str(size * 8) + 'bit'
689 # Get data from the 'file' system command
690 if executable:
691 fileout = _syscmd_file(executable, '')
692 else:
693 fileout = ''
695 if not fileout and \
696 executable == sys.executable:
697 # "file" command did not return anything; we'll try to provide
698 # some sensible defaults then...
699 if sys.platform in _default_architecture:
700 b, l = _default_architecture[sys.platform]
701 if b:
702 bits = b
703 if l:
704 linkage = l
705 return bits, linkage
707 if 'executable' not in fileout and 'shared object' not in fileout:
708 # Format not supported
709 return bits, linkage
711 # Bits
712 if '32-bit' in fileout:
713 bits = '32bit'
714 elif 'N32' in fileout:
715 # On Irix only
716 bits = 'n32bit'
717 elif '64-bit' in fileout:
718 bits = '64bit'
720 # Linkage
721 if 'ELF' in fileout:
722 linkage = 'ELF'
723 elif 'PE' in fileout:
724 # E.g. Windows uses this format
725 if 'Windows' in fileout:
726 linkage = 'WindowsPE'
727 else:
728 linkage = 'PE'
729 elif 'COFF' in fileout:
730 linkage = 'COFF'
731 elif 'MS-DOS' in fileout:
732 linkage = 'MSDOS'
733 else:
734 # XXX the A.OUT format also falls under this class...
735 pass
737 return bits, linkage
739### Portable uname() interface
741uname_result = collections.namedtuple("uname_result",
742 "system node release version machine processor")
744_uname_cache = None
746def uname():
748 """ Fairly portable uname interface. Returns a tuple
749 of strings (system, node, release, version, machine, processor)
750 identifying the underlying platform.
752 Note that unlike the os.uname function this also returns
753 possible processor information as an additional tuple entry.
755 Entries which cannot be determined are set to ''.
757 """
758 global _uname_cache
759 no_os_uname = 0
761 if _uname_cache is not None:
762 return _uname_cache
764 processor = ''
766 # Get some infos from the builtin os.uname API...
767 try:
768 system, node, release, version, machine = os.uname()
769 except AttributeError:
770 no_os_uname = 1
772 if no_os_uname or not list(filter(None, (system, node, release, version, machine))):
773 # Hmm, no there is either no uname or uname has returned
774 #'unknowns'... we'll have to poke around the system then.
775 if no_os_uname:
776 system = sys.platform
777 release = ''
778 version = ''
779 node = _node()
780 machine = ''
782 use_syscmd_ver = 1
784 # Try win32_ver() on win32 platforms
785 if system == 'win32':
786 release, version, csd, ptype = win32_ver()
787 if release and version:
788 use_syscmd_ver = 0
789 # Try to use the PROCESSOR_* environment variables
790 # available on Win XP and later; see
791 # http://support.microsoft.com/kb/888731 and
792 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
793 if not machine:
794 # WOW64 processes mask the native architecture
795 if "PROCESSOR_ARCHITEW6432" in os.environ:
796 machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
797 else:
798 machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
799 if not processor:
800 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
802 # Try the 'ver' system command available on some
803 # platforms
804 if use_syscmd_ver:
805 system, release, version = _syscmd_ver(system)
806 # Normalize system to what win32_ver() normally returns
807 # (_syscmd_ver() tends to return the vendor name as well)
808 if system == 'Microsoft Windows':
809 system = 'Windows'
810 elif system == 'Microsoft' and release == 'Windows':
811 # Under Windows Vista and Windows Server 2008,
812 # Microsoft changed the output of the ver command. The
813 # release is no longer printed. This causes the
814 # system and release to be misidentified.
815 system = 'Windows'
816 if '6.0' == version[:3]:
817 release = 'Vista'
818 else:
819 release = ''
821 # In case we still don't know anything useful, we'll try to
822 # help ourselves
823 if system in ('win32', 'win16'):
824 if not version:
825 if system == 'win32':
826 version = '32bit'
827 else:
828 version = '16bit'
829 system = 'Windows'
831 elif system[:4] == 'java':
832 release, vendor, vminfo, osinfo = java_ver()
833 system = 'Java'
834 version = ', '.join(vminfo)
835 if not version:
836 version = vendor
838 # System specific extensions
839 if system == 'OpenVMS':
840 # OpenVMS seems to have release and version mixed up
841 if not release or release == '0':
842 release = version
843 version = ''
844 # Get processor information
845 try:
846 import vms_lib
847 except ImportError:
848 pass
849 else:
850 csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
851 if (cpu_number >= 128):
852 processor = 'Alpha'
853 else:
854 processor = 'VAX'
855 if not processor:
856 # Get processor information from the uname system command
857 processor = _syscmd_uname('-p', '')
859 #If any unknowns still exist, replace them with ''s, which are more portable
860 if system == 'unknown':
861 system = ''
862 if node == 'unknown':
863 node = ''
864 if release == 'unknown':
865 release = ''
866 if version == 'unknown':
867 version = ''
868 if machine == 'unknown':
869 machine = ''
870 if processor == 'unknown':
871 processor = ''
873 # normalize name
874 if system == 'Microsoft' and release == 'Windows':
875 system = 'Windows'
876 release = 'Vista'
878 _uname_cache = uname_result(system, node, release, version,
879 machine, processor)
880 return _uname_cache
882### Direct interfaces to some of the uname() return values
884def system():
886 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
888 An empty string is returned if the value cannot be determined.
890 """
891 return uname().system
893def node():
895 """ Returns the computer's network name (which may not be fully
896 qualified)
898 An empty string is returned if the value cannot be determined.
900 """
901 return uname().node
903def release():
905 """ Returns the system's release, e.g. '2.2.0' or 'NT'
907 An empty string is returned if the value cannot be determined.
909 """
910 return uname().release
912def version():
914 """ Returns the system's release version, e.g. '#3 on degas'
916 An empty string is returned if the value cannot be determined.
918 """
919 return uname().version
921def machine():
923 """ Returns the machine type, e.g. 'i386'
925 An empty string is returned if the value cannot be determined.
927 """
928 return uname().machine
930def processor():
932 """ Returns the (true) processor name, e.g. 'amdk6'
934 An empty string is returned if the value cannot be
935 determined. Note that many platforms do not provide this
936 information or simply return the same value as for machine(),
937 e.g. NetBSD does this.
939 """
940 return uname().processor
942### Various APIs for extracting information from sys.version
944_sys_version_parser = re.compile(
945 r'([\w.+]+)\s*' # "version<space>"
946 r'\(#?([^,]+)' # "(#buildno"
947 r'(?:,\s*([\w ]*)' # ", builddate"
948 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
949 r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
951_ironpython_sys_version_parser = re.compile(
952 r'IronPython\s*'
953 r'([\d\.]+)'
954 r'(?: \(([\d\.]+)\))?'
955 r' on (.NET [\d\.]+)', re.ASCII)
957# IronPython covering 2.6 and 2.7
958_ironpython26_sys_version_parser = re.compile(
959 r'([\d.]+)\s*'
960 r'\(IronPython\s*'
961 r'[\d.]+\s*'
962 r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
963)
965_pypy_sys_version_parser = re.compile(
966 r'([\w.+]+)\s*'
967 r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
968 r'\[PyPy [^\]]+\]?')
970_sys_version_cache = {}
972def _sys_version(sys_version=None):
974 """ Returns a parsed version of Python's sys.version as tuple
975 (name, version, branch, revision, buildno, builddate, compiler)
976 referring to the Python implementation name, version, branch,
977 revision, build number, build date/time as string and the compiler
978 identification string.
980 Note that unlike the Python sys.version, the returned value
981 for the Python version will always include the patchlevel (it
982 defaults to '.0').
984 The function returns empty strings for tuple entries that
985 cannot be determined.
987 sys_version may be given to parse an alternative version
988 string, e.g. if the version was read from a different Python
989 interpreter.
991 """
992 # Get the Python version
993 if sys_version is None:
994 sys_version = sys.version
996 # Try the cache first
997 result = _sys_version_cache.get(sys_version, None)
998 if result is not None:
999 return result
1001 # Parse it
1002 if 'IronPython' in sys_version:
1003 # IronPython
1004 name = 'IronPython'
1005 if sys_version.startswith('IronPython'):
1006 match = _ironpython_sys_version_parser.match(sys_version)
1007 else:
1008 match = _ironpython26_sys_version_parser.match(sys_version)
1010 if match is None:
1011 raise ValueError(
1012 'failed to parse IronPython sys.version: %s' %
1013 repr(sys_version))
1015 version, alt_version, compiler = match.groups()
1016 buildno = ''
1017 builddate = ''
1019 elif sys.platform.startswith('java'):
1020 # Jython
1021 name = 'Jython'
1022 match = _sys_version_parser.match(sys_version)
1023 if match is None:
1024 raise ValueError(
1025 'failed to parse Jython sys.version: %s' %
1026 repr(sys_version))
1027 version, buildno, builddate, buildtime, _ = match.groups()
1028 if builddate is None:
1029 builddate = ''
1030 compiler = sys.platform
1032 elif "PyPy" in sys_version:
1033 # PyPy
1034 name = "PyPy"
1035 match = _pypy_sys_version_parser.match(sys_version)
1036 if match is None:
1037 raise ValueError("failed to parse PyPy sys.version: %s" %
1038 repr(sys_version))
1039 version, buildno, builddate, buildtime = match.groups()
1040 compiler = ""
1042 else:
1043 # CPython
1044 match = _sys_version_parser.match(sys_version)
1045 if match is None:
1046 raise ValueError(
1047 'failed to parse CPython sys.version: %s' %
1048 repr(sys_version))
1049 version, buildno, builddate, buildtime, compiler = \
1050 match.groups()
1051 name = 'CPython'
1052 if builddate is None:
1053 builddate = ''
1054 elif buildtime:
1055 builddate = builddate + ' ' + buildtime
1057 if hasattr(sys, '_git'):
1058 _, branch, revision = sys._git
1059 elif hasattr(sys, '_mercurial'):
1060 _, branch, revision = sys._mercurial
1061 else:
1062 branch = ''
1063 revision = ''
1065 # Add the patchlevel version if missing
1066 l = version.split('.')
1067 if len(l) == 2:
1068 l.append('0')
1069 version = '.'.join(l)
1071 # Build and cache the result
1072 result = (name, version, branch, revision, buildno, builddate, compiler)
1073 _sys_version_cache[sys_version] = result
1074 return result
1076def python_implementation():
1078 """ Returns a string identifying the Python implementation.
1080 Currently, the following implementations are identified:
1081 'CPython' (C implementation of Python),
1082 'IronPython' (.NET implementation of Python),
1083 'Jython' (Java implementation of Python),
1084 'PyPy' (Python implementation of Python).
1086 """
1087 return _sys_version()[0]
1089def python_version():
1091 """ Returns the Python version as string 'major.minor.patchlevel'
1093 Note that unlike the Python sys.version, the returned value
1094 will always include the patchlevel (it defaults to 0).
1096 """
1097 return _sys_version()[1]
1099def python_version_tuple():
1101 """ Returns the Python version as tuple (major, minor, patchlevel)
1102 of strings.
1104 Note that unlike the Python sys.version, the returned value
1105 will always include the patchlevel (it defaults to 0).
1107 """
1108 return tuple(_sys_version()[1].split('.'))
1110def python_branch():
1112 """ Returns a string identifying the Python implementation
1113 branch.
1115 For CPython this is the SCM branch from which the
1116 Python binary was built.
1118 If not available, an empty string is returned.
1120 """
1122 return _sys_version()[2]
1124def python_revision():
1126 """ Returns a string identifying the Python implementation
1127 revision.
1129 For CPython this is the SCM revision from which the
1130 Python binary was built.
1132 If not available, an empty string is returned.
1134 """
1135 return _sys_version()[3]
1137def python_build():
1139 """ Returns a tuple (buildno, builddate) stating the Python
1140 build number and date as strings.
1142 """
1143 return _sys_version()[4:6]
1145def python_compiler():
1147 """ Returns a string identifying the compiler used for compiling
1148 Python.
1150 """
1151 return _sys_version()[6]
1153### The Opus Magnum of platform strings :-)
1155_platform_cache = {}
1157def platform(aliased=0, terse=0):
1159 """ Returns a single string identifying the underlying platform
1160 with as much useful information as possible (but no more :).
1162 The output is intended to be human readable rather than
1163 machine parseable. It may look different on different
1164 platforms and this is intended.
1166 If "aliased" is true, the function will use aliases for
1167 various platforms that report system names which differ from
1168 their common names, e.g. SunOS will be reported as
1169 Solaris. The system_alias() function is used to implement
1170 this.
1172 Setting terse to true causes the function to return only the
1173 absolute minimum information needed to identify the platform.
1175 """
1176 result = _platform_cache.get((aliased, terse), None)
1177 if result is not None:
1178 return result
1180 # Get uname information and then apply platform specific cosmetics
1181 # to it...
1182 system, node, release, version, machine, processor = uname()
1183 if machine == processor:
1184 processor = ''
1185 if aliased:
1186 system, release, version = system_alias(system, release, version)
1188 if system == 'Darwin':
1189 # macOS (darwin kernel)
1190 macos_release = mac_ver()[0]
1191 if macos_release:
1192 system = 'macOS'
1193 release = macos_release
1195 if system == 'Windows':
1196 # MS platforms
1197 rel, vers, csd, ptype = win32_ver(version)
1198 if terse:
1199 platform = _platform(system, release)
1200 else:
1201 platform = _platform(system, release, version, csd)
1203 elif system in ('Linux',):
1204 # check for libc vs. glibc
1205 libcname, libcversion = libc_ver(sys.executable)
1206 platform = _platform(system, release, machine, processor,
1207 'with',
1208 libcname+libcversion)
1209 elif system == 'Java':
1210 # Java platforms
1211 r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
1212 if terse or not os_name:
1213 platform = _platform(system, release, version)
1214 else:
1215 platform = _platform(system, release, version,
1216 'on',
1217 os_name, os_version, os_arch)
1219 else:
1220 # Generic handler
1221 if terse:
1222 platform = _platform(system, release)
1223 else:
1224 bits, linkage = architecture(sys.executable)
1225 platform = _platform(system, release, machine,
1226 processor, bits, linkage)
1228 _platform_cache[(aliased, terse)] = platform
1229 return platform
1231### Command line interface
1233if __name__ == '__main__':
1234 # Default is to print the aliased verbose platform string
1235 terse = ('terse' in sys.argv or '--terse' in sys.argv)
1236 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
1237 print(platform(aliased, terse))
1238 sys.exit(0)