Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/platform.py: 11%

517 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1#!/usr/bin/env python3 

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 

119 

120### Globals & Constants 

121 

122# Helper for comparing two version number strings. 

123# Based on the description of the PHP's version_compare(): 

124# http://php.net/manual/en/function.version-compare.php 

125 

126_ver_stages = { 

127 # any string not found in this dict, will get 0 assigned 

128 'dev': 10, 

129 'alpha': 20, 'a': 20, 

130 'beta': 30, 'b': 30, 

131 'c': 40, 

132 'RC': 50, 'rc': 50, 

133 # number, will get 100 assigned 

134 'pl': 200, 'p': 200, 

135} 

136 

137_component_re = re.compile(r'([0-9]+|[._+-])') 

138 

139def _comparable_version(version): 

140 result = [] 

141 for v in _component_re.split(version): 

142 if v not in '._+-': 

143 try: 

144 v = int(v, 10) 

145 t = 100 

146 except ValueError: 

147 t = _ver_stages.get(v, 0) 

148 result.extend((t, v)) 

149 return result 

150 

151### Platform specific APIs 

152 

153_libc_search = re.compile(b'(__libc_init)' 

154 b'|' 

155 b'(GLIBC_([0-9.]+))' 

156 b'|' 

157 br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII) 

158 

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

160 

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

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

163 

164 Returns a tuple of strings (lib,version) which default to the 

165 given parameters in case the lookup fails. 

166 

167 Note that the function has intimate knowledge of how different 

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

169 only useable for executables compiled using gcc. 

170 

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

172 

173 """ 

174 if executable is None: 

175 try: 

176 ver = os.confstr('CS_GNU_LIBC_VERSION') 

177 # parse 'glibc 2.28' as ('glibc', '2.28') 

178 parts = ver.split(maxsplit=1) 

179 if len(parts) == 2: 

180 return tuple(parts) 

181 except (AttributeError, ValueError, OSError): 

182 # os.confstr() or CS_GNU_LIBC_VERSION value not available 

183 pass 

184 

185 executable = sys.executable 

186 

187 V = _comparable_version 

188 if hasattr(os.path, 'realpath'): 

189 # Python 2.2 introduced os.path.realpath(); it is used 

190 # here to work around problems with Cygwin not being 

191 # able to open symlinks for reading 

192 executable = os.path.realpath(executable) 

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

194 binary = f.read(chunksize) 

195 pos = 0 

196 while pos < len(binary): 

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

198 m = _libc_search.search(binary, pos) 

199 else: 

200 m = None 

201 if not m or m.end() == len(binary): 

202 chunk = f.read(chunksize) 

203 if chunk: 

204 binary = binary[max(pos, len(binary) - 1000):] + chunk 

205 pos = 0 

206 continue 

207 if not m: 

208 break 

209 libcinit, glibc, glibcversion, so, threads, soversion = [ 

210 s.decode('latin1') if s is not None else s 

211 for s in m.groups()] 

212 if libcinit and not lib: 

213 lib = 'libc' 

214 elif glibc: 

215 if lib != 'glibc': 

216 lib = 'glibc' 

217 version = glibcversion 

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

219 version = glibcversion 

220 elif so: 

221 if lib != 'glibc': 

222 lib = 'libc' 

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

224 version = soversion 

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

226 version = version + threads 

227 pos = m.end() 

228 return lib, version 

229 

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

231 

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

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

234 """ 

235 l = version.split('.') 

236 if build: 

237 l.append(build) 

238 try: 

239 ints = map(int, l) 

240 except ValueError: 

241 strings = l 

242 else: 

243 strings = list(map(str, ints)) 

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

245 return version 

246 

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

248 r'.*' 

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

250 

251# Examples of VER command output: 

252# 

253# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195] 

254# Windows XP: Microsoft Windows XP [Version 5.1.2600] 

255# Windows Vista: Microsoft Windows [Version 6.0.6002] 

256# 

257# Note that the "Version" string gets localized on different 

258# Windows versions. 

259 

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

261 

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

263 

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

265 a tuple (system, release, version). 

266 

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

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

269 

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

271 defaults. 

272 

