Coverage for /pythoncovmergedfiles/medio/medio/usr/lib/python3.9/platform.py: 2%

521 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1#! /usr/bin/python3.9 

2 

3""" This module tries to retrieve as much platform-identifying data as 

4 possible. It makes this information available via function APIs. 

5 

6 If called from the command line, it prints the platform 

7 information concatenated as single string to stdout. The output 

8 format is useable as part of a filename. 

9 

10""" 

11# This module is maintained by Marc-Andre Lemburg <mal@egenix.com>. 

12# If you find problems, please submit bug reports/patches via the 

13# Python bug tracker (http://bugs.python.org) and assign them to "lemburg". 

14# 

15# Still needed: 

16# * support for MS-DOS (PythonDX ?) 

17# * support for Amiga and other still unsupported platforms running Python 

18# * support for additional Linux distributions 

19# 

20# Many thanks to all those who helped adding platform-specific 

21# checks (in no particular order): 

22# 

23# Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell, 

24# Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef 

25# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg 

26# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark 

27# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support), 

28# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve 

29# Dower 

30# 

31# History: 

32# 

33# <see CVS and SVN checkin messages for history> 

34# 

35# 1.0.8 - changed Windows support to read version from kernel32.dll 

36# 1.0.7 - added DEV_NULL 

37# 1.0.6 - added linux_distribution() 

38# 1.0.5 - fixed Java support to allow running the module on Jython 

39# 1.0.4 - added IronPython support 

40# 1.0.3 - added normalization of Windows system name 

41# 1.0.2 - added more Windows support 

42# 1.0.1 - reformatted to make doc.py happy 

43# 1.0.0 - reformatted a bit and checked into Python CVS 

44# 0.8.0 - added sys.version parser and various new access 

45# APIs (python_version(), python_compiler(), etc.) 

46# 0.7.2 - fixed architecture() to use sizeof(pointer) where available 

47# 0.7.1 - added support for Caldera OpenLinux 

48# 0.7.0 - some fixes for WinCE; untabified the source file 

49# 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and 

50# vms_lib.getsyi() configured 

51# 0.6.1 - added code to prevent 'uname -p' on platforms which are 

52# known not to support it 

53# 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k; 

54# did some cleanup of the interfaces - some APIs have changed 

55# 0.5.5 - fixed another type in the MacOS code... should have 

56# used more coffee today ;-) 

57# 0.5.4 - fixed a few typos in the MacOS code 

58# 0.5.3 - added experimental MacOS support; added better popen() 

59# workarounds in _syscmd_ver() -- still not 100% elegant 

60# though 

61# 0.5.2 - fixed uname() to return '' instead of 'unknown' in all 

62# return values (the system uname command tends to return 

63# 'unknown' instead of just leaving the field empty) 

64# 0.5.1 - included code for slackware dist; added exception handlers 

65# to cover up situations where platforms don't have os.popen 

66# (e.g. Mac) or fail on socket.gethostname(); fixed libc 

67# detection RE 

68# 0.5.0 - changed the API names referring to system commands to *syscmd*; 

69# added java_ver(); made syscmd_ver() a private 

70# API (was system_ver() in previous versions) -- use uname() 

71# instead; extended the win32_ver() to also return processor 

72# type information 

73# 0.4.0 - added win32_ver() and modified the platform() output for WinXX 

74# 0.3.4 - fixed a bug in _follow_symlinks() 

75# 0.3.3 - fixed popen() and "file" command invocation bugs 

76# 0.3.2 - added architecture() API and support for it in platform() 

77# 0.3.1 - fixed syscmd_ver() RE to support Windows NT 

78# 0.3.0 - added system alias support 

79# 0.2.3 - removed 'wince' again... oh well. 

80# 0.2.2 - added 'wince' to syscmd_ver() supported platforms 

81# 0.2.1 - added cache logic and changed the platform string format 

82# 0.2.0 - changed the API to use functions instead of module globals 

83# since some action take too long to be run on module import 

84# 0.1.0 - first release 

85# 

86# You can always get the latest version of this module at: 

87# 

88# http://www.egenix.com/files/python/platform.py 

89# 

90# If that URL should fail, try contacting the author. 

91 

