blob: d567dd1a6e1ad7d58afd53a3d42be68070e77eea [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#!/usr/bin/env python3
Marc-André Lemburg246d8472003-04-24 11:36:11 +00002
Brett Cannon8ab27df2003-08-05 03:52:04 +00003""" This module tries to retrieve as much platform-identifying data as
Marc-André Lemburg246d8472003-04-24 11:36:11 +00004 possible. It makes this information available via function APIs.
5
6 If called from the command line, it prints the platform
7 information concatenated as single string to stdout. The output
8 format is useable as part of a filename.
9
10"""
11# This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.
12# If you find problems, please submit bug reports/patches via the
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000013# Python bug tracker (http://bugs.python.org) and assign them to "lemburg".
Marc-André Lemburg246d8472003-04-24 11:36:11 +000014#
Marc-André Lemburg246d8472003-04-24 11:36:11 +000015# Still needed:
Marc-André Lemburg246d8472003-04-24 11:36:11 +000016# * support for MS-DOS (PythonDX ?)
17# * support for Amiga and other still unsupported platforms running Python
18# * support for additional Linux distributions
19#
Brett Cannon8ab27df2003-08-05 03:52:04 +000020# Many thanks to all those who helped adding platform-specific
Marc-André Lemburg246d8472003-04-24 11:36:11 +000021# 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),
Steve Dowerb9f4fea2015-09-22 17:23:39 -070028# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
29# Dower
Marc-André Lemburg246d8472003-04-24 11:36:11 +000030#
31# History:
Marc-André Lemburg380f4172005-11-07 16:11:02 +000032#
33# <see CVS and SVN checkin messages for history>
34#
Steve Dowerb9f4fea2015-09-22 17:23:39 -070035# 1.0.8 - changed Windows support to read version from kernel32.dll
Alexandre Vassalottie52e3782009-07-17 09:18:18 +000036# 1.0.7 - added DEV_NULL
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +000037# 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
Marc-André Lemburgcdc79232004-06-19 17:17:00 +000040# 1.0.3 - added normalization of Windows system name
Marc-André Lemburg91e83e22004-03-25 18:35:12 +000041# 1.0.2 - added more Windows support
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +000042# 1.0.1 - reformatted to make doc.py happy
Marc-André Lemburg246d8472003-04-24 11:36:11 +000043# 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
Martin Pantereb995702016-07-28 01:11:04 +000063# 'unknown' instead of just leaving the field empty)
Marc-André Lemburg246d8472003-04-24 11:36:11 +000064# 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()
penguindustin96466302019-05-06 14:57:17 -040075# 0.3.3 - fixed popen() and "file" command invocation bugs
Marc-André Lemburg246d8472003-04-24 11:36:11 +000076# 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.
91
92__copyright__ = """
93 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
Benjamin Peterson46a99002010-01-09 18:45:30 +000094 Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
Marc-André Lemburg246d8472003-04-24 11:36:11 +000095
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.
102
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 !
110
111"""
112
Matthias Bussonnier6059ce42017-02-24 02:47:34 -0800113__version__ = '1.0.8'
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000114
Larry Hastings68386bc2012-06-24 14:30:41 -0700115import collections
Victor Stinnerb8e689a2018-12-04 17:18:12 +0100116import os
117import re
118import sys
Jason R. Coombs518835f2020-04-16 08:28:09 -0400119import subprocess
120import functools
121import itertools
Berker Peksag1392f712015-05-16 20:24:28 +0300122
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000123### Globals & Constants
124
Serhiy Storchaka7d81e8f2018-08-27 13:29:51 +0300125# Helper for comparing two version number strings.
126# Based on the description of the PHP's version_compare():
127# http://php.net/manual/en/function.version-compare.php
128
129_ver_stages = {
130 # any string not found in this dict, will get 0 assigned
131 'dev': 10,
132 'alpha': 20, 'a': 20,
133 'beta': 30, 'b': 30,
134 'c': 40,
135 'RC': 50, 'rc': 50,
136 # number, will get 100 assigned
137 'pl': 200, 'p': 200,
138}
139
140_component_re = re.compile(r'([0-9]+|[._+-])')
141
142def _comparable_version(version):
143 result = []
144 for v in _component_re.split(version):
145 if v not in '._+-':
146 try:
147 v = int(v, 10)
148 t = 100
149 except ValueError:
150 t = _ver_stages.get(v, 0)
151 result.extend((t, v))
152 return result
153
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000154### Platform specific APIs
155
Antoine Pitrouba7c2262011-10-07 13:26:59 +0200156_libc_search = re.compile(b'(__libc_init)'
157 b'|'
158 b'(GLIBC_([0-9.]+))'
159 b'|'
160 br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000161
Victor Stinner476b1132018-12-05 14:04:52 +0100162def libc_ver(executable=None, lib='', version='', chunksize=16384):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000163
Brett Cannon8ab27df2003-08-05 03:52:04 +0000164 """ Tries to determine the libc version that the file executable
165 (which defaults to the Python interpreter) is linked against.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000166
167 Returns a tuple of strings (lib,version) which default to the
168 given parameters in case the lookup fails.
169
170 Note that the function has intimate knowledge of how different
Brett Cannon8ab27df2003-08-05 03:52:04 +0000171 libc versions add symbols to the executable and thus is probably
172 only useable for executables compiled using gcc.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000173
174 The file is read and scanned in chunks of chunksize bytes.
175
176 """
Kurochand9142832021-01-03 01:03:53 +0900177 if not executable:
Victor Stinner476b1132018-12-05 14:04:52 +0100178 try:
179 ver = os.confstr('CS_GNU_LIBC_VERSION')
180 # parse 'glibc 2.28' as ('glibc', '2.28')
181 parts = ver.split(maxsplit=1)
182 if len(parts) == 2:
183 return tuple(parts)
184 except (AttributeError, ValueError, OSError):
185 # os.confstr() or CS_GNU_LIBC_VERSION value not available
186 pass
187
188 executable = sys.executable
189
Serhiy Storchaka7d81e8f2018-08-27 13:29:51 +0300190 V = _comparable_version
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000191 if hasattr(os.path, 'realpath'):
192 # Python 2.2 introduced os.path.realpath(); it is used
193 # here to work around problems with Cygwin not being
194 # able to open symlinks for reading
195 executable = os.path.realpath(executable)
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300196 with open(executable, 'rb') as f:
197 binary = f.read(chunksize)
198 pos = 0
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300199 while pos < len(binary):
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300200 if b'libc' in binary or b'GLIBC' in binary:
201 m = _libc_search.search(binary, pos)
202 else:
203 m = None
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300204 if not m or m.end() == len(binary):
205 chunk = f.read(chunksize)
206 if chunk:
207 binary = binary[max(pos, len(binary) - 1000):] + chunk
208 pos = 0
209 continue
210 if not m:
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300211 break
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300212 libcinit, glibc, glibcversion, so, threads, soversion = [
213 s.decode('latin1') if s is not None else s
214 for s in m.groups()]
215 if libcinit and not lib:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000216 lib = 'libc'
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300217 elif glibc:
218 if lib != 'glibc':
219 lib = 'glibc'
220 version = glibcversion
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300221 elif V(glibcversion) > V(version):
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300222 version = glibcversion
223 elif so:
224 if lib != 'glibc':
225 lib = 'libc'
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300226 if soversion and (not version or V(soversion) > V(version)):
Serhiy Storchaka46ba6c82015-04-04 11:01:02 +0300227 version = soversion
228 if threads and version[-len(threads):] != threads:
229 version = version + threads
230 pos = m.end()
Victor Stinnerced39362013-12-09 00:14:52 +0100231 return lib, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000232
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000233def _norm_version(version, build=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000234
Brett Cannon8ab27df2003-08-05 03:52:04 +0000235 """ Normalize the version and build strings and return a single
Walter Dörwalde5a7fad2005-11-21 17:01:27 +0000236 version string using the format major.minor.build (or patchlevel).
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000237 """
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000238 l = version.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000239 if build:
240 l.append(build)
241 try:
Victor Stinnerced39362013-12-09 00:14:52 +0100242 ints = map(int, l)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000243 except ValueError:
244 strings = l
245 else:
Victor Stinnerced39362013-12-09 00:14:52 +0100246 strings = list(map(str, ints))
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000247 version = '.'.join(strings[:3])
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000248 return version
249
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000250_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
R David Murray44b548d2016-09-08 13:59:53 -0400251 r'.*'
252 r'\[.* ([\d.]+)\])')
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000253
254# Examples of VER command output:
255#
256# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195]
257# Windows XP: Microsoft Windows XP [Version 5.1.2600]
258# Windows Vista: Microsoft Windows [Version 6.0.6002]
259#
260# Note that the "Version" string gets localized on different
261# Windows versions.
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000262
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000263def _syscmd_ver(system='', release='', version='',
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000264
Victor Stinnerced39362013-12-09 00:14:52 +0100265 supported_platforms=('win32', 'win16', 'dos')):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000266
267 """ Tries to figure out the OS version used and returns
Victor Stinnerced39362013-12-09 00:14:52 +0100268 a tuple (system, release, version).
Tim Peters0eadaac2003-04-24 16:02:54 +0000269
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000270 It uses the "ver" shell command for this which is known
Jesus Ceaf1af7052012-10-05 02:48:46 +0200271 to exists on Windows, DOS. XXX Others too ?
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000272
273 In case this fails, the given parameters are used as
274 defaults.
275
276 """
277 if sys.platform not in supported_platforms:
Victor Stinnerced39362013-12-09 00:14:52 +0100278 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000279
280 # Try some common cmd strings
Victor Stinner3a521f02018-12-07 11:10:33 +0100281 import subprocess
Victor Stinnerced39362013-12-09 00:14:52 +0100282 for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000283 try:
Victor Stinner3a521f02018-12-07 11:10:33 +0100284 info = subprocess.check_output(cmd,
285 stderr=subprocess.DEVNULL,
286 text=True,
287 shell=True)
288 except (OSError, subprocess.CalledProcessError) as why:
289 #print('Command %s failed: %s' % (cmd, why))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000290 continue
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000291 else:
292 break
293 else:
Victor Stinnerced39362013-12-09 00:14:52 +0100294 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000295
296 # Parse the output
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000297 info = info.strip()
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000298 m = _ver_output.match(info)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000299 if m is not None:
Victor Stinnerced39362013-12-09 00:14:52 +0100300 system, release, version = m.groups()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000301 # Strip trailing dots from version and release
302 if release[-1] == '.':
303 release = release[:-1]
304 if version[-1] == '.':
305 version = version[:-1]
306 # Normalize the version and build strings (eliminating additional
307 # zeros)
308 version = _norm_version(version)
Victor Stinnerced39362013-12-09 00:14:52 +0100309 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000310
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700311_WIN32_CLIENT_RELEASES = {
312 (5, 0): "2000",
313 (5, 1): "XP",
314 # Strictly, 5.2 client is XP 64-bit, but platform.py historically
315 # has always called it 2003 Server
316 (5, 2): "2003Server",
317 (5, None): "post2003",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000318
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700319 (6, 0): "Vista",
320 (6, 1): "7",
321 (6, 2): "8",
322 (6, 3): "8.1",
323 (6, None): "post8.1",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000324
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700325 (10, 0): "10",
326 (10, None): "post10",
327}
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000328
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700329# Server release name lookup will default to client names if necessary
330_WIN32_SERVER_RELEASES = {
331 (5, 2): "2003Server",
332
333 (6, 0): "2008Server",
334 (6, 1): "2008ServerR2",
335 (6, 2): "2012Server",
336 (6, 3): "2012ServerR2",
337 (6, None): "post2012ServerR2",
338}
339
Paul Monson62dfd7d2019-04-25 11:36:45 -0700340def win32_is_iot():
341 return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
342
343def win32_edition():
344 try:
345 try:
346 import winreg
347 except ImportError:
348 import _winreg as winreg
349 except ImportError:
350 pass
351 else:
352 try:
353 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
354 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
355 return winreg.QueryValueEx(key, 'EditionId')[0]
356 except OSError:
357 pass
358
359 return None
360
Victor Stinnerced39362013-12-09 00:14:52 +0100361def win32_ver(release='', version='', csd='', ptype=''):
Steve Dower8f278f12015-09-22 17:35:24 -0700362 try:
363 from sys import getwindowsversion
364 except ImportError:
365 return release, version, csd, ptype
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700366
367 winver = getwindowsversion()
Steve Dower74f4af72016-09-17 17:27:48 -0700368 maj, min, build = winver.platform_version or winver[:3]
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700369 version = '{0}.{1}.{2}'.format(maj, min, build)
370
371 release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
372 _WIN32_CLIENT_RELEASES.get((maj, None)) or
373 release)
374
375 # getwindowsversion() reflect the compatibility mode Python is
376 # running under, and so the service pack value is only going to be
377 # valid if the versions match.
378 if winver[:2] == (maj, min):
Christian Heimes02781dc2008-03-21 01:11:52 +0000379 try:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700380 csd = 'SP{}'.format(winver.service_pack_major)
Christian Heimes02781dc2008-03-21 01:11:52 +0000381 except AttributeError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700382 if csd[:13] == 'Service Pack ':
383 csd = 'SP' + csd[13:]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000384
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700385 # VER_NT_SERVER = 3
Steve Dower41519b22016-09-09 09:46:56 -0700386 if getattr(winver, 'product_type', None) == 3:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700387 release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
388 _WIN32_SERVER_RELEASES.get((maj, None)) or
389 release)
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000390
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000391 try:
Steve Dowerd307d052019-04-22 11:40:12 -0700392 try:
393 import winreg
394 except ImportError:
395 import _winreg as winreg
396 except ImportError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700397 pass
Steve Dowerd307d052019-04-22 11:40:12 -0700398 else:
399 try:
400 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
Dennis Sweeney1e7e4512020-05-04 22:33:17 -0400401 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
402 ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
403 except OSError:
Steve Dowerd307d052019-04-22 11:40:12 -0700404 pass
Tim Peters0eadaac2003-04-24 16:02:54 +0000405
Victor Stinnerced39362013-12-09 00:14:52 +0100406 return release, version, csd, ptype
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000407
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700408
Ronald Oussorene186e382010-07-23 11:54:59 +0000409def _mac_ver_xml():
410 fn = '/System/Library/CoreServices/SystemVersion.plist'
411 if not os.path.exists(fn):
412 return None
413
414 try:
415 import plistlib
Brett Cannoncd171c82013-07-04 17:43:24 -0400416 except ImportError:
Ronald Oussorene186e382010-07-23 11:54:59 +0000417 return None
418
Ned Deily936dfae2014-01-13 11:34:19 -0800419 with open(fn, 'rb') as f:
420 pl = plistlib.load(f)
Ronald Oussorene186e382010-07-23 11:54:59 +0000421 release = pl['ProductVersion']
Victor Stinnerced39362013-12-09 00:14:52 +0100422 versioninfo = ('', '', '')
Larry Hastings605a62d2012-06-24 04:33:36 -0700423 machine = os.uname().machine
Ronald Oussorenfcd77012010-08-03 07:42:42 +0000424 if machine in ('ppc', 'Power Macintosh'):
Ezio Melotti9a3777e2013-08-17 15:53:55 +0300425 # Canonical name
Ronald Oussorene186e382010-07-23 11:54:59 +0000426 machine = 'PowerPC'
427
Victor Stinnerced39362013-12-09 00:14:52 +0100428 return release, versioninfo, machine
Ronald Oussorene186e382010-07-23 11:54:59 +0000429
430
Victor Stinnerced39362013-12-09 00:14:52 +0100431def mac_ver(release='', versioninfo=('', '', ''), machine=''):
Ronald Oussorene186e382010-07-23 11:54:59 +0000432
Victor Stinnerb0e08772018-12-12 17:48:08 +0100433 """ Get macOS version information and return it as tuple (release,
Ronald Oussorene186e382010-07-23 11:54:59 +0000434 versioninfo, machine) with versioninfo being a tuple (version,
435 dev_stage, non_release_version).
436
Ezio Melotti30b9d5d2013-08-17 15:50:46 +0300437 Entries which cannot be determined are set to the parameter values
Ronald Oussorene186e382010-07-23 11:54:59 +0000438 which default to ''. All tuple entries are strings.
439 """
440
441 # First try reading the information from an XML file which should
442 # always be present
443 info = _mac_ver_xml()
444 if info is not None:
445 return info
446
Ronald Oussorene186e382010-07-23 11:54:59 +0000447 # If that also doesn't work return the default values
Victor Stinnerced39362013-12-09 00:14:52 +0100448 return release, versioninfo, machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000449
Victor Stinnerced39362013-12-09 00:14:52 +0100450def _java_getprop(name, default):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000451
452 from java.lang import System
453 try:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000454 value = System.getProperty(name)
455 if value is None:
456 return default
457 return value
458 except AttributeError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000459 return default
460
Victor Stinnerced39362013-12-09 00:14:52 +0100461def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
Tim Peters0eadaac2003-04-24 16:02:54 +0000462
Brett Cannon8ab27df2003-08-05 03:52:04 +0000463 """ Version interface for Jython.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000464
Victor Stinnerced39362013-12-09 00:14:52 +0100465 Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
466 a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
467 tuple (os_name, os_version, os_arch).
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000468
469 Values which cannot be determined are set to the defaults
470 given as parameters (which all default to '').
471
472 """
473 # Import the needed APIs
474 try:
475 import java.lang
Brett Cannoncd171c82013-07-04 17:43:24 -0400476 except ImportError:
Victor Stinnerced39362013-12-09 00:14:52 +0100477 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000478
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000479 vendor = _java_getprop('java.vendor', vendor)
480 release = _java_getprop('java.version', release)
481 vm_name, vm_release, vm_vendor = vminfo
482 vm_name = _java_getprop('java.vm.name', vm_name)
483 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
484 vm_release = _java_getprop('java.vm.version', vm_release)
485 vminfo = vm_name, vm_release, vm_vendor
486 os_name, os_version, os_arch = osinfo
487 os_arch = _java_getprop('java.os.arch', os_arch)
488 os_name = _java_getprop('java.os.name', os_name)
489 os_version = _java_getprop('java.os.version', os_version)
490 osinfo = os_name, os_version, os_arch
Tim Peters0eadaac2003-04-24 16:02:54 +0000491
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000492 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000493
494### System name aliasing
495
Victor Stinnerced39362013-12-09 00:14:52 +0100496def system_alias(system, release, version):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000497
Victor Stinnerced39362013-12-09 00:14:52 +0100498 """ Returns (system, release, version) aliased to common
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000499 marketing names used for some systems.
500
501 It also does some reordering of the information in some cases
502 where it would otherwise cause confusion.
503
504 """
Victor Stinnerb0e08772018-12-12 17:48:08 +0100505 if system == 'SunOS':
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000506 # Sun's OS
507 if release < '5':
508 # These releases use the old name SunOS
Victor Stinnerced39362013-12-09 00:14:52 +0100509 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000510 # Modify release (marketing release = SunOS release - 3)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000511 l = release.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000512 if l:
513 try:
514 major = int(l[0])
515 except ValueError:
516 pass
517 else:
518 major = major - 3
519 l[0] = str(major)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000520 release = '.'.join(l)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000521 if release < '6':
522 system = 'Solaris'
523 else:
524 # XXX Whatever the new SunOS marketing name is...
525 system = 'Solaris'
526
Victor Stinnerced39362013-12-09 00:14:52 +0100527 elif system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000528 # In case one of the other tricks
529 system = 'Windows'
530
Victor Stinner60875db2018-12-18 19:51:35 +0100531 # bpo-35516: Don't replace Darwin with macOS since input release and
532 # version arguments can be different than the currently running version.
533
Victor Stinnerced39362013-12-09 00:14:52 +0100534 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000535
536### Various internal helpers
537
538def _platform(*args):
539
540 """ Helper to format the platform string in a filename
541 compatible format e.g. "system-version-machine".
542 """
543 # Format the platform string
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000544 platform = '-'.join(x.strip() for x in filter(len, args))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000545
546 # Cleanup some possible filename obstacles...
Victor Stinnerced39362013-12-09 00:14:52 +0100547 platform = platform.replace(' ', '_')
548 platform = platform.replace('/', '-')
549 platform = platform.replace('\\', '-')
550 platform = platform.replace(':', '-')
551 platform = platform.replace(';', '-')
552 platform = platform.replace('"', '-')
553 platform = platform.replace('(', '-')
554 platform = platform.replace(')', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000555
556 # No need to report 'unknown' information...
Victor Stinnerced39362013-12-09 00:14:52 +0100557 platform = platform.replace('unknown', '')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000558
559 # Fold '--'s and remove trailing '-'
560 while 1:
Victor Stinnerced39362013-12-09 00:14:52 +0100561 cleaned = platform.replace('--', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000562 if cleaned == platform:
563 break
564 platform = cleaned
565 while platform[-1] == '-':
566 platform = platform[:-1]
567
568 return platform
569
570def _node(default=''):
571
572 """ Helper to determine the node name of this machine.
573 """
574 try:
575 import socket
Brett Cannoncd171c82013-07-04 17:43:24 -0400576 except ImportError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000577 # No sockets...
578 return default
579 try:
580 return socket.gethostname()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200581 except OSError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000582 # Still not working...
583 return default
584
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000585def _follow_symlinks(filepath):
586
587 """ In case filepath is a symlink, follow it until a
588 real file is reached.
589 """
Georg Brandl673f7ef2008-01-05 21:20:19 +0000590 filepath = os.path.abspath(filepath)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000591 while os.path.islink(filepath):
592 filepath = os.path.normpath(
Victor Stinnerced39362013-12-09 00:14:52 +0100593 os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000594 return filepath
595
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000596
Victor Stinnerced39362013-12-09 00:14:52 +0100597def _syscmd_file(target, default=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000598
599 """ Interface to the system's file command.
600
601 The function uses the -b option of the file command to have it
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000602 omit the filename in its output. Follow the symlinks. It returns
603 default in case the command should fail.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000604
605 """
Victor Stinnerced39362013-12-09 00:14:52 +0100606 if sys.platform in ('dos', 'win32', 'win16'):
Hirokazu Yamamotod26782e2008-09-01 14:35:47 +0000607 # XXX Others too ?
608 return default
Victor Stinnerb8e689a2018-12-04 17:18:12 +0100609
610 import subprocess
Jesus Ceafc990e92012-10-04 13:51:43 +0200611 target = _follow_symlinks(target)
Victor Stinner0af9c332018-12-17 18:47:24 +0100612 # "file" output is locale dependent: force the usage of the C locale
613 # to get deterministic behavior.
614 env = dict(os.environ, LC_ALL='C')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000615 try:
Victor Stinner0af9c332018-12-17 18:47:24 +0100616 # -b: do not prepend filenames to output lines (brief mode)
617 output = subprocess.check_output(['file', '-b', target],
Victor Stinner3a521f02018-12-07 11:10:33 +0100618 stderr=subprocess.DEVNULL,
Victor Stinner0af9c332018-12-17 18:47:24 +0100619 env=env)
Victor Stinner3a521f02018-12-07 11:10:33 +0100620 except (OSError, subprocess.CalledProcessError):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000621 return default
Victor Stinner0af9c332018-12-17 18:47:24 +0100622 if not output:
623 return default
624 # With the C locale, the output should be mostly ASCII-compatible.
625 # Decode from Latin-1 to prevent Unicode decode error.
626 return output.decode('latin-1')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000627
628### Information about the used architecture
629
630# Default values for architecture; non-empty strings override the
631# defaults given as parameters
632_default_architecture = {
Victor Stinnerced39362013-12-09 00:14:52 +0100633 'win32': ('', 'WindowsPE'),
634 'win16': ('', 'Windows'),
635 'dos': ('', 'MSDOS'),
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000636}
637
Victor Stinnerced39362013-12-09 00:14:52 +0100638def architecture(executable=sys.executable, bits='', linkage=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000639
640 """ Queries the given executable (defaults to the Python interpreter
Brett Cannon8ab27df2003-08-05 03:52:04 +0000641 binary) for various architecture information.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000642
Victor Stinnerced39362013-12-09 00:14:52 +0100643 Returns a tuple (bits, linkage) which contains information about
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000644 the bit architecture and the linkage format used for the
645 executable. Both values are returned as strings.
646
647 Values that cannot be determined are returned as given by the
648 parameter presets. If bits is given as '', the sizeof(pointer)
649 (or sizeof(long) on Python version < 1.5.2) is used as
650 indicator for the supported pointer size.
651
652 The function relies on the system's "file" command to do the
653 actual work. This is available on most if not all Unix
Brett Cannon8ab27df2003-08-05 03:52:04 +0000654 platforms. On some non-Unix platforms where the "file" command
655 does not exist and the executable is set to the Python interpreter
656 binary defaults from _default_architecture are used.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000657
658 """
659 # Use the sizeof(pointer) as default number of bits if nothing
660 # else is given as default.
661 if not bits:
662 import struct
Victor Stinner4aa917c2018-12-14 13:14:10 +0100663 size = struct.calcsize('P')
664 bits = str(size * 8) + 'bit'
Tim Peters0eadaac2003-04-24 16:02:54 +0000665
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000666 # Get data from the 'file' system command
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000667 if executable:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000668 fileout = _syscmd_file(executable, '')
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000669 else:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000670 fileout = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000671
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000672 if not fileout and \
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000673 executable == sys.executable:
674 # "file" command did not return anything; we'll try to provide
Tim Peters0eadaac2003-04-24 16:02:54 +0000675 # some sensible defaults then...
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000676 if sys.platform in _default_architecture:
Victor Stinnerced39362013-12-09 00:14:52 +0100677 b, l = _default_architecture[sys.platform]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000678 if b:
679 bits = b
680 if l:
681 linkage = l
Victor Stinnerced39362013-12-09 00:14:52 +0100682 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000683
Victor Stinner0af9c332018-12-17 18:47:24 +0100684 if 'executable' not in fileout and 'shared object' not in fileout:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000685 # Format not supported
Victor Stinnerced39362013-12-09 00:14:52 +0100686 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000687
688 # Bits
689 if '32-bit' in fileout:
690 bits = '32bit'
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000691 elif '64-bit' in fileout:
692 bits = '64bit'
693
694 # Linkage
695 if 'ELF' in fileout:
696 linkage = 'ELF'
697 elif 'PE' in fileout:
698 # E.g. Windows uses this format
699 if 'Windows' in fileout:
700 linkage = 'WindowsPE'
701 else:
702 linkage = 'PE'
703 elif 'COFF' in fileout:
704 linkage = 'COFF'
705 elif 'MS-DOS' in fileout:
706 linkage = 'MSDOS'
707 else:
708 # XXX the A.OUT format also falls under this class...
709 pass
710
Victor Stinnerced39362013-12-09 00:14:52 +0100711 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000712
Jason R. Coombs518835f2020-04-16 08:28:09 -0400713
714def _get_machine_win32():
715 # Try to use the PROCESSOR_* environment variables
716 # available on Win XP and later; see
717 # http://support.microsoft.com/kb/888731 and
718 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
719
720 # WOW64 processes mask the native architecture
721 return (
722 os.environ.get('PROCESSOR_ARCHITEW6432', '') or
723 os.environ.get('PROCESSOR_ARCHITECTURE', '')
724 )
725
726
727class _Processor:
728 @classmethod
729 def get(cls):
730 func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess)
731 return func() or ''
732
733 def get_win32():
734 return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
735
736 def get_OpenVMS():
737 try:
738 import vms_lib
739 except ImportError:
740 pass
741 else:
742 csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
743 return 'Alpha' if cpu_number >= 128 else 'VAX'
744
745 def from_subprocess():
746 """
747 Fall back to `uname -p`
748 """
749 try:
750 return subprocess.check_output(
751 ['uname', '-p'],
752 stderr=subprocess.DEVNULL,
753 text=True,
754 ).strip()
755 except (OSError, subprocess.CalledProcessError):
756 pass
757
758
759def _unknown_as_blank(val):
760 return '' if val == 'unknown' else val
761
762
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000763### Portable uname() interface
Tim Peters0eadaac2003-04-24 16:02:54 +0000764
Jason R. Coombs518835f2020-04-16 08:28:09 -0400765class uname_result(
766 collections.namedtuple(
767 "uname_result_base",
768 "system node release version machine")
769 ):
770 """
771 A uname_result that's largely compatible with a
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500772 simple namedtuple except that 'processor' is
Jason R. Coombs518835f2020-04-16 08:28:09 -0400773 resolved late and cached to avoid calling "uname"
774 except when needed.
775 """
776
777 @functools.cached_property
778 def processor(self):
779 return _unknown_as_blank(_Processor.get())
780
781 def __iter__(self):
782 return itertools.chain(
783 super().__iter__(),
784 (self.processor,)
785 )
786
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500787 @classmethod
788 def _make(cls, iterable):
789 # override factory to affect length check
790 num_fields = len(cls._fields)
791 result = cls.__new__(cls, *iterable)
792 if len(result) != num_fields + 1:
793 msg = f'Expected {num_fields} arguments, got {len(result)}'
794 raise TypeError(msg)
795 return result
796
Jason R. Coombs518835f2020-04-16 08:28:09 -0400797 def __getitem__(self, key):
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500798 return tuple(self)[key]
Jason R. Coombs2c3d5082020-05-09 10:12:41 -0400799
800 def __len__(self):
801 return len(tuple(iter(self)))
Jason R. Coombs518835f2020-04-16 08:28:09 -0400802
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500803 def __reduce__(self):
804 return uname_result, tuple(self)[:len(self._fields)]
805
Larry Hastings68386bc2012-06-24 14:30:41 -0700806
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000807_uname_cache = None
808
Jason R. Coombs518835f2020-04-16 08:28:09 -0400809
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000810def uname():
811
812 """ Fairly portable uname interface. Returns a tuple
Victor Stinnerced39362013-12-09 00:14:52 +0100813 of strings (system, node, release, version, machine, processor)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000814 identifying the underlying platform.
815
816 Note that unlike the os.uname function this also returns
Brett Cannon8ab27df2003-08-05 03:52:04 +0000817 possible processor information as an additional tuple entry.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000818
819 Entries which cannot be determined are set to ''.
820
821 """
822 global _uname_cache
823
824 if _uname_cache is not None:
825 return _uname_cache
826
827 # Get some infos from the builtin os.uname API...
828 try:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400829 system, node, release, version, machine = infos = os.uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000830 except AttributeError:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400831 system = sys.platform
832 node = _node()
833 release = version = machine = ''
834 infos = ()
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000835
Jason R. Coombs518835f2020-04-16 08:28:09 -0400836 if not any(infos):
837 # uname is not available
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000838
839 # Try win32_ver() on win32 platforms
840 if system == 'win32':
Victor Stinnerced39362013-12-09 00:14:52 +0100841 release, version, csd, ptype = win32_ver()
Jason R. Coombs518835f2020-04-16 08:28:09 -0400842 machine = machine or _get_machine_win32()
Tim Peters0eadaac2003-04-24 16:02:54 +0000843
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000844 # Try the 'ver' system command available on some
845 # platforms
Jason R. Coombs518835f2020-04-16 08:28:09 -0400846 if not (release and version):
Victor Stinnerced39362013-12-09 00:14:52 +0100847 system, release, version = _syscmd_ver(system)
Marc-André Lemburgcdc79232004-06-19 17:17:00 +0000848 # Normalize system to what win32_ver() normally returns
849 # (_syscmd_ver() tends to return the vendor name as well)
850 if system == 'Microsoft Windows':
851 system = 'Windows'
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000852 elif system == 'Microsoft' and release == 'Windows':
853 # Under Windows Vista and Windows Server 2008,
854 # Microsoft changed the output of the ver command. The
855 # release is no longer printed. This causes the
856 # system and release to be misidentified.
857 system = 'Windows'
858 if '6.0' == version[:3]:
859 release = 'Vista'
860 else:
861 release = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000862
863 # In case we still don't know anything useful, we'll try to
864 # help ourselves
Victor Stinnerced39362013-12-09 00:14:52 +0100865 if system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000866 if not version:
867 if system == 'win32':
868 version = '32bit'
869 else:
870 version = '16bit'
871 system = 'Windows'
872
873 elif system[:4] == 'java':
Victor Stinnerced39362013-12-09 00:14:52 +0100874 release, vendor, vminfo, osinfo = java_ver()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000875 system = 'Java'
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000876 version = ', '.join(vminfo)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000877 if not version:
878 version = vendor
879
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000880 # System specific extensions
881 if system == 'OpenVMS':
882 # OpenVMS seems to have release and version mixed up
883 if not release or release == '0':
884 release = version
885 version = ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000886
887 # normalize name
888 if system == 'Microsoft' and release == 'Windows':
889 system = 'Windows'
890 release = 'Vista'
891
Jason R. Coombs518835f2020-04-16 08:28:09 -0400892 vals = system, node, release, version, machine
893 # Replace 'unknown' values with the more portable ''
894 _uname_cache = uname_result(*map(_unknown_as_blank, vals))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000895 return _uname_cache
896
897### Direct interfaces to some of the uname() return values
898
899def system():
900
901 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
902
903 An empty string is returned if the value cannot be determined.
904
905 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700906 return uname().system
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000907
908def node():
909
Brett Cannon8ab27df2003-08-05 03:52:04 +0000910 """ Returns the computer's network name (which may not be fully
911 qualified)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000912
913 An empty string is returned if the value cannot be determined.
914
915 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700916 return uname().node
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000917
918def release():
919
920 """ Returns the system's release, e.g. '2.2.0' or 'NT'
921
922 An empty string is returned if the value cannot be determined.
923
924 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700925 return uname().release
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000926
927def version():
928
929 """ Returns the system's release version, e.g. '#3 on degas'
930
931 An empty string is returned if the value cannot be determined.
932
933 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700934 return uname().version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000935
936def machine():
937
938 """ Returns the machine type, e.g. 'i386'
939
940 An empty string is returned if the value cannot be determined.
941
942 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700943 return uname().machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000944
945def processor():
946
947 """ Returns the (true) processor name, e.g. 'amdk6'
948
949 An empty string is returned if the value cannot be
950 determined. Note that many platforms do not provide this
951 information or simply return the same value as for machine(),
952 e.g. NetBSD does this.
953
954 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700955 return uname().processor
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000956
957### Various APIs for extracting information from sys.version
958
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000959_sys_version_parser = re.compile(
Martin Panter4e505532016-06-08 06:12:22 +0000960 r'([\w.+]+)\s*' # "version<space>"
961 r'\(#?([^,]+)' # "(#buildno"
962 r'(?:,\s*([\w ]*)' # ", builddate"
963 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
964 r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000965
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000966_ironpython_sys_version_parser = re.compile(
967 r'IronPython\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400968 r'([\d\.]+)'
969 r'(?: \(([\d\.]+)\))?'
970 r' on (.NET [\d\.]+)', re.ASCII)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000971
Ezio Melottif076f532013-10-21 03:03:32 +0300972# IronPython covering 2.6 and 2.7
973_ironpython26_sys_version_parser = re.compile(
974 r'([\d.]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400975 r'\(IronPython\s*'
976 r'[\d.]+\s*'
977 r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
Ezio Melottif076f532013-10-21 03:03:32 +0300978)
979
Benjamin Petersone549ead2009-03-28 21:42:05 +0000980_pypy_sys_version_parser = re.compile(
981 r'([\w.+]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400982 r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
983 r'\[PyPy [^\]]+\]?')
Benjamin Petersone549ead2009-03-28 21:42:05 +0000984
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000985_sys_version_cache = {}
986
987def _sys_version(sys_version=None):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000988
989 """ Returns a parsed version of Python's sys.version as tuple
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +0000990 (name, version, branch, revision, buildno, builddate, compiler)
991 referring to the Python implementation name, version, branch,
992 revision, build number, build date/time as string and the compiler
993 identification string.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000994
995 Note that unlike the Python sys.version, the returned value
996 for the Python version will always include the patchlevel (it
997 defaults to '.0').
998
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000999 The function returns empty strings for tuple entries that
1000 cannot be determined.
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001001
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001002 sys_version may be given to parse an alternative version
1003 string, e.g. if the version was read from a different Python
1004 interpreter.
1005
1006 """
1007 # Get the Python version
1008 if sys_version is None:
1009 sys_version = sys.version
1010
1011 # Try the cache first
1012 result = _sys_version_cache.get(sys_version, None)
1013 if result is not None:
1014 return result
1015
1016 # Parse it
Ezio Melottif076f532013-10-21 03:03:32 +03001017 if 'IronPython' in sys_version:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001018 # IronPython
1019 name = 'IronPython'
Ezio Melottif076f532013-10-21 03:03:32 +03001020 if sys_version.startswith('IronPython'):
1021 match = _ironpython_sys_version_parser.match(sys_version)
1022 else:
1023 match = _ironpython26_sys_version_parser.match(sys_version)
1024
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001025 if match is None:
1026 raise ValueError(
1027 'failed to parse IronPython sys.version: %s' %
1028 repr(sys_version))
Ezio Melottif076f532013-10-21 03:03:32 +03001029
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001030 version, alt_version, compiler = match.groups()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001031 buildno = ''
1032 builddate = ''
1033
Ezio Melottif076f532013-10-21 03:03:32 +03001034 elif sys.platform.startswith('java'):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001035 # Jython
1036 name = 'Jython'
Benjamin Petersone549ead2009-03-28 21:42:05 +00001037 match = _sys_version_parser.match(sys_version)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001038 if match is None:
1039 raise ValueError(
1040 'failed to parse Jython sys.version: %s' %
1041 repr(sys_version))
Benjamin Petersone549ead2009-03-28 21:42:05 +00001042 version, buildno, builddate, buildtime, _ = match.groups()
Martin Panter4e505532016-06-08 06:12:22 +00001043 if builddate is None:
1044 builddate = ''
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001045 compiler = sys.platform
Benjamin Petersone549ead2009-03-28 21:42:05 +00001046
1047 elif "PyPy" in sys_version:
1048 # PyPy
1049 name = "PyPy"
1050 match = _pypy_sys_version_parser.match(sys_version)
1051 if match is None:
1052 raise ValueError("failed to parse PyPy sys.version: %s" %
1053 repr(sys_version))
1054 version, buildno, builddate, buildtime = match.groups()
1055 compiler = ""
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001056
1057 else:
1058 # CPython
1059 match = _sys_version_parser.match(sys_version)
1060 if match is None:
1061 raise ValueError(
1062 'failed to parse CPython sys.version: %s' %
1063 repr(sys_version))
1064 version, buildno, builddate, buildtime, compiler = \
1065 match.groups()
Benjamin Petersone549ead2009-03-28 21:42:05 +00001066 name = 'CPython'
Martin Panter4e505532016-06-08 06:12:22 +00001067 if builddate is None:
1068 builddate = ''
1069 elif buildtime:
1070 builddate = builddate + ' ' + buildtime
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001071
Ned Deily5c4b0d02017-03-04 00:19:55 -05001072 if hasattr(sys, '_git'):
1073 _, branch, revision = sys._git
1074 elif hasattr(sys, '_mercurial'):
Georg Brandl82562422011-03-05 21:09:22 +01001075 _, branch, revision = sys._mercurial
Benjamin Petersone549ead2009-03-28 21:42:05 +00001076 else:
1077 branch = ''
1078 revision = ''
1079
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001080 # Add the patchlevel version if missing
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001081 l = version.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001082 if len(l) == 2:
1083 l.append('0')
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001084 version = '.'.join(l)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001085
1086 # Build and cache the result
1087 result = (name, version, branch, revision, buildno, builddate, compiler)
1088 _sys_version_cache[sys_version] = result
1089 return result
1090
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001091def python_implementation():
1092
1093 """ Returns a string identifying the Python implementation.
1094
1095 Currently, the following implementations are identified:
Ezio Melottif16898b2011-05-04 18:37:50 +03001096 'CPython' (C implementation of Python),
1097 'IronPython' (.NET implementation of Python),
1098 'Jython' (Java implementation of Python),
1099 'PyPy' (Python implementation of Python).
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001100
1101 """
1102 return _sys_version()[0]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001103
1104def python_version():
1105
1106 """ Returns the Python version as string 'major.minor.patchlevel'
1107
1108 Note that unlike the Python sys.version, the returned value
1109 will always include the patchlevel (it defaults to 0).
1110
1111 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001112 return _sys_version()[1]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001113
1114def python_version_tuple():
1115
1116 """ Returns the Python version as tuple (major, minor, patchlevel)
1117 of strings.
1118
1119 Note that unlike the Python sys.version, the returned value
1120 will always include the patchlevel (it defaults to 0).
1121
1122 """
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001123 return tuple(_sys_version()[1].split('.'))
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001124
1125def python_branch():
1126
1127 """ Returns a string identifying the Python implementation
1128 branch.
1129
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001130 For CPython this is the SCM branch from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001131 Python binary was built.
1132
1133 If not available, an empty string is returned.
1134
1135 """
Thomas Wouters9fe394c2007-02-05 01:24:16 +00001136
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001137 return _sys_version()[2]
1138
1139def python_revision():
1140
1141 """ Returns a string identifying the Python implementation
1142 revision.
1143
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001144 For CPython this is the SCM revision from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001145 Python binary was built.
1146
1147 If not available, an empty string is returned.
1148
1149 """
1150 return _sys_version()[3]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001151
1152def python_build():
1153
1154 """ Returns a tuple (buildno, builddate) stating the Python
1155 build number and date as strings.
1156
1157 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001158 return _sys_version()[4:6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001159
1160def python_compiler():
1161
1162 """ Returns a string identifying the compiler used for compiling
1163 Python.
1164
1165 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001166 return _sys_version()[6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001167
1168### The Opus Magnum of platform strings :-)
1169
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001170_platform_cache = {}
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001171
1172def platform(aliased=0, terse=0):
1173
1174 """ Returns a single string identifying the underlying platform
1175 with as much useful information as possible (but no more :).
Tim Peters0eadaac2003-04-24 16:02:54 +00001176
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001177 The output is intended to be human readable rather than
1178 machine parseable. It may look different on different
1179 platforms and this is intended.
1180
1181 If "aliased" is true, the function will use aliases for
1182 various platforms that report system names which differ from
1183 their common names, e.g. SunOS will be reported as
1184 Solaris. The system_alias() function is used to implement
1185 this.
1186
1187 Setting terse to true causes the function to return only the
1188 absolute minimum information needed to identify the platform.
1189
1190 """
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001191 result = _platform_cache.get((aliased, terse), None)
1192 if result is not None:
1193 return result
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001194
1195 # Get uname information and then apply platform specific cosmetics
1196 # to it...
Victor Stinnerced39362013-12-09 00:14:52 +01001197 system, node, release, version, machine, processor = uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001198 if machine == processor:
1199 processor = ''
1200 if aliased:
Victor Stinnerced39362013-12-09 00:14:52 +01001201 system, release, version = system_alias(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001202
Victor Stinnerea0ca212018-12-05 22:41:52 +01001203 if system == 'Darwin':
1204 # macOS (darwin kernel)
1205 macos_release = mac_ver()[0]
1206 if macos_release:
Victor Stinnerea0ca212018-12-05 22:41:52 +01001207 system = 'macOS'
1208 release = macos_release
1209
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001210 if system == 'Windows':
1211 # MS platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001212 rel, vers, csd, ptype = win32_ver(version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001213 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001214 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001215 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001216 platform = _platform(system, release, version, csd)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001217
1218 elif system in ('Linux',):
Petr Viktorin8b94b412018-05-16 11:51:18 -04001219 # check for libc vs. glibc
Victor Stinnera719c8f2019-06-27 09:04:28 +02001220 libcname, libcversion = libc_ver()
Petr Viktorin8b94b412018-05-16 11:51:18 -04001221 platform = _platform(system, release, machine, processor,
1222 'with',
1223 libcname+libcversion)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001224 elif system == 'Java':
1225 # Java platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001226 r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001227 if terse or not os_name:
Victor Stinnerced39362013-12-09 00:14:52 +01001228 platform = _platform(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001229 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001230 platform = _platform(system, release, version,
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001231 'on',
Victor Stinnerced39362013-12-09 00:14:52 +01001232 os_name, os_version, os_arch)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001233
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001234 else:
1235 # Generic handler
1236 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001237 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001238 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001239 bits, linkage = architecture(sys.executable)
1240 platform = _platform(system, release, machine,
1241 processor, bits, linkage)
Tim Peters0eadaac2003-04-24 16:02:54 +00001242
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001243 _platform_cache[(aliased, terse)] = platform
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001244 return platform
1245
Christian Heimes5c73afc2020-11-30 22:34:45 +01001246### freedesktop.org os-release standard
1247# https://www.freedesktop.org/software/systemd/man/os-release.html
1248
1249# NAME=value with optional quotes (' or "). The regular expression is less
1250# strict than shell lexer, but that's ok.
1251_os_release_line = re.compile(
1252 "^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
1253)
1254# unescape five special characters mentioned in the standard
1255_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
1256# /etc takes precedence over /usr/lib
Victor Stinnerbfda4f52020-12-23 17:35:53 +01001257_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
Christian Heimes5c73afc2020-11-30 22:34:45 +01001258_os_release_cache = None
1259
1260
1261def _parse_os_release(lines):
1262 # These fields are mandatory fields with well-known defaults
1263 # in pratice all Linux distributions override NAME, ID, and PRETTY_NAME.
1264 info = {
1265 "NAME": "Linux",
1266 "ID": "linux",
1267 "PRETTY_NAME": "Linux",
1268 }
1269
1270 for line in lines:
1271 mo = _os_release_line.match(line)
1272 if mo is not None:
1273 info[mo.group('name')] = _os_release_unescape.sub(
1274 r"\1", mo.group('value')
1275 )
1276
1277 return info
1278
1279
1280def freedesktop_os_release():
1281 """Return operation system identification from freedesktop.org os-release
1282 """
1283 global _os_release_cache
1284
1285 if _os_release_cache is None:
1286 errno = None
1287 for candidate in _os_release_candidates:
1288 try:
1289 with open(candidate, encoding="utf-8") as f:
1290 _os_release_cache = _parse_os_release(f)
1291 break
1292 except OSError as e:
1293 errno = e.errno
1294 else:
1295 raise OSError(
1296 errno,
1297 f"Unable to read files {', '.join(_os_release_candidates)}"
1298 )
1299
1300 return _os_release_cache.copy()
1301
1302
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001303### Command line interface
1304
1305if __name__ == '__main__':
1306 # Default is to print the aliased verbose platform string
Tim Peters0eadaac2003-04-24 16:02:54 +00001307 terse = ('terse' in sys.argv or '--terse' in sys.argv)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001308 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
Victor Stinnerced39362013-12-09 00:14:52 +01001309 print(platform(aliased, terse))
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001310 sys.exit(0)