273 """ 

274 if sys.platform not in supported_platforms: 

275 return system, release, version 

276 

277 # Try some common cmd strings 

278 import subprocess 

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

280 try: 

281 info = subprocess.check_output(cmd, 

282 stderr=subprocess.DEVNULL, 

283 text=True, 

284 shell=True) 

285 except (OSError, subprocess.CalledProcessError) as why: 

286 #print('Command %s failed: %s' % (cmd, why)) 

287 continue 

288 else: 

289 break 

290 else: 

291 return system, release, version 

292 

293 # Parse the output 

294 info = info.strip() 

295 m = _ver_output.match(info) 

296 if m is not None: 

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

298 # Strip trailing dots from version and release 

299 if release[-1] == '.': 

300 release = release[:-1] 

301 if version[-1] == '.': 

302 version = version[:-1] 

303 # Normalize the version and build strings (eliminating additional 

304 # zeros) 

305 version = _norm_version(version) 

306 return system, release, version 

307 

308_WIN32_CLIENT_RELEASES = { 

309 (5, 0): "2000", 

310 (5, 1): "XP", 

311 # Strictly, 5.2 client is XP 64-bit, but platform.py historically 

312 # has always called it 2003 Server 

313 (5, 2): "2003Server", 

314 (5, None): "post2003", 

315 

316 (6, 0): "Vista", 

317 (6, 1): "7", 

318 (6, 2): "8", 

319 (6, 3): "8.1", 

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

321 

322 (10, 0): "10", 

323 (10, None): "post10", 

324} 

325 

326# Server release name lookup will default to client names if necessary 

327_WIN32_SERVER_RELEASES = { 

328 (5, 2): "2003Server", 

329 

330 (6, 0): "2008Server", 

331 (6, 1): "2008ServerR2", 

332 (6, 2): "2012Server", 

333 (6, 3): "2012ServerR2", 

334 (6, None): "post2012ServerR2", 

335} 

336 

337def win32_is_iot(): 

338 return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS') 

339 

340def win32_edition(): 

341 try: 

342 try: 

343 import winreg 

344 except ImportError: 

345 import _winreg as winreg 

346 except ImportError: 

347 pass 

348 else: 

349 try: 

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

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

352 return winreg.QueryValueEx(key, 'EditionId')[0] 

353 except OSError: 

354 pass 

355 

356 return None 

357 

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

359 try: 

360 from sys import getwindowsversion 

361 except ImportError: 

362 return release, version, csd, ptype 

363 

364 winver = getwindowsversion() 

365 maj, min, build = winver.platform_version or winver[:3] 

366 version = '{0}.{1}.{2}'.format(maj, min, build) 

367 

368 release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or 

369 _WIN32_CLIENT_RELEASES.get((maj, None)) or 

370 release) 

371 

372 # getwindowsversion() reflect the compatibility mode Python is 

373 # running under, and so the service pack value is only going to be 

374 # valid if the versions match. 

375 if winver[:2] == (maj, min): 

376 try: 

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

378 except AttributeError: 

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

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

381 

382 # VER_NT_SERVER = 3 

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

384 release = (_WIN32_SERVER_RELEASES.get((maj, min)) or 

385 _WIN32_SERVER_RELEASES.get((maj, None)) or 

386 release) 

387 

388 try: 

389 try: 

390 import winreg 

391 except ImportError: 

392 import _winreg as winreg 

393 except ImportError: 

394 pass 

395 else: 

396 try: 

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

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

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

400 except OSError: 

401 pass 

402 

403 return release, version, csd, ptype 

404 

405 

406def _mac_ver_xml(): 

407 fn = '/System/Library/CoreServices/SystemVersion.plist' 

408 if not os.path.exists(fn): 

409 return None 

410 

411 try: 

412 import plistlib 

413 except ImportError: 

414 return None 

415 

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

417 pl = plistlib.load(f) 

418 release = pl['ProductVersion'] 

419 versioninfo = ('', '', '') 

420 machine = os.uname().machine 

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

422 # Canonical name 

423 machine = 'PowerPC' 

424 

425 return release, versioninfo, machine 

426 

427 

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

429 

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

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

432 dev_stage, non_release_version). 

433 

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

435 which default to ''. All tuple entries are strings. 

436 """ 

437 

438 # First try reading the information from an XML file which should 

439 # always be present 

440 info = _mac_ver_xml() 

441 if info is not None: 

442 return info 

443 

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

445 return release, versioninfo, machine 

446 

447def _java_getprop(name, default): 

448 

449 from java.lang import System 

450 try: 

451 value = System.getProperty(name) 

452 if value is None: 

453 return default 

454 return value 

455 except AttributeError: 

456 return default 

457 

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