92__copyright__ = """ 

93 Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com 

94 Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com 

95 

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 

113__version__ = '1.0.8' 

114 

115import collections 

116import os 

117import re 

118import sys 

119import subprocess 

120import functools 

121import itertools 

122 

123### Globals & Constants 

124 

125# 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 

154### Platform specific APIs 

155 

156_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) 

161 

162def libc_ver(executable=None, lib='', version='', chunksize=16384): 

163 

164 """ Tries to determine the libc version that the file executable 

165 (which defaults to the Python interpreter) is linked against. 

166 

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 

171 libc versions add symbols to the executable and thus is probably 

172 only useable for executables compiled using gcc. 

173 

174 The file is read and scanned in chunks of chunksize bytes. 

175 

176 """ 

177 if executable is None: 

178 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 

190 V = _comparable_version 

191 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) 

196 with open(executable, 'rb') as f: 

197 binary = f.read(chunksize) 

198 pos = 0 

199 while pos < len(binary): 

200 if b'libc' in binary or b'GLIBC' in binary: 

201 m = _libc_search.search(binary, pos) 

202 else: 

203 m = None 

204 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: 

211 break 

212 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: 

216 lib = 'libc' 

217 elif glibc: 

218 if lib != 'glibc': 

219 lib = 'glibc' 

220 version = glibcversion 

221 elif V(glibcversion) > V(version): 

222 version = glibcversion 

223 elif so: 

224 if lib != 'glibc': 

225 lib = 'libc' 

226 if soversion and (not version or V(soversion) > V(version)): 

227 version = soversion 

228 if threads and version[-len(threads):] != threads: 

229 version = version + threads 

230 pos = m.end() 

231 return lib, version 

232 

233def _norm_version(version, build=''): 

234 

235 """ Normalize the version and build strings and return a single 

236 version string using the format major.minor.build (or patchlevel). 

237 """ 

238 l = version.split('.') 

239 if build: 

240 l.append(build) 

241 try: 

242 strings = list(map(str, map(int, l))) 

243 except ValueError: 

244 strings = l 

245 version = '.'.join(strings[:3]) 

246 return version 

247 

248_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' 

249 r'.*' 

250 r'\[.* ([\d.]+)\])') 

251 

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. 

260 

261def _syscmd_ver(system='', release='', version='', 

262 

263 supported_platforms=('win32', 'win16', 'dos')): 

264 

265 """ Tries to figure out the OS version used and returns 

266 a tuple (system, release, version). 

267 

268 It uses the "ver" shell command for this which is known 

269 to exists on Windows, DOS. XXX Others too ? 

270 

271 In case this fails, the given parameters are used as 

272 defaults. 

273 

274 """ 

275 if sys.platform not in supported_platforms: 

276 return system, release, version 

277 

278 # Try some common cmd strings 

279 import subprocess 

280 for cmd in ('ver', 'command /c ver', 'cmd /c ver'): 

281 try: 

282 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)) 

288 continue 

289 else: 

290 break 

291 else: 

292 return system, release, version 

293 

294 # Parse the output 

295 info = info.strip() 

296 m = _ver_output.match(info) 

297 if m is not None: 

298 system, release, version = m.groups() 

299 # 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) 

307 return system, release, version 

308 

309_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", 

316 

317 (6, 0): "Vista", 

318 (6, 1): "7", 

319 (6, 2): "8", 

320 (6, 3): "8.1", 

321 (6, None): "post8.1", 

322 

323 (10, 0): "10", 

324 (10, None): "post10", 

325} 

326 

327# 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 

338def 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 

359def win32_ver(release='', version='', csd='', ptype=''): 

360 try: 

361 from sys import getwindowsversion 

362 except ImportError: 

363 return release, version, csd, ptype 

364 

365 winver = getwindowsversion() 

366 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) 

371 

372 release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or 

373 _WIN32_CLIENT_RELEASES.get((major, None)) or 

374 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. 

379 if winver[:2] == (major, minor): 

380 try: 

381 csd = 'SP{}'.format(winver.service_pack_major) 

382 except AttributeError: 

383 if csd[:13] == 'Service Pack ': 

384 csd = 'SP' + csd[13:] 

385 

386 # VER_NT_SERVER = 3 

387 if getattr(winver, 'product_type', None) == 3: 

388 release = (_WIN32_SERVER_RELEASES.get((major, minor)) or 

389 _WIN32_SERVER_RELEASES.get((major, None)) or 

390 release) 

391 

392 try: 

393 try: 

394 import winreg 

395 except ImportError: 

396 import _winreg as winreg 

397 except ImportError: 

398 pass 

399 else: 

400 try: 

401 cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion' 

402 with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key: 

403 ptype = winreg.QueryValueEx(key, 'CurrentType')[0] 

404 except OSError: 

405 pass 

406 

407 return release, version, csd, ptype 

408 

409 

410def _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 

417 except ImportError: 

418 return None 

419 

420 with open(fn, 'rb') as f: 

421 pl = plistlib.load(f) 

422 release = pl['ProductVersion'] 

423 versioninfo = ('', '', '') 

424 machine = os.uname().machine 

425 if machine in ('ppc', 'Power Macintosh'): 

426 # Canonical name 

427 machine = 'PowerPC' 

428 

429 return release, versioninfo, machine 

430 

431 

432def mac_ver(release='', versioninfo=('', '', ''), machine=''): 

433 

434 """ Get macOS version information and return it as tuple (release, 

435 versioninfo, machine) with versioninfo being a tuple (version, 

436 dev_stage, non_release_version). 

