blob: e32f9c11cdbf1685d0dcb907b380cef8c1ae95d3 [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,
Miss Islington (bot)425756a2021-07-13 12:48:23 -0700283 stdin=subprocess.DEVNULL,
Victor Stinner3a521f02018-12-07 11:10:33 +0100284 stderr=subprocess.DEVNULL,
285 text=True,
286 shell=True)
287 except (OSError, subprocess.CalledProcessError) as why:
288 #print('Command %s failed: %s' % (cmd, why))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000289 continue
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000290 else:
291 break
292 else:
Victor Stinnerced39362013-12-09 00:14:52 +0100293 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000294
295 # Parse the output
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000296 info = info.strip()
Marc-André Lemburg366a0fe2003-04-24 11:46:35 +0000297 m = _ver_output.match(info)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000298 if m is not None:
Victor Stinnerced39362013-12-09 00:14:52 +0100299 system, release, version = m.groups()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000300 # Strip trailing dots from version and release
301 if release[-1] == '.':
302 release = release[:-1]
303 if version[-1] == '.':
304 version = version[:-1]
305 # Normalize the version and build strings (eliminating additional
306 # zeros)
307 version = _norm_version(version)
Victor Stinnerced39362013-12-09 00:14:52 +0100308 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000309
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700310_WIN32_CLIENT_RELEASES = {
311 (5, 0): "2000",
312 (5, 1): "XP",
313 # Strictly, 5.2 client is XP 64-bit, but platform.py historically
314 # has always called it 2003 Server
315 (5, 2): "2003Server",
316 (5, None): "post2003",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000317
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700318 (6, 0): "Vista",
319 (6, 1): "7",
320 (6, 2): "8",
321 (6, 3): "8.1",
322 (6, None): "post8.1",
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000323
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700324 (10, 0): "10",
325 (10, None): "post10",
326}
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000327
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700328# Server release name lookup will default to client names if necessary
329_WIN32_SERVER_RELEASES = {
330 (5, 2): "2003Server",
331
332 (6, 0): "2008Server",
333 (6, 1): "2008ServerR2",
334 (6, 2): "2012Server",
335 (6, 3): "2012ServerR2",
336 (6, None): "post2012ServerR2",
337}
338
Paul Monson62dfd7d2019-04-25 11:36:45 -0700339def win32_is_iot():
340 return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
341
342def win32_edition():
343 try:
344 try:
345 import winreg
346 except ImportError:
347 import _winreg as winreg
348 except ImportError:
349 pass
350 else:
351 try:
352 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
353 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
354 return winreg.QueryValueEx(key, 'EditionId')[0]
355 except OSError:
356 pass
357
358 return None
359
Victor Stinnerced39362013-12-09 00:14:52 +0100360def win32_ver(release='', version='', csd='', ptype=''):
Steve Dower8f278f12015-09-22 17:35:24 -0700361 try:
362 from sys import getwindowsversion
363 except ImportError:
364 return release, version, csd, ptype
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700365
366 winver = getwindowsversion()
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530367 try:
368 major, minor, build = map(int, _syscmd_ver()[2].split('.'))
369 except ValueError:
370 major, minor, build = winver.platform_version or winver[:3]
371 version = '{0}.{1}.{2}'.format(major, minor, build)
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700372
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530373 release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or
374 _WIN32_CLIENT_RELEASES.get((major, None)) or
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700375 release)
376
377 # getwindowsversion() reflect the compatibility mode Python is
378 # running under, and so the service pack value is only going to be
379 # valid if the versions match.
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530380 if winver[:2] == (major, minor):
Christian Heimes02781dc2008-03-21 01:11:52 +0000381 try:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700382 csd = 'SP{}'.format(winver.service_pack_major)
Christian Heimes02781dc2008-03-21 01:11:52 +0000383 except AttributeError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700384 if csd[:13] == 'Service Pack ':
385 csd = 'SP' + csd[13:]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000386
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700387 # VER_NT_SERVER = 3
Steve Dower41519b22016-09-09 09:46:56 -0700388 if getattr(winver, 'product_type', None) == 3:
Shreyan Avigyan2a3f4892021-04-22 22:13:37 +0530389 release = (_WIN32_SERVER_RELEASES.get((major, minor)) or
390 _WIN32_SERVER_RELEASES.get((major, None)) or
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700391 release)
Alexandre Vassalottie52e3782009-07-17 09:18:18 +0000392
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000393 try:
Steve Dowerd307d052019-04-22 11:40:12 -0700394 try:
395 import winreg
396 except ImportError:
397 import _winreg as winreg
398 except ImportError:
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700399 pass
Steve Dowerd307d052019-04-22 11:40:12 -0700400 else:
401 try:
402 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
Dennis Sweeney1e7e4512020-05-04 22:33:17 -0400403 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
404 ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
405 except OSError:
Steve Dowerd307d052019-04-22 11:40:12 -0700406 pass
Tim Peters0eadaac2003-04-24 16:02:54 +0000407
Victor Stinnerced39362013-12-09 00:14:52 +0100408 return release, version, csd, ptype
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000409
Steve Dowerb9f4fea2015-09-22 17:23:39 -0700410
Ronald Oussorene186e382010-07-23 11:54:59 +0000411def _mac_ver_xml():
412 fn = '/System/Library/CoreServices/SystemVersion.plist'
413 if not os.path.exists(fn):
414 return None
415
416 try:
417 import plistlib
Brett Cannoncd171c82013-07-04 17:43:24 -0400418 except ImportError:
Ronald Oussorene186e382010-07-23 11:54:59 +0000419 return None
420
Ned Deily936dfae2014-01-13 11:34:19 -0800421 with open(fn, 'rb') as f:
422 pl = plistlib.load(f)
Ronald Oussorene186e382010-07-23 11:54:59 +0000423 release = pl['ProductVersion']
Victor Stinnerced39362013-12-09 00:14:52 +0100424 versioninfo = ('', '', '')
Larry Hastings605a62d2012-06-24 04:33:36 -0700425 machine = os.uname().machine
Ronald Oussorenfcd77012010-08-03 07:42:42 +0000426 if machine in ('ppc', 'Power Macintosh'):
Ezio Melotti9a3777e2013-08-17 15:53:55 +0300427 # Canonical name
Ronald Oussorene186e382010-07-23 11:54:59 +0000428 machine = 'PowerPC'
429
Victor Stinnerced39362013-12-09 00:14:52 +0100430 return release, versioninfo, machine
Ronald Oussorene186e382010-07-23 11:54:59 +0000431
432
Victor Stinnerced39362013-12-09 00:14:52 +0100433def mac_ver(release='', versioninfo=('', '', ''), machine=''):
Ronald Oussorene186e382010-07-23 11:54:59 +0000434
Victor Stinnerb0e08772018-12-12 17:48:08 +0100435 """ Get macOS version information and return it as tuple (release,
Ronald Oussorene186e382010-07-23 11:54:59 +0000436 versioninfo, machine) with versioninfo being a tuple (version,
437 dev_stage, non_release_version).
438
Ezio Melotti30b9d5d2013-08-17 15:50:46 +0300439 Entries which cannot be determined are set to the parameter values
Ronald Oussorene186e382010-07-23 11:54:59 +0000440 which default to ''. All tuple entries are strings.
441 """
442
443 # First try reading the information from an XML file which should
444 # always be present
445 info = _mac_ver_xml()
446 if info is not None:
447 return info
448
Ronald Oussorene186e382010-07-23 11:54:59 +0000449 # If that also doesn't work return the default values
Victor Stinnerced39362013-12-09 00:14:52 +0100450 return release, versioninfo, machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000451
Victor Stinnerced39362013-12-09 00:14:52 +0100452def _java_getprop(name, default):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000453
454 from java.lang import System
455 try:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000456 value = System.getProperty(name)
457 if value is None:
458 return default
459 return value
460 except AttributeError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000461 return default
462
Victor Stinnerced39362013-12-09 00:14:52 +0100463def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
Tim Peters0eadaac2003-04-24 16:02:54 +0000464
Brett Cannon8ab27df2003-08-05 03:52:04 +0000465 """ Version interface for Jython.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000466
Victor Stinnerced39362013-12-09 00:14:52 +0100467 Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
468 a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
469 tuple (os_name, os_version, os_arch).
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000470
471 Values which cannot be determined are set to the defaults
472 given as parameters (which all default to '').
473
474 """
475 # Import the needed APIs
476 try:
477 import java.lang
Brett Cannoncd171c82013-07-04 17:43:24 -0400478 except ImportError:
Victor Stinnerced39362013-12-09 00:14:52 +0100479 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000480
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000481 vendor = _java_getprop('java.vendor', vendor)
482 release = _java_getprop('java.version', release)
483 vm_name, vm_release, vm_vendor = vminfo
484 vm_name = _java_getprop('java.vm.name', vm_name)
485 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
486 vm_release = _java_getprop('java.vm.version', vm_release)
487 vminfo = vm_name, vm_release, vm_vendor
488 os_name, os_version, os_arch = osinfo
489 os_arch = _java_getprop('java.os.arch', os_arch)
490 os_name = _java_getprop('java.os.name', os_name)
491 os_version = _java_getprop('java.os.version', os_version)
492 osinfo = os_name, os_version, os_arch
Tim Peters0eadaac2003-04-24 16:02:54 +0000493
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000494 return release, vendor, vminfo, osinfo
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000495
496### System name aliasing
497
Victor Stinnerced39362013-12-09 00:14:52 +0100498def system_alias(system, release, version):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000499
Victor Stinnerced39362013-12-09 00:14:52 +0100500 """ Returns (system, release, version) aliased to common
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000501 marketing names used for some systems.
502
503 It also does some reordering of the information in some cases
504 where it would otherwise cause confusion.
505
506 """
Victor Stinnerb0e08772018-12-12 17:48:08 +0100507 if system == 'SunOS':
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000508 # Sun's OS
509 if release < '5':
510 # These releases use the old name SunOS
Victor Stinnerced39362013-12-09 00:14:52 +0100511 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000512 # Modify release (marketing release = SunOS release - 3)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000513 l = release.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000514 if l:
515 try:
516 major = int(l[0])
517 except ValueError:
518 pass
519 else:
520 major = major - 3
521 l[0] = str(major)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000522 release = '.'.join(l)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000523 if release < '6':
524 system = 'Solaris'
525 else:
526 # XXX Whatever the new SunOS marketing name is...
527 system = 'Solaris'
528
Victor Stinnerced39362013-12-09 00:14:52 +0100529 elif system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000530 # In case one of the other tricks
531 system = 'Windows'
532
Victor Stinner60875db2018-12-18 19:51:35 +0100533 # bpo-35516: Don't replace Darwin with macOS since input release and
534 # version arguments can be different than the currently running version.
535
Victor Stinnerced39362013-12-09 00:14:52 +0100536 return system, release, version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000537
538### Various internal helpers
539
540def _platform(*args):
541
542 """ Helper to format the platform string in a filename
543 compatible format e.g. "system-version-machine".
544 """
545 # Format the platform string
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000546 platform = '-'.join(x.strip() for x in filter(len, args))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000547
548 # Cleanup some possible filename obstacles...
Victor Stinnerced39362013-12-09 00:14:52 +0100549 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('(', '-')
556 platform = platform.replace(')', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000557
558 # No need to report 'unknown' information...
Victor Stinnerced39362013-12-09 00:14:52 +0100559 platform = platform.replace('unknown', '')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000560
561 # Fold '--'s and remove trailing '-'
562 while 1:
Victor Stinnerced39362013-12-09 00:14:52 +0100563 cleaned = platform.replace('--', '-')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000564 if cleaned == platform:
565 break
566 platform = cleaned
567 while platform[-1] == '-':
568 platform = platform[:-1]
569
570 return platform
571
572def _node(default=''):
573
574 """ Helper to determine the node name of this machine.
575 """
576 try:
577 import socket
Brett Cannoncd171c82013-07-04 17:43:24 -0400578 except ImportError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000579 # No sockets...
580 return default
581 try:
582 return socket.gethostname()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200583 except OSError:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000584 # Still not working...
585 return default
586
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000587def _follow_symlinks(filepath):
588
589 """ In case filepath is a symlink, follow it until a
590 real file is reached.
591 """
Georg Brandl673f7ef2008-01-05 21:20:19 +0000592 filepath = os.path.abspath(filepath)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000593 while os.path.islink(filepath):
594 filepath = os.path.normpath(
Victor Stinnerced39362013-12-09 00:14:52 +0100595 os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000596 return filepath
597
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000598
Victor Stinnerced39362013-12-09 00:14:52 +0100599def _syscmd_file(target, default=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000600
601 """ Interface to the system's file command.
602
603 The function uses the -b option of the file command to have it
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000604 omit the filename in its output. Follow the symlinks. It returns
605 default in case the command should fail.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000606
607 """
Victor Stinnerced39362013-12-09 00:14:52 +0100608 if sys.platform in ('dos', 'win32', 'win16'):
Hirokazu Yamamotod26782e2008-09-01 14:35:47 +0000609 # XXX Others too ?
610 return default
Victor Stinnerb8e689a2018-12-04 17:18:12 +0100611
612 import subprocess
Jesus Ceafc990e92012-10-04 13:51:43 +0200613 target = _follow_symlinks(target)
Victor Stinner0af9c332018-12-17 18:47:24 +0100614 # "file" output is locale dependent: force the usage of the C locale
615 # to get deterministic behavior.
616 env = dict(os.environ, LC_ALL='C')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000617 try:
Victor Stinner0af9c332018-12-17 18:47:24 +0100618 # -b: do not prepend filenames to output lines (brief mode)
619 output = subprocess.check_output(['file', '-b', target],
Victor Stinner3a521f02018-12-07 11:10:33 +0100620 stderr=subprocess.DEVNULL,
Victor Stinner0af9c332018-12-17 18:47:24 +0100621 env=env)
Victor Stinner3a521f02018-12-07 11:10:33 +0100622 except (OSError, subprocess.CalledProcessError):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000623 return default
Victor Stinner0af9c332018-12-17 18:47:24 +0100624 if not output:
625 return default
626 # With the C locale, the output should be mostly ASCII-compatible.
627 # Decode from Latin-1 to prevent Unicode decode error.
628 return output.decode('latin-1')
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000629
630### Information about the used architecture
631
632# Default values for architecture; non-empty strings override the
633# defaults given as parameters
634_default_architecture = {
Victor Stinnerced39362013-12-09 00:14:52 +0100635 'win32': ('', 'WindowsPE'),
636 'win16': ('', 'Windows'),
637 'dos': ('', 'MSDOS'),
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000638}
639
Victor Stinnerced39362013-12-09 00:14:52 +0100640def architecture(executable=sys.executable, bits='', linkage=''):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000641
642 """ Queries the given executable (defaults to the Python interpreter
Brett Cannon8ab27df2003-08-05 03:52:04 +0000643 binary) for various architecture information.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000644
Victor Stinnerced39362013-12-09 00:14:52 +0100645 Returns a tuple (bits, linkage) which contains information about
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000646 the bit architecture and the linkage format used for the
647 executable. Both values are returned as strings.
648
649 Values that cannot be determined are returned as given by the
650 parameter presets. If bits is given as '', the sizeof(pointer)
651 (or sizeof(long) on Python version < 1.5.2) is used as
652 indicator for the supported pointer size.
653
654 The function relies on the system's "file" command to do the
655 actual work. This is available on most if not all Unix
Brett Cannon8ab27df2003-08-05 03:52:04 +0000656 platforms. On some non-Unix platforms where the "file" command
657 does not exist and the executable is set to the Python interpreter
658 binary defaults from _default_architecture are used.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000659
660 """
661 # Use the sizeof(pointer) as default number of bits if nothing
662 # else is given as default.
663 if not bits:
664 import struct
Victor Stinner4aa917c2018-12-14 13:14:10 +0100665 size = struct.calcsize('P')
666 bits = str(size * 8) + 'bit'
Tim Peters0eadaac2003-04-24 16:02:54 +0000667
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000668 # Get data from the 'file' system command
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000669 if executable:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000670 fileout = _syscmd_file(executable, '')
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000671 else:
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000672 fileout = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000673
Victor Stinnerddfb2c32010-08-13 16:30:15 +0000674 if not fileout and \
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000675 executable == sys.executable:
676 # "file" command did not return anything; we'll try to provide
Tim Peters0eadaac2003-04-24 16:02:54 +0000677 # some sensible defaults then...
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000678 if sys.platform in _default_architecture:
Victor Stinnerced39362013-12-09 00:14:52 +0100679 b, l = _default_architecture[sys.platform]
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000680 if b:
681 bits = b
682 if l:
683 linkage = l
Victor Stinnerced39362013-12-09 00:14:52 +0100684 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000685
Victor Stinner0af9c332018-12-17 18:47:24 +0100686 if 'executable' not in fileout and 'shared object' not in fileout:
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000687 # Format not supported
Victor Stinnerced39362013-12-09 00:14:52 +0100688 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000689
690 # Bits
691 if '32-bit' in fileout:
692 bits = '32bit'
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000693 elif '64-bit' in fileout:
694 bits = '64bit'
695
696 # Linkage
697 if 'ELF' in fileout:
698 linkage = 'ELF'
699 elif 'PE' in fileout:
700 # E.g. Windows uses this format
701 if 'Windows' in fileout:
702 linkage = 'WindowsPE'
703 else:
704 linkage = 'PE'
705 elif 'COFF' in fileout:
706 linkage = 'COFF'
707 elif 'MS-DOS' in fileout:
708 linkage = 'MSDOS'
709 else:
710 # XXX the A.OUT format also falls under this class...
711 pass
712
Victor Stinnerced39362013-12-09 00:14:52 +0100713 return bits, linkage
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000714
Jason R. Coombs518835f2020-04-16 08:28:09 -0400715
716def _get_machine_win32():
717 # Try to use the PROCESSOR_* environment variables
718 # available on Win XP and later; see
719 # http://support.microsoft.com/kb/888731 and
720 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
721
722 # WOW64 processes mask the native architecture
723 return (
724 os.environ.get('PROCESSOR_ARCHITEW6432', '') or
725 os.environ.get('PROCESSOR_ARCHITECTURE', '')
726 )
727
728
729class _Processor:
730 @classmethod
731 def get(cls):
732 func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess)
733 return func() or ''
734
735 def get_win32():
736 return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
737
738 def get_OpenVMS():
739 try:
740 import vms_lib
741 except ImportError:
742 pass
743 else:
744 csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
745 return 'Alpha' if cpu_number >= 128 else 'VAX'
746
747 def from_subprocess():
748 """
749 Fall back to `uname -p`
750 """
751 try:
752 return subprocess.check_output(
753 ['uname', '-p'],
754 stderr=subprocess.DEVNULL,
755 text=True,
756 ).strip()
757 except (OSError, subprocess.CalledProcessError):
758 pass
759
760
761def _unknown_as_blank(val):
762 return '' if val == 'unknown' else val
763
764
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000765### Portable uname() interface
Tim Peters0eadaac2003-04-24 16:02:54 +0000766
Jason R. Coombs518835f2020-04-16 08:28:09 -0400767class uname_result(
768 collections.namedtuple(
769 "uname_result_base",
770 "system node release version machine")
771 ):
772 """
773 A uname_result that's largely compatible with a
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500774 simple namedtuple except that 'processor' is
Jason R. Coombs518835f2020-04-16 08:28:09 -0400775 resolved late and cached to avoid calling "uname"
776 except when needed.
777 """
778
779 @functools.cached_property
780 def processor(self):
781 return _unknown_as_blank(_Processor.get())
782
783 def __iter__(self):
784 return itertools.chain(
785 super().__iter__(),
786 (self.processor,)
787 )
788
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500789 @classmethod
790 def _make(cls, iterable):
791 # override factory to affect length check
792 num_fields = len(cls._fields)
793 result = cls.__new__(cls, *iterable)
794 if len(result) != num_fields + 1:
795 msg = f'Expected {num_fields} arguments, got {len(result)}'
796 raise TypeError(msg)
797 return result
798
Jason R. Coombs518835f2020-04-16 08:28:09 -0400799 def __getitem__(self, key):
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500800 return tuple(self)[key]
Jason R. Coombs2c3d5082020-05-09 10:12:41 -0400801
802 def __len__(self):
803 return len(tuple(iter(self)))
Jason R. Coombs518835f2020-04-16 08:28:09 -0400804
Jason R. Coombsa6fd0f42020-12-31 14:08:03 -0500805 def __reduce__(self):
806 return uname_result, tuple(self)[:len(self._fields)]
807
Larry Hastings68386bc2012-06-24 14:30:41 -0700808
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000809_uname_cache = None
810
Jason R. Coombs518835f2020-04-16 08:28:09 -0400811
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000812def uname():
813
814 """ Fairly portable uname interface. Returns a tuple
Victor Stinnerced39362013-12-09 00:14:52 +0100815 of strings (system, node, release, version, machine, processor)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000816 identifying the underlying platform.
817
818 Note that unlike the os.uname function this also returns
Brett Cannon8ab27df2003-08-05 03:52:04 +0000819 possible processor information as an additional tuple entry.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000820
821 Entries which cannot be determined are set to ''.
822
823 """
824 global _uname_cache
825
826 if _uname_cache is not None:
827 return _uname_cache
828
829 # Get some infos from the builtin os.uname API...
830 try:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400831 system, node, release, version, machine = infos = os.uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000832 except AttributeError:
Jason R. Coombs518835f2020-04-16 08:28:09 -0400833 system = sys.platform
834 node = _node()
835 release = version = machine = ''
836 infos = ()
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000837
Jason R. Coombs518835f2020-04-16 08:28:09 -0400838 if not any(infos):
839 # uname is not available
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000840
841 # Try win32_ver() on win32 platforms
842 if system == 'win32':
Victor Stinnerced39362013-12-09 00:14:52 +0100843 release, version, csd, ptype = win32_ver()
Jason R. Coombs518835f2020-04-16 08:28:09 -0400844 machine = machine or _get_machine_win32()
Tim Peters0eadaac2003-04-24 16:02:54 +0000845
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000846 # Try the 'ver' system command available on some
847 # platforms
Jason R. Coombs518835f2020-04-16 08:28:09 -0400848 if not (release and version):
Victor Stinnerced39362013-12-09 00:14:52 +0100849 system, release, version = _syscmd_ver(system)
Marc-André Lemburgcdc79232004-06-19 17:17:00 +0000850 # Normalize system to what win32_ver() normally returns
851 # (_syscmd_ver() tends to return the vendor name as well)
852 if system == 'Microsoft Windows':
853 system = 'Windows'
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000854 elif system == 'Microsoft' and release == 'Windows':
855 # Under Windows Vista and Windows Server 2008,
856 # Microsoft changed the output of the ver command. The
857 # release is no longer printed. This causes the
858 # system and release to be misidentified.
859 system = 'Windows'
860 if '6.0' == version[:3]:
861 release = 'Vista'
862 else:
863 release = ''
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000864
865 # In case we still don't know anything useful, we'll try to
866 # help ourselves
Victor Stinnerced39362013-12-09 00:14:52 +0100867 if system in ('win32', 'win16'):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000868 if not version:
869 if system == 'win32':
870 version = '32bit'
871 else:
872 version = '16bit'
873 system = 'Windows'
874
875 elif system[:4] == 'java':
Victor Stinnerced39362013-12-09 00:14:52 +0100876 release, vendor, vminfo, osinfo = java_ver()
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000877 system = 'Java'
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000878 version = ', '.join(vminfo)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000879 if not version:
880 version = vendor
881
Amaury Forgeot d'Arc35c86582008-06-17 21:11:29 +0000882 # System specific extensions
883 if system == 'OpenVMS':
884 # OpenVMS seems to have release and version mixed up
885 if not release or release == '0':
886 release = version
887 version = ''
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000888
889 # normalize name
890 if system == 'Microsoft' and release == 'Windows':
891 system = 'Windows'
892 release = 'Vista'
893
Jason R. Coombs518835f2020-04-16 08:28:09 -0400894 vals = system, node, release, version, machine
895 # Replace 'unknown' values with the more portable ''
896 _uname_cache = uname_result(*map(_unknown_as_blank, vals))
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000897 return _uname_cache
898
899### Direct interfaces to some of the uname() return values
900
901def system():
902
903 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.
904
905 An empty string is returned if the value cannot be determined.
906
907 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700908 return uname().system
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000909
910def node():
911
Brett Cannon8ab27df2003-08-05 03:52:04 +0000912 """ Returns the computer's network name (which may not be fully
913 qualified)
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000914
915 An empty string is returned if the value cannot be determined.
916
917 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700918 return uname().node
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000919
920def release():
921
922 """ Returns the system's release, e.g. '2.2.0' or 'NT'
923
924 An empty string is returned if the value cannot be determined.
925
926 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700927 return uname().release
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000928
929def version():
930
931 """ Returns the system's release version, e.g. '#3 on degas'
932
933 An empty string is returned if the value cannot be determined.
934
935 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700936 return uname().version
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000937
938def machine():
939
940 """ Returns the machine type, e.g. 'i386'
941
942 An empty string is returned if the value cannot be determined.
943
944 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700945 return uname().machine
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000946
947def processor():
948
949 """ Returns the (true) processor name, e.g. 'amdk6'
950
951 An empty string is returned if the value cannot be
952 determined. Note that many platforms do not provide this
953 information or simply return the same value as for machine(),
954 e.g. NetBSD does this.
955
956 """
Larry Hastings68386bc2012-06-24 14:30:41 -0700957 return uname().processor
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000958
959### Various APIs for extracting information from sys.version
960
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000961_sys_version_parser = re.compile(
Martin Panter4e505532016-06-08 06:12:22 +0000962 r'([\w.+]+)\s*' # "version<space>"
963 r'\(#?([^,]+)' # "(#buildno"
964 r'(?:,\s*([\w ]*)' # ", builddate"
965 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
966 r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000967
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000968_ironpython_sys_version_parser = re.compile(
969 r'IronPython\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400970 r'([\d\.]+)'
971 r'(?: \(([\d\.]+)\))?'
972 r' on (.NET [\d\.]+)', re.ASCII)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000973
Ezio Melottif076f532013-10-21 03:03:32 +0300974# IronPython covering 2.6 and 2.7
975_ironpython26_sys_version_parser = re.compile(
976 r'([\d.]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400977 r'\(IronPython\s*'
978 r'[\d.]+\s*'
979 r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
Ezio Melottif076f532013-10-21 03:03:32 +0300980)
981
Benjamin Petersone549ead2009-03-28 21:42:05 +0000982_pypy_sys_version_parser = re.compile(
983 r'([\w.+]+)\s*'
R David Murray44b548d2016-09-08 13:59:53 -0400984 r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
985 r'\[PyPy [^\]]+\]?')
Benjamin Petersone549ead2009-03-28 21:42:05 +0000986
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000987_sys_version_cache = {}
988
989def _sys_version(sys_version=None):
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000990
991 """ Returns a parsed version of Python's sys.version as tuple
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +0000992 (name, version, branch, revision, buildno, builddate, compiler)
993 referring to the Python implementation name, version, branch,
994 revision, build number, build date/time as string and the compiler
995 identification string.
Marc-André Lemburg246d8472003-04-24 11:36:11 +0000996
997 Note that unlike the Python sys.version, the returned value
998 for the Python version will always include the patchlevel (it
999 defaults to '.0').
1000
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001001 The function returns empty strings for tuple entries that
1002 cannot be determined.
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001003
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001004 sys_version may be given to parse an alternative version
1005 string, e.g. if the version was read from a different Python
1006 interpreter.
1007
1008 """
1009 # Get the Python version
1010 if sys_version is None:
1011 sys_version = sys.version
1012
1013 # Try the cache first
1014 result = _sys_version_cache.get(sys_version, None)
1015 if result is not None:
1016 return result
1017
1018 # Parse it
Ezio Melottif076f532013-10-21 03:03:32 +03001019 if 'IronPython' in sys_version:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001020 # IronPython
1021 name = 'IronPython'
Ezio Melottif076f532013-10-21 03:03:32 +03001022 if sys_version.startswith('IronPython'):
1023 match = _ironpython_sys_version_parser.match(sys_version)
1024 else:
1025 match = _ironpython26_sys_version_parser.match(sys_version)
1026
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001027 if match is None:
1028 raise ValueError(
1029 'failed to parse IronPython sys.version: %s' %
1030 repr(sys_version))
Ezio Melottif076f532013-10-21 03:03:32 +03001031
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001032 version, alt_version, compiler = match.groups()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001033 buildno = ''
1034 builddate = ''
1035
Ezio Melottif076f532013-10-21 03:03:32 +03001036 elif sys.platform.startswith('java'):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001037 # Jython
1038 name = 'Jython'
Benjamin Petersone549ead2009-03-28 21:42:05 +00001039 match = _sys_version_parser.match(sys_version)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001040 if match is None:
1041 raise ValueError(
1042 'failed to parse Jython sys.version: %s' %
1043 repr(sys_version))
Benjamin Petersone549ead2009-03-28 21:42:05 +00001044 version, buildno, builddate, buildtime, _ = match.groups()
Martin Panter4e505532016-06-08 06:12:22 +00001045 if builddate is None:
1046 builddate = ''
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001047 compiler = sys.platform
Benjamin Petersone549ead2009-03-28 21:42:05 +00001048
1049 elif "PyPy" in sys_version:
1050 # PyPy
1051 name = "PyPy"
1052 match = _pypy_sys_version_parser.match(sys_version)
1053 if match is None:
1054 raise ValueError("failed to parse PyPy sys.version: %s" %
1055 repr(sys_version))
1056 version, buildno, builddate, buildtime = match.groups()
1057 compiler = ""
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001058
1059 else:
1060 # CPython
1061 match = _sys_version_parser.match(sys_version)
1062 if match is None:
1063 raise ValueError(
1064 'failed to parse CPython sys.version: %s' %
1065 repr(sys_version))
1066 version, buildno, builddate, buildtime, compiler = \
1067 match.groups()
Benjamin Petersone549ead2009-03-28 21:42:05 +00001068 name = 'CPython'
Martin Panter4e505532016-06-08 06:12:22 +00001069 if builddate is None:
1070 builddate = ''
1071 elif buildtime:
1072 builddate = builddate + ' ' + buildtime
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001073
Ned Deily5c4b0d02017-03-04 00:19:55 -05001074 if hasattr(sys, '_git'):
1075 _, branch, revision = sys._git
1076 elif hasattr(sys, '_mercurial'):
Georg Brandl82562422011-03-05 21:09:22 +01001077 _, branch, revision = sys._mercurial
Benjamin Petersone549ead2009-03-28 21:42:05 +00001078 else:
1079 branch = ''
1080 revision = ''
1081
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001082 # Add the patchlevel version if missing
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001083 l = version.split('.')
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001084 if len(l) == 2:
1085 l.append('0')
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001086 version = '.'.join(l)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001087
1088 # Build and cache the result
1089 result = (name, version, branch, revision, buildno, builddate, compiler)
1090 _sys_version_cache[sys_version] = result
1091 return result
1092
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001093def python_implementation():
1094
1095 """ Returns a string identifying the Python implementation.
1096
1097 Currently, the following implementations are identified:
Ezio Melottif16898b2011-05-04 18:37:50 +03001098 'CPython' (C implementation of Python),
1099 'IronPython' (.NET implementation of Python),
1100 'Jython' (Java implementation of Python),
1101 'PyPy' (Python implementation of Python).
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001102
1103 """
1104 return _sys_version()[0]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001105
1106def python_version():
1107
1108 """ Returns the Python version as string 'major.minor.patchlevel'
1109
1110 Note that unlike the Python sys.version, the returned value
1111 will always include the patchlevel (it defaults to 0).
1112
1113 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001114 return _sys_version()[1]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001115
1116def python_version_tuple():
1117
1118 """ Returns the Python version as tuple (major, minor, patchlevel)
1119 of strings.
1120
1121 Note that unlike the Python sys.version, the returned value
1122 will always include the patchlevel (it defaults to 0).
1123
1124 """
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001125 return tuple(_sys_version()[1].split('.'))
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001126
1127def python_branch():
1128
1129 """ Returns a string identifying the Python implementation
1130 branch.
1131
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001132 For CPython this is the SCM branch from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001133 Python binary was built.
1134
1135 If not available, an empty string is returned.
1136
1137 """
Thomas Wouters9fe394c2007-02-05 01:24:16 +00001138
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001139 return _sys_version()[2]
1140
1141def python_revision():
1142
1143 """ Returns a string identifying the Python implementation
1144 revision.
1145
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +01001146 For CPython this is the SCM revision from which the
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001147 Python binary was built.
1148
1149 If not available, an empty string is returned.
1150
1151 """
1152 return _sys_version()[3]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001153
1154def python_build():
1155
1156 """ Returns a tuple (buildno, builddate) stating the Python
1157 build number and date as strings.
1158
1159 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001160 return _sys_version()[4:6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001161
1162def python_compiler():
1163
1164 """ Returns a string identifying the compiler used for compiling
1165 Python.
1166
1167 """
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001168 return _sys_version()[6]
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001169
1170### The Opus Magnum of platform strings :-)
1171
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001172_platform_cache = {}
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001173
1174def platform(aliased=0, terse=0):
1175
1176 """ Returns a single string identifying the underlying platform
1177 with as much useful information as possible (but no more :).
Tim Peters0eadaac2003-04-24 16:02:54 +00001178
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001179 The output is intended to be human readable rather than
1180 machine parseable. It may look different on different
1181 platforms and this is intended.
1182
1183 If "aliased" is true, the function will use aliases for
1184 various platforms that report system names which differ from
1185 their common names, e.g. SunOS will be reported as
1186 Solaris. The system_alias() function is used to implement
1187 this.
1188
1189 Setting terse to true causes the function to return only the
1190 absolute minimum information needed to identify the platform.
1191
1192 """
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001193 result = _platform_cache.get((aliased, terse), None)
1194 if result is not None:
1195 return result
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001196
1197 # Get uname information and then apply platform specific cosmetics
1198 # to it...
Victor Stinnerced39362013-12-09 00:14:52 +01001199 system, node, release, version, machine, processor = uname()
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001200 if machine == processor:
1201 processor = ''
1202 if aliased:
Victor Stinnerced39362013-12-09 00:14:52 +01001203 system, release, version = system_alias(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001204
Victor Stinnerea0ca212018-12-05 22:41:52 +01001205 if system == 'Darwin':
1206 # macOS (darwin kernel)
1207 macos_release = mac_ver()[0]
1208 if macos_release:
Victor Stinnerea0ca212018-12-05 22:41:52 +01001209 system = 'macOS'
1210 release = macos_release
1211
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001212 if system == 'Windows':
1213 # MS platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001214 rel, vers, csd, ptype = win32_ver(version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001215 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001216 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001217 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001218 platform = _platform(system, release, version, csd)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001219
1220 elif system in ('Linux',):
Petr Viktorin8b94b412018-05-16 11:51:18 -04001221 # check for libc vs. glibc
Victor Stinnera719c8f2019-06-27 09:04:28 +02001222 libcname, libcversion = libc_ver()
Petr Viktorin8b94b412018-05-16 11:51:18 -04001223 platform = _platform(system, release, machine, processor,
1224 'with',
1225 libcname+libcversion)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001226 elif system == 'Java':
1227 # Java platforms
Victor Stinnerced39362013-12-09 00:14:52 +01001228 r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001229 if terse or not os_name:
Victor Stinnerced39362013-12-09 00:14:52 +01001230 platform = _platform(system, release, version)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001231 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001232 platform = _platform(system, release, version,
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001233 'on',
Victor Stinnerced39362013-12-09 00:14:52 +01001234 os_name, os_version, os_arch)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001235
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001236 else:
1237 # Generic handler
1238 if terse:
Victor Stinnerced39362013-12-09 00:14:52 +01001239 platform = _platform(system, release)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001240 else:
Victor Stinnerced39362013-12-09 00:14:52 +01001241 bits, linkage = architecture(sys.executable)
1242 platform = _platform(system, release, machine,
1243 processor, bits, linkage)
Tim Peters0eadaac2003-04-24 16:02:54 +00001244
Marc-André Lemburg91e83e22004-03-25 18:35:12 +00001245 _platform_cache[(aliased, terse)] = platform
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001246 return platform
1247
Christian Heimes5c73afc2020-11-30 22:34:45 +01001248### freedesktop.org os-release standard
1249# https://www.freedesktop.org/software/systemd/man/os-release.html
1250
1251# NAME=value with optional quotes (' or "). The regular expression is less
1252# strict than shell lexer, but that's ok.
1253_os_release_line = re.compile(
1254 "^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
1255)
1256# unescape five special characters mentioned in the standard
1257_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
1258# /etc takes precedence over /usr/lib
Victor Stinnerbfda4f52020-12-23 17:35:53 +01001259_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
Christian Heimes5c73afc2020-11-30 22:34:45 +01001260_os_release_cache = None
1261
1262
1263def _parse_os_release(lines):
1264 # These fields are mandatory fields with well-known defaults
Christian Clausscfca4a62021-10-07 17:49:47 +02001265 # in practice all Linux distributions override NAME, ID, and PRETTY_NAME.
Christian Heimes5c73afc2020-11-30 22:34:45 +01001266 info = {
1267 "NAME": "Linux",
1268 "ID": "linux",
1269 "PRETTY_NAME": "Linux",
1270 }
1271
1272 for line in lines:
1273 mo = _os_release_line.match(line)
1274 if mo is not None:
1275 info[mo.group('name')] = _os_release_unescape.sub(
1276 r"\1", mo.group('value')
1277 )
1278
1279 return info
1280
1281
1282def freedesktop_os_release():
1283 """Return operation system identification from freedesktop.org os-release
1284 """
1285 global _os_release_cache
1286
1287 if _os_release_cache is None:
1288 errno = None
1289 for candidate in _os_release_candidates:
1290 try:
1291 with open(candidate, encoding="utf-8") as f:
1292 _os_release_cache = _parse_os_release(f)
1293 break
1294 except OSError as e:
1295 errno = e.errno
1296 else:
1297 raise OSError(
1298 errno,
1299 f"Unable to read files {', '.join(_os_release_candidates)}"
1300 )
1301
1302 return _os_release_cache.copy()
1303
1304
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001305### Command line interface
1306
1307if __name__ == '__main__':
1308 # Default is to print the aliased verbose platform string
Tim Peters0eadaac2003-04-24 16:02:54 +00001309 terse = ('terse' in sys.argv or '--terse' in sys.argv)
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001310 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
Victor Stinnerced39362013-12-09 00:14:52 +01001311 print(platform(aliased, terse))
Marc-André Lemburg246d8472003-04-24 11:36:11 +00001312 sys.exit(0)