459 

460 """ Version interface for Jython. 

461 

462 Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being 

463 a tuple (vm_name, vm_release, vm_vendor) and osinfo being a 

464 tuple (os_name, os_version, os_arch). 

465 

466 Values which cannot be determined are set to the defaults 

467 given as parameters (which all default to ''). 

468 

469 """ 

470 # Import the needed APIs 

471 try: 

472 import java.lang 

473 except ImportError: 

474 return release, vendor, vminfo, osinfo 

475 

476 vendor = _java_getprop('java.vendor', vendor) 

477 release = _java_getprop('java.version', release) 

478 vm_name, vm_release, vm_vendor = vminfo 

479 vm_name = _java_getprop('java.vm.name', vm_name) 

480 vm_vendor = _java_getprop('java.vm.vendor', vm_vendor) 

481 vm_release = _java_getprop('java.vm.version', vm_release) 

482 vminfo = vm_name, vm_release, vm_vendor 

483 os_name, os_version, os_arch = osinfo 

484 os_arch = _java_getprop('java.os.arch', os_arch) 

485 os_name = _java_getprop('java.os.name', os_name) 

486 os_version = _java_getprop('java.os.version', os_version) 

487 osinfo = os_name, os_version, os_arch 

488 

489 return release, vendor, vminfo, osinfo 

490 

491### System name aliasing 

492 

493def system_alias(system, release, version): 

494 

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

496 marketing names used for some systems. 

497 

498 It also does some reordering of the information in some cases 

499 where it would otherwise cause confusion. 

500 

501 """ 

502 if system == 'SunOS': 

503 # Sun's OS 

504 if release < '5': 

505 # These releases use the old name SunOS 

506 return system, release, version 

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

508 l = release.split('.') 

509 if l: 

510 try: 

511 major = int(l[0]) 

512 except ValueError: 

513 pass 

514 else: 

515 major = major - 3 

516 l[0] = str(major) 

517 release = '.'.join(l) 

518 if release < '6': 

519 system = 'Solaris' 

520 else: 

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

522 system = 'Solaris' 

523 

524 elif system == 'IRIX64': 

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

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

527 # apps are also supported.. 

528 system = 'IRIX' 

529 if version: 

530 version = version + ' (64bit)' 

531 else: 

532 version = '64bit' 

533 

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

535 # In case one of the other tricks 

536 system = 'Windows' 

537 

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

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

540 

541 return system, release, version 

542 

543### Various internal helpers 

544 

545def _platform(*args): 

546 

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

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

549 """ 

550 # Format the platform string 

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

552 

553 # Cleanup some possible filename obstacles... 

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

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

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

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

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

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

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

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

562 

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

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

565 

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

567 while 1: 

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

569 if cleaned == platform: 

570 break 

571 platform = cleaned 

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

573 platform = platform[:-1] 

574 

575 return platform 

576 

577def _node(default=''): 

578 

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

580 """ 

581 try: 

582 import socket 

583 except ImportError: 

584 # No sockets... 

585 return default 

586 try: 

587 return socket.gethostname() 

588 except OSError: 

589 # Still not working... 

590 return default 

591 

592def _follow_symlinks(filepath): 

593 

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

595 real file is reached. 

596 """ 

597 filepath = os.path.abspath(filepath) 

598 while os.path.islink(filepath): 