437 

438 Entries which cannot be determined are set to the parameter values 

439 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 

448 # If that also doesn't work return the default values 

449 return release, versioninfo, machine 

450 

451def _java_getprop(name, default): 

452 

453 from java.lang import System 

454 try: 

455 value = System.getProperty(name) 

456 if value is None: 

457 return default 

458 return value 

459 except AttributeError: 

460 return default 

461 

462def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')): 

463 

464 """ Version interface for Jython. 

465 

466 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). 

469 

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 

477 except ImportError: 

478 return release, vendor, vminfo, osinfo 

479 

480 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 

492 

493 return release, vendor, vminfo, osinfo 

494 

495### System name aliasing 

496 

497def system_alias(system, release, version): 

498 

499 """ Returns (system, release, version) aliased to common 

500 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 """ 

506 if system == 'SunOS': 

507 # Sun's OS 

508 if release < '5': 

509 # These releases use the old name SunOS 

510 return system, release, version 

511 # Modify release (marketing release = SunOS release - 3) 

512 l = release.split('.') 

513 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) 

521 release = '.'.join(l) 

522 if release < '6': 

523 system = 'Solaris' 

524 else: 

525 # XXX Whatever the new SunOS marketing name is... 

526 system = 'Solaris' 

527 

528 elif system == 'IRIX64': 

529 # IRIX reports IRIX64 on platforms with 64-bit support; yet it 

530 # is really a version and not a different platform, since 32-bit 

531 # apps are also supported.. 

532 system = 'IRIX' 

533 if version: 

534 version = version + ' (64bit)' 

535 else: 

536 version = '64bit' 

537 

538 elif system in ('win32', 'win16'): 

539 # In case one of the other tricks 

540 system = 'Windows' 

541 

542 # bpo-35516: Don't replace Darwin with macOS since input release and 

543 # version arguments can be different than the currently running version. 

544 

545 return system, release, version 

546 

547### Various internal helpers 

548 

549def _platform(*args): 

550 

551 """ Helper to format the platform string in a filename 

552 compatible format e.g. "system-version-machine". 

553 """ 

554 # Format the platform string 

555 platform = '-'.join(x.strip() for x in filter(len, args)) 

556 

557 # Cleanup some possible filename obstacles... 

558 platform = platform.replace(' ', '_') 

559 platform = platform.replace('/', '-') 

560 platform = platform.replace('\\', '-') 

561 platform = platform.replace(':', '-') 

562 platform = platform.replace(';', '-') 

563 platform = platform.replace('"', '-') 

564 platform = platform.replace('(', '-') 

565 platform = platform.replace(')', '-') 

566 

567 # No need to report 'unknown' information... 

568 platform = platform.replace('unknown', '') 

569 

570 # Fold '--'s and remove trailing '-' 

571 while 1: 

572 cleaned = platform.replace('--', '-') 

573 if cleaned == platform: 

574 break 

575 platform = cleaned 

576 while platform[-1] == '-': 

577 platform = platform[:-1] 

578 

579 return platform 

580 

581def _node(default=''): 

582 

583 """ Helper to determine the node name of this machine. 

