blob: d298a42edc8483028198d8ce4a89b7dcc20f1795 [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:
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530242 strings = list(map(str, map(int, l)))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000243 except ValueError:
244 strings = l
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000245 version = '.'.join(strings[:3])
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000246 return version
247
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000248_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
R David Murray44b548d2016-09-08 13:59:53 -0400249 r'.*'
250 r'\[.* ([\d.]+)\])')
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000251
252# Examples of VER command output:
253#
254# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195]
255# Windows XP: Microsoft Windows XP [Version 5.1.2600]
256# Windows Vista: Microsoft Windows [Version 6.0.6002]
257#
258# Note that the "Version" string gets localized on different
259# Windows versions.
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000260
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000261def _syscmd_ver(system='', release='', version='',
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000262
Victor Stinnerced39362013-12-09 00:14:52 +0100263 supported_platforms=('win32', 'win16', 'dos')):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000264
265 """ Tries to figure out the OS version used and returns
Victor Stinnerced39362013-12-09 00:14:52 +0100266 a tuple (system, release, version).
Tim Peters0eadaac2003-04-24 16:02:54 +0000267
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000268 It uses the "ver" shell command for this which is known
Jesus Ceaf1af7052012-10-05 02:48:46 +0200269 to exists on Windows, DOS. XXX Others too ?
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000270
271 In case this fails, the given parameters are used as
272 defaults.
273
274 """
275 if sys.platform not in supported_platforms:
Victor Stinnerced39362013-12-09 00:14:52 +0100276 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000277
278 # Try some common cmd strings
Victor Stinner3a521f02018-12-07 11:10:33 +0100279 import subprocess
Victor Stinnerced39362013-12-09 00:14:52 +0100280 for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000281 try:
Victor Stinner3a521f02018-12-07 11:10:33 +0100282 info = subprocess.check_output(cmd,
283 stderr=subprocess.DEVNULL,
284 text=True,
285 shell=True)
286 except (OSError, subprocess.CalledProcessError) as why:
287 #print('Command %s failed: %s' % (cmd, why))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000288 continue
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000289 else:
290 break
291 else:
Victor Stinnerced39362013-12-09 00:14:52 +0100292 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000293
294 # Parse the output
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000295 info = info.strip()
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000296 m = _ver_output.match(info)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000297 if m is not None:
Victor Stinnerced39362013-12-09 00:14:52 +0100298 system, release, version = m.groups()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000299 # Strip trailing dots from version and release
300 if release[-1] == '.':
301 release = release[:-1]
302 if version[-1] == '.':
303 version = version[:-1]
304 # Normalize the version and build strings (eliminating additional
305 # zeros)
306 version = _norm_version(version)
Victor Stinnerced39362013-12-09 00:14:52 +0100307 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000308
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700309_WIN32_CLIENT_RELEASES = {
310 (5, 0): "2000",
311 (5, 1): "XP",
312 # Strictly, 5.2 client is XP 64-bit, but platform.py historically
313 # has always called it 2003 Server
314 (5, 2): "2003Server",
315 (5, None): "post2003",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000316
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700317 (6, 0): "Vista",
318 (6, 1): "7",
319 (6, 2): "8",
320 (6, 3): "8.1",
321 (6, None): "post8.1",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000322
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700323 (10, 0): "10",
324 (10, None): "post10",
325}
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000326
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700327# Server release name lookup will default to client names if necessary
328_WIN32_SERVER_RELEASES = {
329 (5, 2): "2003Server",
330
331 (6, 0): "2008Server",
332 (6, 1): "2008ServerR2",
333 (6, 2): "2012Server",
334 (6, 3): "2012ServerR2",
335 (6, None): "post2012ServerR2",
336}
337
Paul Monson62dfd7d2019-04-25 11:36:45 -0700338def win32_is_iot():
339 return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
340
341def win32_edition():
342 try:
343 try:
344 import winreg
345 except ImportError:
346 import _winreg as winreg
347 except ImportError:
348 pass
349 else:
350 try:
351 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
352 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
353 return winreg.QueryValueEx(key, 'EditionId')[0]
354 except OSError:
355 pass
356
357 return None
358
Victor Stinnerced39362013-12-09 00:14:52 +0100359def win32_ver(release='', version='', csd='', ptype=''):
Steve Dower8f278f12015-09-22 17:35:24 -0700360 try:
361 from sys import getwindowsversion
362 except ImportError:
363 return release, version, csd, ptype
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700364
365 winver = getwindowsversion()
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530366 try:
367 major, minor, build = map(int, _syscmd_ver()[2].split('.'))
368 except ValueError:
369 major, minor, build = winver.platform_version or winver[:3]
370 version = '{0}.{1}.{2}'.format(major, minor, build)
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700371
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530372 release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or
373 _WIN32_CLIENT_RELEASES.get((major, None)) or
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700374 release)
375
376 # getwindowsversion() reflect the compatibility mode Python is
377 # running under, and so the service pack value is only going to be
378 # valid if the versions match.
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530379 if winver[:2] == (major, minor):
Christian Heimes02781dc2008-03-21 01:11:52 +0000380 try:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700381 csd = 'SP{}'.format(winver.service_pack_major)
Christian Heimes02781dc2008-03-21 01:11:52 +0000382 except AttributeError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700383 if csd[:13] == 'Service Pack ':
384 csd = 'SP' + csd[13:]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000385
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700386 # VER_NT_SERVER = 3
Steve Dower41519b22016-09-09 09:46:56 -0700387 if getattr(winver, 'product_type', None) == 3:
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530388 release = (_WIN32_SERVER_RELEASES.get((major, minor)) or
389 _WIN32_SERVER_RELEASES.get((major, None)) or
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700390 release)
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000391
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000392 try:
Steve Dowerd307d052019-04-22 11:40:12 -0700393 try:
394 import winreg
395 except ImportError:
396 import _winreg as winreg
397 except ImportError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700398 pass
Steve Dowerd307d052019-04-22 11:40:12 -0700399 else:
400 try:
401 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
Dennis Sweeney1e7e4512020-05-04 22:33:17 -0400402 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
403 ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
404 except OSError:
Steve Dowerd307d052019-04-22 11:40:12 -0700405 pass
Tim Peters0eadaac2003-04-24 16:02:54 +0000406
Victor Stinnerced39362013-12-09 00:14:52 +0100407 return release, version, csd, ptype
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000408
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700409
Ronald Oussorene186e382010-07-23 11:54:59 +0000410def _mac_ver_xml():
411 fn = '/System/Library/CoreServices/SystemVersion.plist'
412 if not os.path.exists(fn):
413 return None
414
415 try:
416 import plistlib
Brett Cannoncd171c82013-07-04 17:43:24 -0400417 except ImportError:
Ronald Oussorene186e382010-07-23 11:54:59 +0000418 return None
419
Ned Deily936dfae2014-01-13 11:34:19 -0800420 with open(fn, 'rb') as f:
421 pl = plistlib.load(f)
Ronald Oussorene186e382010-07-23 11:54:59 +0000422 release = pl['ProductVersion']
Victor Stinnerced39362013-12-09 00:14:52 +0100423 versioninfo = ('', '', '')
Larry Hastings605a62d2012-06-24 04:33:36 -0700424 machine = os.uname().machine
Ronald Oussorenfcd77012010-08-03 07:42:42 +0000425 if machine in ('ppc', 'Power Macintosh'):
Ezio Melotti9a3777e2013-08-17 15:53:55 +0300426 # Canonical name
Ronald Oussorene186e382010-07-23 11:54:59 +0000427 machine = 'PowerPC'
428
Victor Stinnerced39362013-12-09 00:14:52 +0100429 return release, versioninfo, machine
Ronald Oussorene186e382010-07-23 11:54:59 +0000430
431
Victor Stinnerced39362013-12-09 00:14:52 +0100432def mac_ver(release='', versioninfo=('', '', ''), machine=''):
Ronald Oussorene186e382010-07-23 11:54:59 +0000433
Victor Stinnerb0e08772018-12-12 17:48:08 +0100434 """ Get macOS version information and return it as tuple (release,
Ronald Oussorene186e382010-07-23 11:54:59 +0000435 versioninfo, machine) with versioninfo being a tuple (version,
436 dev_stage, non_release_version).
437
Ezio Melotti30b9d5d2013-08-17 15:50:46 +0300438 Entries which cannot be determined are set to the parameter values
Ronald Oussorene186e382010-07-23 11:54:59 +0000439 which default to ''. All tuple entries are strings.
440 """
441
442 # First try reading the information from an XML file which should
443 # always be present
444 info = _mac_ver_xml()
445 if info is not None:
446 return info
447
Ronald Oussorene186e382010-07-23 11:54:59 +0000448 # If that also doesn't work return the default values
Victor Stinnerced39362013-12-09 00:14:52 +0100449 return release, versioninfo, machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000450
Victor Stinnerced39362013-12-09 00:14:52 +0100451def _java_getprop(name, default):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000452
453 from java.lang import System
454 try:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000455 value = System.getProperty(name)
456 if value is None:
457 return default
458 return value
459 except AttributeError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000460 return default
461
Victor Stinnerced39362013-12-09 00:14:52 +0100462def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
Tim Peters0eadaac2003-04-24 16:02:54 +0000463
Brett Cannon8ab27df2003-08-05 03:52:04 +0000464 """ Version interface for Jython.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000465
Victor Stinnerced39362013-12-09 00:14:52 +0100466 Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
467 a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
468 tuple (os_name, os_version, os_arch).
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000469
470 Values which cannot be determined are set to the defaults
471 given as parameters (which all default to '').
472
473 """
474 # Import the needed APIs
475 try:
476 import java.lang
Brett Cannoncd171c82013-07-04 17:43:24 -0400477 except ImportError:
Victor Stinnerced39362013-12-09 00:14:52 +0100478 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000479
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000480 vendor = _java_getprop('java.vendor', vendor)
481 release = _java_getprop('java.version', release)
482 vm_name, vm_release, vm_vendor = vminfo
483 vm_name = _java_getprop('java.vm.name', vm_name)
484 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
485 vm_release = _java_getprop('java.vm.version', vm_release)
486 vminfo = vm_name, vm_release, vm_vendor
487 os_name, os_version, os_arch = osinfo
488 os_arch = _java_getprop('java.os.arch', os_arch)
489 os_name = _java_getprop('java.os.name', os_name)
490 os_version = _java_getprop('java.os.version', os_version)
491 osinfo = os_name, os_version, os_arch
Tim Peters0eadaac2003-04-24 16:02:54 +0000492
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000493 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000494
495### System name aliasing
496
Victor Stinnerced39362013-12-09 00:14:52 +0100497def system_alias(system, release, version):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000498
Victor Stinnerced39362013-12-09 00:14:52 +0100499 """ Returns (system, release, version) aliased to common
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000500 marketing names used for some systems.
501
502 It also does some reordering of the information in some cases
503 where it would otherwise cause confusion.
504
505 """
Victor Stinnerb0e08772018-12-12 17:48:08 +0100506 if system == 'SunOS':
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000507 # Sun's OS
508 if release < '5':
509 # These releases use the old name SunOS
Victor Stinnerced39362013-12-09 00:14:52 +0100510 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000511 # Modify release (marketing release = SunOS release - 3)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000512 l = release.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000513 if l:
514 try:
515 major = int(l[0])
516 except ValueError:
517 pass
518 else:
519 major = major - 3
520 l[0] = str(major)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000521 release = '.'.join(l)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000522 if release < '6':
523 system = 'Solaris'
524 else:
525 # XXX Whatever the new SunOS marketing name is...
526 system = 'Solaris'
527
Victor Stinnerced39362013-12-09 00:14:52 +0100528 elif system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000529 # In case one of the other tricks
530 system = 'Windows'
531
Victor Stinner60875db2018-12-18 19:51:35 +0100532 # bpo-35516: Don't replace Darwin with macOS since input release and
533 # version arguments can be different than the currently running version.
534
Victor Stinnerced39362013-12-09 00:14:52 +0100535 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000536
537### Various internal helpers
538
539def _platform(*args):
540
541 """ Helper to format the platform string in a filename
542 compatible format e.g. "system-version-machine".
543 """
544 # Format the platform string
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000545 platform = '-'.join(x.strip() for x in filter(len, args))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000546
547 # Cleanup some possible filename obstacles...
Victor Stinnerced39362013-12-09 00:14:52 +0100548 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('(', '-')
555 platform = platform.replace(')', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000556
557 # No need to report 'unknown' information...
Victor Stinnerced39362013-12-09 00:14:52 +0100558 platform = platform.replace('unknown', '')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000559
560 # Fold '--'s and remove trailing '-'
561 while 1:
Victor Stinnerced39362013-12-09 00:14:52 +0100562 cleaned = platform.replace('--', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000563 if cleaned == platform:
564 break
565 platform = cleaned
566 while platform[-1] == '-':
567 platform = platform[:-1]
568
569 return platform
570
571def _node(default=''):
572
573 """ Helper to determine the node name of this machine.
574 """
575 try:
576 import socket
Brett Cannoncd171c82013-07-04 17:43:24 -0400577 except ImportError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000578 # No sockets...
579 return default
580 try:
581 return socket.gethostname()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200582 except OSError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000583 # Still not working...
584 return default
585
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000586def _follow_symlinks(filepath):
587
588 """ In case filepath is a symlink, follow it until a
589 real file is reached.
590 """
Georg Brandl673f7ef2008-01-05 21:20:19 +0000591 filepath = os.path.abspath(filepath)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000592 while os.path.islink(filepath):
593 filepath = os.path.normpath(
Victor Stinnerced39362013-12-09 00:14:52 +0100594 os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000595 return filepath
596
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000597
Victor Stinnerced39362013-12-09 00:14:52 +0100598def _syscmd_file(target, default=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000599
600 """ Interface to the system's file command.
601
602 The function uses the -b option of the file command to have it
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000603 omit the filename in its output. Follow the symlinks. It returns
604 default in case the command should fail.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000605
606 """
Victor Stinnerced39362013-12-09 00:14:52 +0100607 if sys.platform in ('dos', 'win32', 'win16'):
Hirokazu Yamamotod26782e2008-09-01 14:35:47 +0000608 # XXX Others too ?
609 return default
Victor Stinnerb8e689a2018-12-04 17:18:12 +0100610
611 import subprocess
Jesus Ceafc990e92012-10-04 13:51:43 +0200612 target = _follow_symlinks(target)
Victor Stinner0af9c332018-12-17 18:47:24 +0100613 # "file" output is locale dependent: force the usage of the C locale
614 # to get deterministic behavior.
615 env = dict(os.environ, LC_ALL='C')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000616 try:
Victor Stinner0af9c332018-12-17 18:47:24 +0100617 # -b: do not prepend filenames to output lines (brief mode)
618 output = subprocess.check_output(['file', '-b', target],
Victor Stinner3a521f02018-12-07 11:10:33 +0100619 stderr=subprocess.DEVNULL,
Victor Stinner0af9c332018-12-17 18:47:24 +0100620 env=env)
Victor Stinner3a521f02018-12-07 11:10:33 +0100621 except (OSError, subprocess.CalledProcessError):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000622 return default
Victor Stinner0af9c332018-12-17 18:47:24 +0100623 if not output:
624 return default
625 # With the C locale, the output should be mostly ASCII-compatible.
626 # Decode from Latin-1 to prevent Unicode decode error.
627 return output.decode('latin-1')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000628
629### Information about the used architecture
630
631# Default values for architecture; non-empty strings override the
632# defaults given as parameters
633_default_architecture = {
Victor Stinnerced39362013-12-09 00:14:52 +0100634 'win32': ('', 'WindowsPE'),
635 'win16': ('', 'Windows'),
636 'dos': ('', 'MSDOS'),
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000637}
638
Victor Stinnerced39362013-12-09 00:14:52 +0100639def architecture(executable=sys.executable, bits='', linkage=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000640
641 """ Queries the given executable (defaults to the Python interpreter
Brett Cannon8ab27df2003-08-05 03:52:04 +0000642 binary) for various architecture information.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000643
Victor Stinnerced39362013-12-09 00:14:52 +0100644 Returns a tuple (bits, linkage) which contains information about
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000645 the bit architecture and the linkage format used for the
646 executable. Both values are returned as strings.
647
648 Values that cannot be determined are returned as given by the
649 parameter presets. If bits is given as '', the sizeof(pointer)
650 (or sizeof(long) on Python version < 1.5.2) is used as
651 indicator for the supported pointer size.
652
653 The function relies on the system's "file" command to do the
654 actual work. This is available on most if not all Unix
Brett Cannon8ab27df2003-08-05 03:52:04 +0000655 platforms. On some non-Unix platforms where the "file" command
656 does not exist and the executable is set to the Python interpreter
657 binary defaults from _default_architecture are used.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000658
659 """
660 # Use the sizeof(pointer) as default number of bits if nothing
661 # else is given as default.
662 if not bits:
663 import struct
Victor Stinner4aa917c2018-12-14 13:14:10 +0100664 size = struct.calcsize('P')
665 bits = str(size * 8) + 'bit'
Tim Peters0eadaac2003-04-24 16:02:54 +0000666
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000667 # Get data from the 'file' system command
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000668 if executable:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000669 fileout = _syscmd_file(executable, '')
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000670 else:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000671 fileout = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000672
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000673 if not fileout and \
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000674 executable == sys.executable:
675 # "file" command did not return anything; we'll try to provide
Tim Peters0eadaac2003-04-24 16:02:54 +0000676 # some sensible defaults then...
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000677 if sys.platform in _default_architecture:
Victor Stinnerced39362013-12-09 00:14:52 +0100678 b, l = _default_architecture[sys.platform]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000679 if b:
680 bits = b
681 if l:
682 linkage = l
Victor Stinnerced39362013-12-09 00:14:52 +0100683 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000684
Victor Stinner0af9c332018-12-17 18:47:24 +0100685 if 'executable' not in fileout and 'shared object' not in fileout:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000686 # Format not supported
Victor Stinnerced39362013-12-09 00:14:52 +0100687 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000688
689 # Bits
690 if '32-bit' in fileout:
691 bits = '32bit'
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000692 elif '64-bit' in fileout:
693 bits = '64bit'
694
695 # Linkage
696 if 'ELF' in fileout:
697 linkage = 'ELF'
698 elif 'PE' in fileout:
699 # E.g. Windows uses this format
700 if 'Windows' in fileout:
701 linkage = 'WindowsPE'
702 else:
703 linkage = 'PE'
704 elif 'COFF' in fileout:
705 linkage = 'COFF'
706 elif 'MS-DOS' in fileout:
707 linkage = 'MSDOS'
708 else:
709 # XXX the A.OUT format also falls under this class...
710 pass
711
Victor Stinnerced39362013-12-09 00:14:52 +0100712 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000713
Jason R. Coombs518835f2020-04-16 08:28:09 -0400714
715def _get_machine_win32():
716 # Try to use the PROCESSOR_* environment variables
717 # available on Win XP and later; see
718 # http://support.microsoft.com/kb/888731 and
719 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
720
721 # WOW64 processes mask the native architecture
722 return (
723 os.environ.get('PROCESSOR_ARCHITEW6432', '') or
724 os.environ.get('PROCESSOR_ARCHITECTURE', '')
725 )
726
727
728class _Processor:
729 @classmethod
730 def get(cls):
731 func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess)
732 return func() or ''
733
734 def get_win32():
735 return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
736
737 def get_OpenVMS():
738 try:
739 import vms_lib
740 except ImportError:
741 pass
742 else:
743 csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
744 return 'Alpha' if cpu_number >= 128 else 'VAX'
745
746 def from_subprocess():
747 """
748 Fall back to `uname -p`
749 """
750 try:
751 return subprocess.check_output(
752 ['uname', '-p'],
753 stderr=subprocess.DEVNULL,
754 text=True,
755 ).strip()
756 except (OSError, subprocess.CalledProcessError):
757 pass
758
759
760def _unknown_as_blank(val):
761 return '' if val == 'unknown' else val
762
763
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000764### Portable uname() interface
Tim Peters0eadaac2003-04-24 16:02:54 +0000765
Jason R. Coombs518835f2020-04-16 08:28:09 -0400766class uname_result(
767 collections.namedtuple(
768 "uname_result_base",
769 "system node release version machine")
770 ):
771 """
772 A uname_result that's largely compatible with a
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500773 simple namedtuple except that 'processor' is
Jason R. Coombs518835f2020-04-16 08:28:09 -0400774 resolved late and cached to avoid calling "uname"
775 except when needed.
776 """
777
778 @functools.cached_property
779 def processor(self):
780 return _unknown_as_blank(_Processor.get())
781
782 def __iter__(self):
783 return itertools.chain(
784 super().__iter__(),
785 (self.processor,)
786 )
787
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500788 @classmethod
789 def _make(cls, iterable):
790 # override factory to affect length check
791 num_fields = len(cls._fields)
792 result = cls.__new__(cls, *iterable)
793 if len(result) != num_fields + 1:
794 msg = f'Expected {num_fields} arguments, got {len(result)}'
795 raise TypeError(msg)
796 return result
797
Jason R. Coombs518835f2020-04-16 08:28:09 -0400798 def __getitem__(self, key):
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500799 return tuple(self)[key]
Jason R. Coombs2c3d5082020-05-09 10:12:41 -0400800
801 def __len__(self):
802 return len(tuple(iter(self)))
Jason R. Coombs518835f2020-04-16 08:28:09 -0400803
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500804 def __reduce__(self):
805 return uname_result, tuple(self)[:len(self._fields)]
806
Larry Hastings68386bc2012-06-24 14:30:41 -0700807
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000808_uname_cache = None
809
Jason R. Coombs518835f2020-04-16 08:28:09 -0400810
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000811def uname():
812
813 """ Fairly portable uname interface. Returns a tuple
Victor Stinnerced39362013-12-09 00:14:52 +0100814 of strings (system, node, release, version, machine, processor)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000815 identifying the underlying platform.
816
817 Note that unlike the os.uname function this also returns
Brett Cannon8ab27df2003-08-05 03:52:04 +0000818 possible processor information as an additional tuple entry.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000819
820 Entries which cannot be determined are set to ''.
821
822 """
823 global _uname_cache
824
825 if _uname_cache is not None:
826 return _uname_cache
827
828 # Get some infos from the builtin os.uname API...
829 try:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400830 system, node, release, version, machine = infos = os.uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000831 except AttributeError:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400832 system = sys.platform
833 node = _node()
834 release = version = machine = ''
835 infos = ()
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000836
Jason R. Coombs518835f2020-04-16 08:28:09 -0400837 if not any(infos):
838 # uname is not available
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000839
840 # Try win32_ver() on win32 platforms
841 if system == 'win32':
Victor Stinnerced39362013-12-09 00:14:52 +0100842 release, version, csd, ptype = win32_ver()
Jason R. Coombs518835f2020-04-16 08:28:09 -0400843 machine = machine or _get_machine_win32()
Tim Peters0eadaac2003-04-24 16:02:54 +0000844
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000845 # Try the 'ver' system command available on some
846 # platforms
Jason R. Coombs518835f2020-04-16 08:28:09 -0400847 if not (release and version):
Victor Stinnerced39362013-12-09 00:14:52 +0100848 system, release, version = _syscmd_ver(system)
Marc-André Lemburgcdc79232004-06-19 17:17:00 +0000849 # Normalize system to what win32_ver() normally returns
850 # (_syscmd_ver() tends to return the vendor name as well)
851 if system == 'Microsoft Windows':
852 system = 'Windows'
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000853 elif system == 'Microsoft' and release == 'Windows':
854 # Under Windows Vista and Windows Server 2008,
855 # Microsoft changed the output of the ver command. The
856 # release is no longer printed. This causes the
857 # system and release to be misidentified.
858 system = 'Windows'
859 if '6.0' == version[:3]:
860 release = 'Vista'
861 else:
862 release = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000863
864 # In case we still don't know anything useful, we'll try to
865 # help ourselves
Victor Stinnerced39362013-12-09 00:14:52 +0100866 if system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000867 if not version:
868 if system == 'win32':
869 version = '32bit'
870 else:
871 version = '16bit'
872 system = 'Windows'
873
874 elif system[:4] == 'java':
Victor Stinnerced39362013-12-09 00:14:52 +0100875 release, vendor, vminfo, osinfo = java_ver()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000876 system = 'Java'
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000877 version = ', '.join(vminfo)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000878 if not version:
879 version = vendor
880
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000881 # System specific extensions
882 if system == 'OpenVMS':
883 # OpenVMS seems to have release and version mixed up
884 if not release or release == '0':
885 release = version
886 version = ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000887
888 # normalize name
889 if system == 'Microsoft' and release == 'Windows':
890 system = 'Windows'
891 release = 'Vista'
892
Jason R. Coombs518835f2020-04-16 08:28:09 -0400893 vals = system, node, release, version, machine
894 # Replace 'unknown' values with the more portable ''
895 _uname_cache = uname_result(*map(_unknown_as_blank, vals))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000896 return _uname_cache
897
898### Direct interfaces to some of the uname() return values
899
900def system():
901
902 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
903
904 An empty string is returned if the value cannot be determined.
905
906 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700907 return uname().system
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000908
909def node():
910
Brett Cannon8ab27df2003-08-05 03:52:04 +0000911 """ Returns the computer's network name (which may not be fully
912 qualified)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000913
914 An empty string is returned if the value cannot be determined.
915
916 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700917 return uname().node
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000918
919def release():
920
921 """ Returns the system's release, e.g. '2.2.0' or 'NT'
922
923 An empty string is returned if the value cannot be determined.
924
925 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700926 return uname().release
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000927
928def version():
929
930 """ Returns the system's release version, e.g. '#3 on degas'
931
932 An empty string is returned if the value cannot be determined.
933
934 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700935 return uname().version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000936
937def machine():
938
939 """ Returns the machine type, e.g. 'i386'
940
941 An empty string is returned if the value cannot be determined.
942
943 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700944 return uname().machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000945
946def processor():
947
948 """ Returns the (true) processor name, e.g. 'amdk6'
949
950 An empty string is returned if the value cannot be
951 determined. Note that many platforms do not provide this
952 information or simply return the same value as for machine(),
953 e.g. NetBSD does this.
954
955 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700956 return uname().processor
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000957
958### Various APIs for extracting information from sys.version
959
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000960_sys_version_parser = re.compile(
Martin Panter4e505532016-06-08 06:12:22 +0000961 r'([\w.+]+)\s*' # "version<space>"
962 r'\(#?([^,]+)' # "(#buildno"
963 r'(?:,\s*([\w ]*)' # ", builddate"
964 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
965 r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000966
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000967_ironpython_sys_version_parser = re.compile(
968 r'IronPython\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400969 r'([\d\.]+)'
970 r'(?: \(([\d\.]+)\))?'
971 r' on (.NET [\d\.]+)', re.ASCII)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000972
Ezio Melottif076f532013-10-21 03:03:32 +0300973# IronPython covering 2.6 and 2.7
974_ironpython26_sys_version_parser = re.compile(
975 r'([\d.]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400976 r'\(IronPython\s*'
977 r'[\d.]+\s*'
978 r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
Ezio Melottif076f532013-10-21 03:03:32 +0300979)
980
Benjamin Petersone549ead2009-03-28 21:42:05 +0000981_pypy_sys_version_parser = re.compile(
982 r'([\w.+]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400983 r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
984 r'\[PyPy [^\]]+\]?')
Benjamin Petersone549ead2009-03-28 21:42:05 +0000985
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000986_sys_version_cache = {}
987
988def _sys_version(sys_version=None):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000989
990 """ Returns a parsed version of Python's sys.version as tuple
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +0000991 (name, version, branch, revision, buildno, builddate, compiler)
992 referring to the Python implementation name, version, branch,
993 revision, build number, build date/time as string and the compiler
994 identification string.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000995
996 Note that unlike the Python sys.version, the returned value
997 for the Python version will always include the patchlevel (it
998 defaults to '.0').
999
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001000 The function returns empty strings for tuple entries that
1001 cannot be determined.
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001002
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001003 sys_version may be given to parse an alternative version
1004 string, e.g. if the version was read from a different Python
1005 interpreter.
1006
1007 """
1008 # Get the Python version
1009 if sys_version is None:
1010 sys_version = sys.version
1011
1012 # Try the cache first
1013 result = _sys_version_cache.get(sys_version, None)
1014 if result is not None:
1015 return result
1016
1017 # Parse it
Ezio Melottif076f532013-10-21 03:03:32 +03001018 if 'IronPython' in sys_version:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001019 # IronPython
1020 name = 'IronPython'
Ezio Melottif076f532013-10-21 03:03:32 +03001021 if sys_version.startswith('IronPython'):
1022 match = _ironpython_sys_version_parser.match(sys_version)
1023 else:
1024 match = _ironpython26_sys_version_parser.match(sys_version)
1025
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001026 if match is None:
1027 raise ValueError(
1028 'failed to parse IronPython sys.version: %s' %
1029 repr(sys_version))
Ezio Melottif076f532013-10-21 03:03:32 +03001030
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001031 version, alt_version, compiler = match.groups()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001032 buildno = ''
1033 builddate = ''
1034
Ezio Melottif076f532013-10-21 03:03:32 +03001035 elif sys.platform.startswith('java'):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001036 # Jython
1037 name = 'Jython'
Benjamin Petersone549ead2009-03-28 21:42:05 +00001038 match = _sys_version_parser.match(sys_version)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001039 if match is None:
1040 raise ValueError(
1041 'failed to parse Jython sys.version: %s' %
1042 repr(sys_version))
Benjamin Petersone549ead2009-03-28 21:42:05 +00001043 version, buildno, builddate, buildtime, _ = match.groups()
Martin Panter4e505532016-06-08 06:12:22 +00001044 if builddate is None:
1045 builddate = ''
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001046 compiler = sys.platform
Benjamin Petersone549ead2009-03-28 21:42:05 +00001047
1048 elif "PyPy" in sys_version:
1049 # PyPy
1050 name = "PyPy"
1051 match = _pypy_sys_version_parser.match(sys_version)
1052 if match is None:
1053 raise ValueError("failed to parse PyPy sys.version: %s" %
1054 repr(sys_version))
1055 version, buildno, builddate, buildtime = match.groups()
1056 compiler = ""
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001057
1058 else:
1059 # CPython
1060 match = _sys_version_parser.match(sys_version)
1061 if match is None:
1062 raise ValueError(
1063 'failed to parse CPython sys.version: %s' %
1064 repr(sys_version))
1065 version, buildno, builddate, buildtime, compiler = \
1066 match.groups()
Benjamin Petersone549ead2009-03-28 21:42:05 +00001067 name = 'CPython'
Martin Panter4e505532016-06-08 06:12:22 +00001068 if builddate is None:
1069 builddate = ''
1070 elif buildtime:
1071 builddate = builddate + ' ' + buildtime
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001072
Ned Deily5c4b0d02017-03-04 00:19:55 -05001073 if hasattr(sys, '_git'):
1074 _, branch, revision = sys._git
1075 elif hasattr(sys, '_mercurial'):
Georg Brandl82562422011-03-05 21:09:22 +01001076 _, branch, revision = sys._mercurial
Benjamin Petersone549ead2009-03-28 21:42:05 +00001077 else:
1078 branch = ''
1079 revision = ''
1080
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001081 # Add the patchlevel version if missing
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001082 l = version.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001083 if len(l) == 2:
1084 l.append('0')
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001085 version = '.'.join(l)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001086
1087 # Build and cache the result
1088 result = (name, version, branch, revision, buildno, builddate, compiler)
1089 _sys_version_cache[sys_version] = result
1090 return result
1091
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001092def python_implementation():
1093
1094 """ Returns a string identifying the Python implementation.
1095
1096 Currently, the following implementations are identified:
Ezio Melottif16898b2011-05-04 18:37:50 +03001097 'CPython' (C implementation of Python),
1098 'IronPython' (.NET implementation of Python),
1099 'Jython' (Java implementation of Python),
1100 'PyPy' (Python implementation of Python).
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001101
1102 """
1103 return _sys_version()[0]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001104
1105def python_version():
1106
1107 """ Returns the Python version as string 'major.minor.patchlevel'
1108
1109 Note that unlike the Python sys.version, the returned value
1110 will always include the patchlevel (it defaults to 0).
1111
1112 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001113 return _sys_version()[1]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001114
1115def python_version_tuple():
1116
1117 """ Returns the Python version as tuple (major, minor, patchlevel)
1118 of strings.
1119
1120 Note that unlike the Python sys.version, the returned value
1121 will always include the patchlevel (it defaults to 0).
1122
1123 """
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001124 return tuple(_sys_version()[1].split('.'))
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001125
1126def python_branch():
1127
1128 """ Returns a string identifying the Python implementation
1129 branch.
1130
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001131 For CPython this is the SCM branch from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001132 Python binary was built.
1133
1134 If not available, an empty string is returned.
1135
1136 """
Thomas Wouters9fe394c2007-02-05 01:24:16 +00001137
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001138 return _sys_version()[2]
1139
1140def python_revision():
1141
1142 """ Returns a string identifying the Python implementation
1143 revision.
1144
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001145 For CPython this is the SCM revision from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001146 Python binary was built.
1147
1148 If not available, an empty string is returned.
1149
1150 """
1151 return _sys_version()[3]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001152
1153def python_build():
1154
1155 """ Returns a tuple (buildno, builddate) stating the Python
1156 build number and date as strings.
1157
1158 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001159 return _sys_version()[4:6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001160
1161def python_compiler():
1162
1163 """ Returns a string identifying the compiler used for compiling
1164 Python.
1165
1166 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001167 return _sys_version()[6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001168
1169### The Opus Magnum of platform strings :-)
1170
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001171_platform_cache = {}
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001172
1173def platform(aliased=0, terse=0):
1174
1175 """ Returns a single string identifying the underlying platform
1176 with as much useful information as possible (but no more :).
Tim Peters0eadaac2003-04-24 16:02:54 +00001177
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001178 The output is intended to be human readable rather than
1179 machine parseable. It may look different on different
1180 platforms and this is intended.
1181
1182 If "aliased" is true, the function will use aliases for
1183 various platforms that report system names which differ from
1184 their common names, e.g. SunOS will be reported as
1185 Solaris. The system_alias() function is used to implement
1186 this.
1187
1188 Setting terse to true causes the function to return only the
1189 absolute minimum information needed to identify the platform.
1190
1191 """
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001192 result = _platform_cache.get((aliased, terse), None)
1193 if result is not None:
1194 return result
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001195
1196 # Get uname information and then apply platform specific cosmetics
1197 # to it...
Victor Stinnerced39362013-12-09 00:14:52 +01001198 system, node, release, version, machine, processor = uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001199 if machine == processor:
1200 processor = ''
1201 if aliased:
Victor Stinnerced39362013-12-09 00:14:52 +01001202 system, release, version = system_alias(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001203
Victor Stinnerea0ca212018-12-05 22:41:52 +01001204 if system == 'Darwin':
1205 # macOS (darwin kernel)
1206 macos_release = mac_ver()[0]
1207 if macos_release:
Victor Stinnerea0ca212018-12-05 22:41:52 +01001208 system = 'macOS'
1209 release = macos_release
1210
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001211 if system == 'Windows':
1212 # MS platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001213 rel, vers, csd, ptype = win32_ver(version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001214 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001215 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001216 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001217 platform = _platform(system, release, version, csd)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001218
1219 elif system in ('Linux',):
Petr Viktorin8b94b412018-05-16 11:51:18 -04001220 # check for libc vs. glibc
Victor Stinnera719c8f2019-06-27 09:04:28 +02001221 libcname, libcversion = libc_ver()
Petr Viktorin8b94b412018-05-16 11:51:18 -04001222 platform = _platform(system, release, machine, processor,
1223 'with',
1224 libcname+libcversion)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001225 elif system == 'Java':
1226 # Java platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001227 r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001228 if terse or not os_name:
Victor Stinnerced39362013-12-09 00:14:52 +01001229 platform = _platform(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001230 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001231 platform = _platform(system, release, version,
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001232 'on',
Victor Stinnerced39362013-12-09 00:14:52 +01001233 os_name, os_version, os_arch)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001234
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001235 else:
1236 # Generic handler
1237 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001238 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001239 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001240 bits, linkage = architecture(sys.executable)
1241 platform = _platform(system, release, machine,
1242 processor, bits, linkage)
Tim Peters0eadaac2003-04-24 16:02:54 +00001243
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001244 _platform_cache[(aliased, terse)] = platform
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001245 return platform
1246
Christian Heimes5c73afc2020-11-30 22:34:45 +01001247### freedesktop.org os-release standard
1248# https://www.freedesktop.org/software/systemd/man/os-release.html
1249
1250# NAME=value with optional quotes (' or "). The regular expression is less
1251# strict than shell lexer, but that's ok.
1252_os_release_line = re.compile(
1253 "^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
1254)
1255# unescape five special characters mentioned in the standard
1256_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
1257# /etc takes precedence over /usr/lib
Victor Stinnerbfda4f52020-12-23 17:35:53 +01001258_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
Christian Heimes5c73afc2020-11-30 22:34:45 +01001259_os_release_cache = None
1260
1261
1262def _parse_os_release(lines):
1263 # These fields are mandatory fields with well-known defaults
1264 # in pratice all Linux distributions override NAME, ID, and PRETTY_NAME.
1265 info = {
1266 "NAME": "Linux",
1267 "ID": "linux",
1268 "PRETTY_NAME": "Linux",
1269 }
1270
1271 for line in lines:
1272 mo = _os_release_line.match(line)
1273 if mo is not None:
1274 info[mo.group('name')] = _os_release_unescape.sub(
1275 r"\1", mo.group('value')
1276 )
1277
1278 return info
1279
1280
1281def freedesktop_os_release():
1282 """Return operation system identification from freedesktop.org os-release
1283 """
1284 global _os_release_cache
1285
1286 if _os_release_cache is None:
1287 errno = None
1288 for candidate in _os_release_candidates:
1289 try:
1290 with open(candidate, encoding="utf-8") as f:
1291 _os_release_cache = _parse_os_release(f)
1292 break
1293 except OSError as e:
1294 errno = e.errno
1295 else:
1296 raise OSError(
1297 errno,
1298 f"Unable to read files {', '.join(_os_release_candidates)}"
1299 )
1300
1301 return _os_release_cache.copy()
1302
1303
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001304### Command line interface
1305
1306if __name__ == '__main__':
1307 # Default is to print the aliased verbose platform string
Tim Peters0eadaac2003-04-24 16:02:54 +00001308 terse = ('terse' in sys.argv or '--terse' in sys.argv)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001309 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
Victor Stinnerced39362013-12-09 00:14:52 +01001310 print(platform(aliased, terse))
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001311 sys.exit(0)