599 filepath = os.path.normpath( 

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

601 return filepath 

602 

603def _syscmd_uname(option, default=''): 

604 

605 """ Interface to the system's uname command. 

606 """ 

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

608 # XXX Others too ? 

609 return default 

610 

611 import subprocess 

612 try: 

613 output = subprocess.check_output(('uname', option), 

614 stderr=subprocess.DEVNULL, 

615 text=True) 

616 except (OSError, subprocess.CalledProcessError): 

617 return default 

618 return (output.strip() or default) 

619 

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

621 

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

623 

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

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

626 default in case the command should fail. 

627 

628 """ 

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

630 # XXX Others too ? 

631 return default 

632 

633 import subprocess 

634 target = _follow_symlinks(target) 

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

636 # to get deterministic behavior. 

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

638 try: 

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

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

641 stderr=subprocess.DEVNULL, 

642 env=env) 

643 except (OSError, subprocess.CalledProcessError): 

644 return default 

645 if not output: 

646 return default 

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

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

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

650 

651### Information about the used architecture 

652 

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

654# defaults given as parameters 

655_default_architecture = { 

656 'win32': ('', 'WindowsPE'), 

657 'win16': ('', 'Windows'), 

658 'dos': ('', 'MSDOS'), 

659} 

660 

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

662 

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

664 binary) for various architecture information. 

665 

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

667 the bit architecture and the linkage format used for the 

668 executable. Both values are returned as strings. 

669 

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

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

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

673 indicator for the supported pointer size. 

674 

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

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

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

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

679 binary defaults from _default_architecture are used. 

680 

681 """ 

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

683 # else is given as default. 

684 if not bits: 

685 import struct 

686 size = struct.calcsize('P') 

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

688 

689 # Get data from the 'file' system command 

690 if executable: 

691 fileout = _syscmd_file(executable, '') 

692 else: 

693 fileout = '' 

694 

695 if not fileout and \ 

696 executable == sys.executable: 

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

698 # some sensible defaults then... 

699 if sys.platform in _default_architecture: 

700 b, l = _default_architecture[sys.platform] 

701 if b: 

702 bits = b 

703 if l: 

704 linkage = l 

705 return bits, linkage 

706 

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

708 # Format not supported 

709 return bits, linkage 

710 

711 # Bits 

712 if '32-bit' in fileout: 

713 bits = '32bit' 

714 elif 'N32' in fileout: 

715 # On Irix only 

716 bits = 'n32bit' 

717 elif '64-bit' in fileout: 

718 bits = '64bit' 

719 

720 # Linkage 

721 if 'ELF' in fileout: 

722 linkage = 'ELF' 

723 elif 'PE' in fileout: 

724 # E.g. Windows uses this format 

725 if 'Windows' in fileout: 

726 linkage = 'WindowsPE' 

727 else: 

728 linkage = 'PE' 

729 elif 'COFF' in fileout: 

730 linkage = 'COFF' 

731 elif 'MS-DOS' in fileout: 

732 linkage = 'MSDOS' 

733 else: 

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

735 pass 

736 

737 return bits, linkage 

738 

739### Portable uname() interface 

740 

741uname_result = collections.namedtuple("uname_result", 

742 "system node release version machine processor") 

743 

744_uname_cache = None 

745 

746def uname(): 

747 

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

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

750 identifying the underlying platform. 

751 

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

753 possible processor information as an additional tuple entry. 

754 

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

756 

757 """ 

758 global _uname_cache 

759 no_os_uname = 0 

760 

761 if _uname_cache is not None: 

762 return _uname_cache 

763 

764 processor = '' 

765 

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

767 try: 

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

769 except AttributeError: 

770 no_os_uname = 1 

771 

772 if no_os_uname or not list(filter(None, (system, node, release, version, machine))): 

773 # Hmm, no there is either no uname or uname has returned 

774 #'unknowns'... we'll have to poke around the system then. 

775 if no_os_uname: 

776 system = sys.platform 

777 release = '' 

778 version = '' 

779 node = _node() 

780 machine = '' 

781 

782 use_syscmd_ver = 1 

783 

784 # Try win32_ver() on win32 platforms 

785 if system == 'win32': 

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

787 if release and version: 

788 use_syscmd_ver = 0 

789 # Try to use the PROCESSOR_* environment variables 

790 # available on Win XP and later; see 

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

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

793 if not machine: 

794 # WOW64 processes mask the native architecture 

795 if "PROCESSOR_ARCHITEW6432" in os.environ: 

796 machine = os.environ.get("PROCESSOR_ARCHITEW6432", '') 

797 else: 

798 machine = os.environ.get('PROCESSOR_ARCHITECTURE', '') 

799 if not processor: 

800 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine) 

801 

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

803 # platforms 

804 if use_syscmd_ver: 

805 system, release, version = _syscmd_ver(system) 

806 # Normalize system to what win32_ver() normally returns 

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

808 if system == 'Microsoft Windows': 

809 system = 'Windows' 

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

811 # Under Windows Vista and Windows Server 2008, 

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

813 # release is no longer printed. This causes the 

814 # system and release to be misidentified. 

815 system = 'Windows' 

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

817 release = 'Vista' 

818 else: 

819 release = '' 

820 

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

822 # help ourselves 

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

824 if not version: 

825 if system == 'win32': 

826 version = '32bit' 

827 else: 

828 version = '16bit' 

829 system = 'Windows' 

830 

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

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

833 system = 'Java' 

834 version = ', '.join(vminfo) 

835 if not version: 

836 version = vendor 

837 

838 # System specific extensions 

839 if system == 'OpenVMS': 

840 # OpenVMS seems to have release and version mixed up 

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

842 release = version 

843 version = '' 

844 # Get processor information 

845 try: 

846 import vms_lib 

847 except ImportError: 

848 pass 

849 else: 

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

851 if (cpu_number >= 128): 

852 processor = 'Alpha' 

853 else: 

854 processor = 'VAX' 

855 if not processor: 

856 # Get processor information from the uname system command 

857 processor = _syscmd_uname('-p', '') 

858 

859 #If any unknowns still exist, replace them with ''s, which are more portable 

860 if system == 'unknown': 

861 system = '' 

862 if node == 'unknown': 

863 node = '' 

864 if release == 'unknown': 

865 release = '' 

866 if version == 'unknown': 

867 version = '' 

868 if machine == 'unknown': 

869 machine = '' 

870 if processor == 'unknown': 

871 processor = '' 

872 

873 # normalize name 

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

875 system = 'Windows' 

876 release = 'Vista' 

877 

878 _uname_cache = uname_result(system, node, release, version, 

879 machine, processor) 

880 return _uname_cache 

881 

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

883 

884def system(): 

885 

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

887 

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

889 

890 """ 

891 return uname().system 

892 

893def node(): 

894 

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

896 qualified) 