584 """ 

585 try: 

586 import socket 

587 except ImportError: 

588 # No sockets... 

589 return default 

590 try: 

591 return socket.gethostname() 

592 except OSError: 

593 # Still not working... 

594 return default 

595 

596def _follow_symlinks(filepath): 

597 

598 """ In case filepath is a symlink, follow it until a 

599 real file is reached. 

600 """ 

601 filepath = os.path.abspath(filepath) 

602 while os.path.islink(filepath): 

603 filepath = os.path.normpath( 

604 os.path.join(os.path.dirname(filepath), os.readlink(filepath))) 

605 return filepath 

606 

607 

608def _syscmd_file(target, default=''): 

609 

610 """ Interface to the system's file command. 

611 

612 The function uses the -b option of the file command to have it 

613 omit the filename in its output. Follow the symlinks. It returns 

614 default in case the command should fail. 

615 

616 """ 

617 if sys.platform in ('dos', 'win32', 'win16'): 

618 # XXX Others too ? 

619 return default 

620 

621 import subprocess 

622 target = _follow_symlinks(target) 

623 # "file" output is locale dependent: force the usage of the C locale 

624 # to get deterministic behavior. 

625 env = dict(os.environ, LC_ALL='C') 

626 try: 

627 # -b: do not prepend filenames to output lines (brief mode) 

628 output = subprocess.check_output(['file', '-b', target], 

629 stderr=subprocess.DEVNULL, 

630 env=env) 

631 except (OSError, subprocess.CalledProcessError): 

632 return default 

633 if not output: 

634 return default 

635 # With the C locale, the output should be mostly ASCII-compatible. 

636 # Decode from Latin-1 to prevent Unicode decode error. 

637 return output.decode('latin-1') 

638 

639### Information about the used architecture 

640 

641# Default values for architecture; non-empty strings override the 

642# defaults given as parameters 

643_default_architecture = { 

644 'win32': ('', 'WindowsPE'), 

645 'win16': ('', 'Windows'), 

646 'dos': ('', 'MSDOS'), 

647} 

648 

649def architecture(executable=sys.executable, bits='', linkage=''): 

650 

651 """ Queries the given executable (defaults to the Python interpreter 

652 binary) for various architecture information. 

653 

654 Returns a tuple (bits, linkage) which contains information about 

655 the bit architecture and the linkage format used for the 

656 executable. Both values are returned as strings. 

657 

658 Values that cannot be determined are returned as given by the 

659 parameter presets. If bits is given as '', the sizeof(pointer) 

660 (or sizeof(long) on Python version < 1.5.2) is used as 

661 indicator for the supported pointer size. 

662 

663 The function relies on the system's "file" command to do the 

664 actual work. This is available on most if not all Unix 

665 platforms. On some non-Unix platforms where the "file" command 

666 does not exist and the executable is set to the Python interpreter 

667 binary defaults from _default_architecture are used. 

668 

669 """ 

670 # Use the sizeof(pointer) as default number of bits if nothing 

671 # else is given as default. 

672 if not bits: 

673 import struct 

674 size = struct.calcsize('P') 

675 bits = str(size * 8) + 'bit' 

676 

677 # Get data from the 'file' system command 

678 if executable: 

679 fileout = _syscmd_file(executable, '') 

680 else: 

681 fileout = '' 

682 

683 if not fileout and \ 

684 executable == sys.executable: 

685 # "file" command did not return anything; we'll try to provide 

686 # some sensible defaults then... 

687 if sys.platform in _default_architecture: 

688 b, l = _default_architecture[sys.platform] 

689 if b: 

690 bits = b 

691 if l: 

692 linkage = l 

693 return bits, linkage 

694 

695 if 'executable' not in fileout and 'shared object' not in fileout: 

696 # Format not supported 

697 return bits, linkage 

698 

699 # Bits 

700 if '32-bit' in fileout: 

701 bits = '32bit' 

702 elif 'N32' in fileout: 

703 # On Irix only 

704 bits = 'n32bit' 

705 elif '64-bit' in fileout: 

706 bits = '64bit' 

707 

708 # Linkage 

709 if 'ELF' in fileout: 

710 linkage = 'ELF' 

711 elif 'PE' in fileout: 

712 # E.g. Windows uses this format 

713 if 'Windows' in fileout: 

714 linkage = 'WindowsPE' 

715 else: 

716 linkage = 'PE' 

717 elif 'COFF' in fileout: 

718 linkage = 'COFF' 

719 elif 'MS-DOS' in fileout: 

720 linkage = 'MSDOS' 

721 else: 

722 # XXX the A.OUT format also falls under this class... 

723 pass 

724 

725 return bits, linkage 

726 

727 

728def _get_machine_win32(): 

729 # Try to use the PROCESSOR_* environment variables 

730 # available on Win XP and later; see 

731 # http://support.microsoft.com/kb/888731 and 

732 # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM 

733 

734 # WOW64 processes mask the native architecture 

735 return ( 

736 os.environ.get('PROCESSOR_ARCHITEW6432', '') or 

737 os.environ.get('PROCESSOR_ARCHITECTURE', '') 

738 ) 

739 

740 

741class _Processor: 

742 @classmethod 

743 def get(cls): 

744 func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess) 

745 return func() or '' 

746 

747 def get_win32(): 

748 return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32()) 

749 

750 def get_OpenVMS(): 

751 try: 

752 import vms_lib 

753 except ImportError: 

754 pass 

755 else: 

756 csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) 

757 return 'Alpha' if cpu_number >= 128 else 'VAX' 

758 

759 def from_subprocess(): 

760 """ 

761 Fall back to `uname -p` 

762 """ 

763 try: 

764 return subprocess.check_output( 

765 ['uname', '-p'], 

766 stderr=subprocess.DEVNULL, 

767 text=True, 

768 ).strip() 

769 except (OSError, subprocess.CalledProcessError): 

770 pass 

771 

772 

773def _unknown_as_blank(val): 

774 return '' if val == 'unknown' else val 

775 

776 

777### Portable uname() interface 

778 

779class uname_result( 

780 collections.namedtuple( 

781 "uname_result_base", 

782 "system node release version machine") 

783 ): 

784 """ 

785 A uname_result that's largely compatible with a 

786 simple namedtuple except that 'processor' is 

787 resolved late and cached to avoid calling "uname" 

788 except when needed. 

789 """ 

790 

791 @functools.cached_property 

792 def processor(self): 

793 return _unknown_as_blank(_Processor.get()) 

794 

795 def __iter__(self): 

796 return itertools.chain( 

797 super().__iter__(), 

798 (self.processor,) 

799 ) 

800 

801 @classmethod 

802 def _make(cls, iterable): 

803 # override factory to affect length check 

804 num_fields = len(cls._fields) 

805 result = cls.__new__(cls, *iterable) 

806 if len(result) != num_fields + 1: 

807 msg = f'Expected {num_fields} arguments, got {len(result)}' 

808 raise TypeError(msg) 

809 return result 

810 

811 def __getitem__(self, key): 

812 return tuple(self)[key] 

813 

814 def __len__(self): 

815 return len(tuple(iter(self))) 

816 

817 def __reduce__(self): 

818 return uname_result, tuple(self)[:len(self._fields)] 

819 

820 

821_uname_cache = None 

822 

823 

824def uname(): 

825 

826 """ Fairly portable uname interface. Returns a tuple 

827 of strings (system, node, release, version, machine, processor) 

828 identifying the underlying platform. 

829 

830 Note that unlike the os.uname function this also returns 

831 possible processor information as an additional tuple entry. 

832 

833 Entries which cannot be determined are set to ''. 

834 