897 

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

899 

900 """ 

901 return uname().node 

902 

903def release(): 

904 

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

906 

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

908 

909 """ 

910 return uname().release 

911 

912def version(): 

913 

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

915 

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

917 

918 """ 

919 return uname().version 

920 

921def machine(): 

922 

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

924 

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

926 

927 """ 

928 return uname().machine 

929 

930def processor(): 

931 

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

933 

934 An empty string is returned if the value cannot be 

935 determined. Note that many platforms do not provide this 

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

937 e.g. NetBSD does this. 

938 

939 """ 

940 return uname().processor 

941 

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

943 

944_sys_version_parser = re.compile( 

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

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

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

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

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

950 

951_ironpython_sys_version_parser = re.compile( 

952 r'IronPython\s*' 

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

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

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

956 

957# IronPython covering 2.6 and 2.7 

958_ironpython26_sys_version_parser = re.compile( 

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

960 r'\(IronPython\s*' 

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

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

963) 

964 

965_pypy_sys_version_parser = re.compile( 

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

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

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

969 

970_sys_version_cache = {} 

971 

972def _sys_version(sys_version=None): 

973 

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

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

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

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

978 identification string. 

979 

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

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

982 defaults to '.0'). 

983 

984 The function returns empty strings for tuple entries that 

985 cannot be determined. 

986 

987 sys_version may be given to parse an alternative version 

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

989 interpreter. 

990 

991 """ 

992 # Get the Python version 

993 if sys_version is None: 

994 sys_version = sys.version 

995 

996 # Try the cache first 

997 result = _sys_version_cache.get(sys_version, None) 

998 if result is not None: 

999 return result 

1000 

1001 # Parse it 

1002 if 'IronPython' in sys_version: 

1003 # IronPython 

1004 name = 'IronPython' 

1005 if sys_version.startswith('IronPython'): 

1006 match = _ironpython_sys_version_parser.match(sys_version) 

1007 else: 

1008 match = _ironpython26_sys_version_parser.match(sys_version) 

1009 

1010 if match is None: 

1011 raise ValueError( 

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

1013 repr(sys_version)) 

1014 

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

1016 buildno = '' 

1017 builddate = '' 

1018 

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

1020 # Jython 

1021 name = 'Jython' 

1022 match = _sys_version_parser.match(sys_version) 

1023 if match is None: 

1024 raise ValueError( 

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

1026 repr(sys_version)) 

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

1028 if builddate is None: 

1029 builddate = '' 

1030 compiler = sys.platform 

1031 

1032 elif "PyPy" in sys_version: 

1033 # PyPy 

1034 name = "PyPy" 

1035 match = _pypy_sys_version_parser.match(sys_version) 

1036 if match is None: 

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

1038 repr(sys_version)) 

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

1040 compiler = "" 

1041 

1042 else: 

1043 # CPython 

1044 match = _sys_version_parser.match(sys_version) 

1045 if match is None: 

1046 raise ValueError( 

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

1048 repr(sys_version)) 

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

1050 match.groups() 

1051 name = 'CPython' 