835 """ 

836 global _uname_cache 

837 

838 if _uname_cache is not None: 

839 return _uname_cache 

840 

841 # Get some infos from the builtin os.uname API... 

842 try: 

843 system, node, release, version, machine = infos = os.uname() 

844 except AttributeError: 

845 system = sys.platform 

846 node = _node() 

847 release = version = machine = '' 

848 infos = () 

849 

850 if not any(infos): 

851 # uname is not available 

852 

853 # Try win32_ver() on win32 platforms 

854 if system == 'win32': 

855 release, version, csd, ptype = win32_ver() 

856 machine = machine or _get_machine_win32() 

857 

858 # Try the 'ver' system command available on some 

859 # platforms 

860 if not (release and version): 

861 system, release, version = _syscmd_ver(system) 

862 # Normalize system to what win32_ver() normally returns 

863 # (_syscmd_ver() tends to return the vendor name as well) 

864 if system == 'Microsoft Windows': 

865 system = 'Windows' 

866 elif system == 'Microsoft' and release == 'Windows': 

867 # Under Windows Vista and Windows Server 2008, 

868 # Microsoft changed the output of the ver command. The 

869 # release is no longer printed. This causes the 

870 # system and release to be misidentified. 

871 system = 'Windows' 

872 if '6.0' == version[:3]: 

873 release = 'Vista' 

874 else: 

875 release = '' 

876 

877 # In case we still don't know anything useful, we'll try to 

878 # help ourselves 

879 if system in ('win32', 'win16'): 

880 if not version: 

881 if system == 'win32': 

882 version = '32bit' 

883 else: 

884 version = '16bit' 

885 system = 'Windows' 

886 

887 elif system[:4] == 'java': 

888 release, vendor, vminfo, osinfo = java_ver() 

889 system = 'Java' 

890 version = ', '.join(vminfo) 

891 if not version: 

892 version = vendor 

893 

894 # System specific extensions 

895 if system == 'OpenVMS': 

896 # OpenVMS seems to have release and version mixed up 

897 if not release or release == '0': 

898 release = version 

899 version = '' 

900 

901 # normalize name 

902 if system == 'Microsoft' and release == 'Windows': 

903 system = 'Windows' 

904 release = 'Vista' 

905 

906 vals = system, node, release, version, machine 

907 # Replace 'unknown' values with the more portable '' 

908 _uname_cache = uname_result(*map(_unknown_as_blank, vals)) 

909 return _uname_cache 

910 

911### Direct interfaces to some of the uname() return values 

912 

913def system(): 

914 

915 """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'. 

916 

917 An empty string is returned if the value cannot be determined. 

918 

919 """ 

920 return uname().system 

921 

922def node(): 

923 

924 """ Returns the computer's network name (which may not be fully 

925 qualified) 

926 

927 An empty string is returned if the value cannot be determined. 

928 

929 """ 

930 return uname().node 

931 

932def release(): 

933 

934 """ Returns the system's release, e.g. '2.2.0' or 'NT' 

935 

936 An empty string is returned if the value cannot be determined. 

937 

938 """ 

939 return uname().release 

940 

941def version(): 

942 

943 """ Returns the system's release version, e.g. '#3 on degas' 

944 

945 An empty string is returned if the value cannot be determined. 

946 

947 """ 

948 return uname().version 

949 

950def machine(): 

951 

952 """ Returns the machine type, e.g. 'i386' 

953 

954 An empty string is returned if the value cannot be determined. 

955 

956 """ 

957 return uname().machine 

958 

959def processor(): 

960 

961 """ Returns the (true) processor name, e.g. 'amdk6' 

962 

963 An empty string is returned if the value cannot be 

964 determined. Note that many platforms do not provide this 

965 information or simply return the same value as for machine(), 

966 e.g. NetBSD does this. 

967 

968 """ 

969 return uname().processor 

970 

971### Various APIs for extracting information from sys.version 

972 

973_sys_version_parser = re.compile( 

974 r'([\w.+]+)\s*' # "version<space>" 

975 r'\(#?([^,]+)' # "(#buildno" 

976 r'(?:,\s*([\w ]*)' # ", builddate" 

977 r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>" 

978 r'\[([^\]]+)\]?', re.ASCII) # "[compiler]" 

979 

980_ironpython_sys_version_parser = re.compile( 

981 r'IronPython\s*' 

982 r'([\d\.]+)' 

983 r'(?: \(([\d\.]+)\))?' 

984 r' on (.NET [\d\.]+)', re.ASCII) 

985 

986# IronPython covering 2.6 and 2.7 

987_ironpython26_sys_version_parser = re.compile( 

988 r'([\d.]+)\s*' 

989 r'\(IronPython\s*' 

990 r'[\d.]+\s*' 

991 r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)' 

992) 

993 

994_pypy_sys_version_parser = re.compile( 

995 r'([\w.+]+)\s*' 

996 r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' 

997 r'\[PyPy [^\]]+\]?') 

998 

999_sys_version_cache = {} 

1000 

1001def _sys_version(sys_version=None): 

1002 

1003 """ Returns a parsed version of Python's sys.version as tuple 

1004 (name, version, branch, revision, buildno, builddate, compiler) 

1005 referring to the Python implementation name, version, branch, 

1006 revision, build number, build date/time as string and the compiler 

1007 identification string. 

1008 

1009 Note that unlike the Python sys.version, the returned value 

1010 for the Python version will always include the patchlevel (it 

1011 defaults to '.0'). 

1012 

1013 The function returns empty strings for tuple entries that 

1014 cannot be determined. 

1015 

1016 sys_version may be given to parse an alternative version 

1017 string, e.g. if the version was read from a different Python 

1018 interpreter. 

1019 

1020 """ 

1021 # Get the Python version 

1022 if sys_version is None: 

1023 sys_version = sys.version 

1024 

1025 # Try the cache first 

1026 result = _sys_version_cache.get(sys_version, None) 

1027 if result is not None: 

1028 return result 

1029 

1030 # Parse it 

1031 if 'IronPython' in sys_version: 

1032 # IronPython 

1033 name = 'IronPython' 

1034 if sys_version.startswith('IronPython'): 

1035 match = _ironpython_sys_version_parser.match(sys_version) 

1036 else: 

1037 match = _ironpython26_sys_version_parser.match(sys_version) 

1038 

1039 if match is None: 

1040 raise ValueError( 

1041 'failed to parse IronPython sys.version: %s' % 

1042 repr(sys_version)) 

1043 

1044 version, alt_version, compiler = match.groups() 

1045 buildno = '' 

1046 builddate = '' 

1047 

1048 elif sys.platform.startswith('java'): 

1049 # Jython 

1050 name = 'Jython' 

1051 match = _sys_version_parser.match(sys_version) 

1052 if match is None: 

1053 raise ValueError( 

1054 'failed to parse Jython sys.version: %s' % 

1055 repr(sys_version)) 

1056 version, buildno, builddate, buildtime, _ = match.groups() 

1057 if builddate is None: 

1058 builddate = '' 

1059 compiler = sys.platform 

1060 

1061 elif "PyPy" in sys_version: 

1062 # PyPy 

1063 name = "PyPy" 

1064 match = _pypy_sys_version_parser.match(sys_version) 

1065 if match is None: 

1066 raise ValueError("failed to parse PyPy sys.version: %s" % 

1067 repr(sys_version)) 

1068 version, buildno, builddate, buildtime = match.groups() 

1069 compiler = "" 

1070 

1071 else: 

1072 # CPython 

1073 match = _sys_version_parser.match(sys_version) 

1074 if match is None: 

1075 raise ValueError( 

1076 'failed to parse CPython sys.version: %s' % 

1077 repr(sys_version)) 

1078 version, buildno, builddate, buildtime, compiler = \ 

1079 match.groups() 

1080 name = 'CPython' 

1081 if builddate is None: 

1082 builddate = '' 

1083 elif buildtime: 

1084 builddate = builddate + ' ' + buildtime 

1085 

1086 if hasattr(sys, '_git'): 

1087 _, branch, revision = sys._git 

1088 elif hasattr(sys, '_mercurial'): 

1089 _, branch, revision = sys._mercurial 

1090 else: 

1091 branch = '' 

1092 revision = '' 

1093 

1094 # Add the patchlevel version if missing 

1095 l = version.split('.') 

1096 if len(l) == 2: 

1097 l.append('0') 

1098 version = '.'.join(l) 

1099 

1100 # Build and cache the result 

1101 result = (name, version, branch, revision, buildno, builddate, compiler) 

1102 _sys_version_cache[sys_version] = result 

1103 return result 

1104 

1105def python_implementation(): 

1106 

1107 """ Returns a string identifying the Python implementation. 

1108 

1109 Currently, the following implementations are identified: 

1110 'CPython' (C implementation of Python), 

1111 'IronPython' (.NET implementation of Python), 

1112 'Jython' (Java implementation of Python), 

1113 'PyPy' (Python implementation of Python). 

1114 

1115 """ 

1116 return _sys_version()[0] 

1117 

1118def python_version(): 

1119 

1120 """ Returns the Python version as string 'major.minor.patchlevel' 

1121 

1122 Note that unlike the Python sys.version, the returned value 

1123 will always include the patchlevel (it defaults to 0). 

1124 

1125 """ 

1126 return _sys_version()[1] 

1127 

1128def python_version_tuple(): 

1129 

1130 """ Returns the Python version as tuple (major, minor, patchlevel) 