1052 if builddate is None: 

1053 builddate = '' 

1054 elif buildtime: 

1055 builddate = builddate + ' ' + buildtime 

1056 

1057 if hasattr(sys, '_git'): 

1058 _, branch, revision = sys._git 

1059 elif hasattr(sys, '_mercurial'): 

1060 _, branch, revision = sys._mercurial 

1061 else: 

1062 branch = '' 

1063 revision = '' 

1064 

1065 # Add the patchlevel version if missing 

1066 l = version.split('.') 

1067 if len(l) == 2: 

1068 l.append('0') 

1069 version = '.'.join(l) 

1070 

1071 # Build and cache the result 

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

1073 _sys_version_cache[sys_version] = result 

1074 return result 

1075 

1076def python_implementation(): 

1077 

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

1079 

1080 Currently, the following implementations are identified: 

1081 'CPython' (C implementation of Python), 

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

1083 'Jython' (Java implementation of Python), 

1084 'PyPy' (Python implementation of Python). 

1085 

1086 """ 

1087 return _sys_version()[0] 

1088 

1089def python_version(): 

1090 

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

1092 

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

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

1095 

1096 """ 

1097 return _sys_version()[1] 

1098 

1099def python_version_tuple(): 

1100 

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

1102 of strings. 

1103 

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

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

1106 

1107 """ 

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

1109 

1110def python_branch(): 

1111 

1112 """ Returns a string identifying the Python implementation 

1113 branch. 

1114 

1115 For CPython this is the SCM branch from which the 

1116 Python binary was built. 

1117 

1118 If not available, an empty string is returned. 

1119 

1120 """ 

1121 

1122 return _sys_version()[2] 

1123 

1124def python_revision(): 

1125 

1126 """ Returns a string identifying the Python implementation 

1127 revision. 

1128 

1129 For CPython this is the SCM revision from which the 

1130 Python binary was built. 

1131 

1132 If not available, an empty string is returned. 

1133 

1134 """ 

1135 return _sys_version()[3] 

1136 

1137def python_build(): 

1138 

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

1140 build number and date as strings. 

1141 

1142 """ 

1143 return _sys_version()[4:6] 

1144 

1145def python_compiler(): 

1146 

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

1148 Python. 

1149 

1150 """ 

1151 return _sys_version()[6] 

1152 

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

1154 

1155_platform_cache = {} 

1156 

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

1158 

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

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

1161 

1162 The output is intended to be human readable rather than 

1163 machine parseable. It may look different on different 

1164 platforms and this is intended. 

1165 

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

1167 various platforms that report system names which differ from 

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

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

1170 this. 

1171 

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

1173 absolute minimum information needed to identify the platform. 

1174 

1175 """ 

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

1177 if result is not None: 

1178 return result 

1179 

1180 # Get uname information and then apply platform specific cosmetics 

1181 # to it... 

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

1183 if machine == processor: 

1184 processor = '' 

1185 if aliased: 

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

1187 

1188 if system == 'Darwin': 

1189 # macOS (darwin kernel) 

1190 macos_release = mac_ver()[0] 

1191 if macos_release: 

1192 system = 'macOS' 

1193 release = macos_release 

1194 

1195 if system == 'Windows': 

1196 # MS platforms 

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

1198 if terse: 

1199 platform = _platform(system, release) 

1200 else: 

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

1202 

1203 elif system in ('Linux',): 

1204 # check for libc vs. glibc 

1205 libcname, libcversion = libc_ver(sys.executable) 

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

1207 'with', 

1208 libcname+libcversion) 

1209 elif system == 'Java': 

1210 # Java platforms 

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

1212 if terse or not os_name: 

1213 platform = _platform(system, release, version) 

1214 else: 

1215 platform = _platform(system, release, version, 

1216 'on', 

1217 os_name, os_version, os_arch) 

1218 

1219 else: 

1220 # Generic handler 

1221 if terse: 

1222 platform = _platform(system, release) 

1223 else: 

1224 bits, linkage = architecture(sys.executable) 

1225 platform = _platform(system, release, machine, 

1226 processor, bits, linkage) 

1227 

1228 _platform_cache[(aliased, terse)] = platform 

1229 return platform 

1230 

1231### Command line interface 

1232 

1233if __name__ == '__main__': 

1234 # Default is to print the aliased verbose platform string 

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

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

1237 print(platform(aliased, terse)) 

1238 sys.exit(0)