1131 of strings. 

1132 

1133 Note that unlike the Python sys.version, the returned value 

1134 will always include the patchlevel (it defaults to 0). 

1135 

1136 """ 

1137 return tuple(_sys_version()[1].split('.')) 

1138 

1139def python_branch(): 

1140 

1141 """ Returns a string identifying the Python implementation 

1142 branch. 

1143 

1144 For CPython this is the SCM branch from which the 

1145 Python binary was built. 

1146 

1147 If not available, an empty string is returned. 

1148 

1149 """ 

1150 

1151 return _sys_version()[2] 

1152 

1153def python_revision(): 

1154 

1155 """ Returns a string identifying the Python implementation 

1156 revision. 

1157 

1158 For CPython this is the SCM revision from which the 

1159 Python binary was built. 

1160 

1161 If not available, an empty string is returned. 

1162 

1163 """ 

1164 return _sys_version()[3] 

1165 

1166def python_build(): 

1167 

1168 """ Returns a tuple (buildno, builddate) stating the Python 

1169 build number and date as strings. 

1170 

1171 """ 

1172 return _sys_version()[4:6] 

1173 

1174def python_compiler(): 

1175 

1176 """ Returns a string identifying the compiler used for compiling 

1177 Python. 

1178 

1179 """ 

1180 return _sys_version()[6] 

1181 

1182### The Opus Magnum of platform strings :-) 

1183 

1184_platform_cache = {} 

1185 

1186def platform(aliased=0, terse=0): 

1187 

1188 """ Returns a single string identifying the underlying platform 

1189 with as much useful information as possible (but no more :). 

1190 

1191 The output is intended to be human readable rather than 

1192 machine parseable. It may look different on different 

1193 platforms and this is intended. 

1194 

1195 If "aliased" is true, the function will use aliases for 

1196 various platforms that report system names which differ from 

1197 their common names, e.g. SunOS will be reported as 

1198 Solaris. The system_alias() function is used to implement 

1199 this. 

1200 

1201 Setting terse to true causes the function to return only the 

1202 absolute minimum information needed to identify the platform. 

1203 

1204 """ 

1205 result = _platform_cache.get((aliased, terse), None) 

1206 if result is not None: 

1207 return result 

1208 

1209 # Get uname information and then apply platform specific cosmetics 

1210 # to it... 

1211 system, node, release, version, machine, processor = uname() 

1212 if machine == processor: 

1213 processor = '' 

1214 if aliased: 

1215 system, release, version = system_alias(system, release, version) 

1216 

1217 if system == 'Darwin': 

1218 # macOS (darwin kernel) 

1219 macos_release = mac_ver()[0] 

1220 if macos_release: 

1221 system = 'macOS' 

1222 release = macos_release 

1223 

1224 if system == 'Windows': 

1225 # MS platforms 

1226 rel, vers, csd, ptype = win32_ver(version) 

1227 if terse: 

1228 platform = _platform(system, release) 

1229 else: 

1230 platform = _platform(system, release, version, csd) 

1231 

1232 elif system in ('Linux',): 

1233 # check for libc vs. glibc 

1234 libcname, libcversion = libc_ver() 

1235 platform = _platform(system, release, machine, processor, 

1236 'with', 

1237 libcname+libcversion) 

1238 elif system == 'Java': 

1239 # Java platforms 

1240 r, v, vminfo, (os_name, os_version, os_arch) = java_ver() 

1241 if terse or not os_name: 

1242 platform = _platform(system, release, version) 

1243 else: 

1244 platform = _platform(system, release, version, 

1245 'on', 

1246 os_name, os_version, os_arch) 

1247 

1248 else: 

1249 # Generic handler 

1250 if terse: 

1251 platform = _platform(system, release) 

1252 else: 

1253 bits, linkage = architecture(sys.executable) 

1254 platform = _platform(system, release, machine, 

1255 processor, bits, linkage) 

1256 

1257 _platform_cache[(aliased, terse)] = platform 

1258 return platform 

1259 

1260### Command line interface 

1261 

1262if __name__ == '__main__': 

1263 # Default is to print the aliased verbose platform string 

1264 terse = ('terse' in sys.argv or '--terse' in sys.argv) 

1265 aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv) 

1266 print(platform(aliased, terse)) 

1267 sys.exit(0)