pythoninfo.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. """
  2. Collect various information about Python to help debugging test failures.
  3. """
  4. from __future__ import print_function
  5. import errno
  6. import re
  7. import sys
  8. import traceback
  9. import unittest
  10. import warnings
  11. MS_WINDOWS = (sys.platform == 'win32')
  12. def normalize_text(text):
  13. if text is None:
  14. return None
  15. text = str(text)
  16. text = re.sub(r'\s+', ' ', text)
  17. return text.strip()
  18. class PythonInfo:
  19. def __init__(self):
  20. self.info = {}
  21. def add(self, key, value):
  22. if key in self.info:
  23. raise ValueError("duplicate key: %r" % key)
  24. if value is None:
  25. return
  26. if not isinstance(value, int):
  27. if not isinstance(value, str):
  28. # convert other objects like sys.flags to string
  29. value = str(value)
  30. value = value.strip()
  31. if not value:
  32. return
  33. self.info[key] = value
  34. def get_infos(self):
  35. """
  36. Get information as a key:value dictionary where values are strings.
  37. """
  38. return {key: str(value) for key, value in self.info.items()}
  39. def copy_attributes(info_add, obj, name_fmt, attributes, *, formatter=None):
  40. for attr in attributes:
  41. value = getattr(obj, attr, None)
  42. if value is None:
  43. continue
  44. name = name_fmt % attr
  45. if formatter is not None:
  46. value = formatter(attr, value)
  47. info_add(name, value)
  48. def copy_attr(info_add, name, mod, attr_name):
  49. try:
  50. value = getattr(mod, attr_name)
  51. except AttributeError:
  52. return
  53. info_add(name, value)
  54. def call_func(info_add, name, mod, func_name, *, formatter=None):
  55. try:
  56. func = getattr(mod, func_name)
  57. except AttributeError:
  58. return
  59. value = func()
  60. if formatter is not None:
  61. value = formatter(value)
  62. info_add(name, value)
  63. def collect_sys(info_add):
  64. attributes = (
  65. '_emscripten_info',
  66. '_framework',
  67. 'abiflags',
  68. 'api_version',
  69. 'builtin_module_names',
  70. 'byteorder',
  71. 'dont_write_bytecode',
  72. 'executable',
  73. 'flags',
  74. 'float_info',
  75. 'float_repr_style',
  76. 'hash_info',
  77. 'hexversion',
  78. 'implementation',
  79. 'int_info',
  80. 'maxsize',
  81. 'maxunicode',
  82. 'path',
  83. 'platform',
  84. 'platlibdir',
  85. 'prefix',
  86. 'thread_info',
  87. 'version',
  88. 'version_info',
  89. 'winver',
  90. )
  91. copy_attributes(info_add, sys, 'sys.%s', attributes)
  92. call_func(info_add, 'sys.androidapilevel', sys, 'getandroidapilevel')
  93. call_func(info_add, 'sys.windowsversion', sys, 'getwindowsversion')
  94. encoding = sys.getfilesystemencoding()
  95. if hasattr(sys, 'getfilesystemencodeerrors'):
  96. encoding = '%s/%s' % (encoding, sys.getfilesystemencodeerrors())
  97. info_add('sys.filesystem_encoding', encoding)
  98. for name in ('stdin', 'stdout', 'stderr'):
  99. stream = getattr(sys, name)
  100. if stream is None:
  101. continue
  102. encoding = getattr(stream, 'encoding', None)
  103. if not encoding:
  104. continue
  105. errors = getattr(stream, 'errors', None)
  106. if errors:
  107. encoding = '%s/%s' % (encoding, errors)
  108. info_add('sys.%s.encoding' % name, encoding)
  109. # Were we compiled --with-pydebug?
  110. Py_DEBUG = hasattr(sys, 'gettotalrefcount')
  111. if Py_DEBUG:
  112. text = 'Yes (sys.gettotalrefcount() present)'
  113. else:
  114. text = 'No (sys.gettotalrefcount() missing)'
  115. info_add('build.Py_DEBUG', text)
  116. # Were we compiled --with-trace-refs?
  117. Py_TRACE_REFS = hasattr(sys, 'getobjects')
  118. if Py_TRACE_REFS:
  119. text = 'Yes (sys.getobjects() present)'
  120. else:
  121. text = 'No (sys.getobjects() missing)'
  122. info_add('build.Py_TRACE_REFS', text)
  123. def collect_platform(info_add):
  124. import platform
  125. arch = platform.architecture()
  126. arch = ' '.join(filter(bool, arch))
  127. info_add('platform.architecture', arch)
  128. info_add('platform.python_implementation',
  129. platform.python_implementation())
  130. info_add('platform.platform',
  131. platform.platform(aliased=True))
  132. libc_ver = ('%s %s' % platform.libc_ver()).strip()
  133. if libc_ver:
  134. info_add('platform.libc_ver', libc_ver)
  135. def collect_locale(info_add):
  136. import locale
  137. info_add('locale.getencoding', locale.getencoding())
  138. def collect_builtins(info_add):
  139. info_add('builtins.float.float_format', float.__getformat__("float"))
  140. info_add('builtins.float.double_format', float.__getformat__("double"))
  141. def collect_urandom(info_add):
  142. import os
  143. if hasattr(os, 'getrandom'):
  144. # PEP 524: Check if system urandom is initialized
  145. try:
  146. try:
  147. os.getrandom(1, os.GRND_NONBLOCK)
  148. state = 'ready (initialized)'
  149. except BlockingIOError as exc:
  150. state = 'not seeded yet (%s)' % exc
  151. info_add('os.getrandom', state)
  152. except OSError as exc:
  153. # Python was compiled on a more recent Linux version
  154. # than the current Linux kernel: ignore OSError(ENOSYS)
  155. if exc.errno != errno.ENOSYS:
  156. raise
  157. def collect_os(info_add):
  158. import os
  159. def format_attr(attr, value):
  160. if attr in ('supports_follow_symlinks', 'supports_fd',
  161. 'supports_effective_ids'):
  162. return str(sorted(func.__name__ for func in value))
  163. else:
  164. return value
  165. attributes = (
  166. 'name',
  167. 'supports_bytes_environ',
  168. 'supports_effective_ids',
  169. 'supports_fd',
  170. 'supports_follow_symlinks',
  171. )
  172. copy_attributes(info_add, os, 'os.%s', attributes, formatter=format_attr)
  173. for func in (
  174. 'cpu_count',
  175. 'getcwd',
  176. 'getegid',
  177. 'geteuid',
  178. 'getgid',
  179. 'getloadavg',
  180. 'getresgid',
  181. 'getresuid',
  182. 'getuid',
  183. 'uname',
  184. ):
  185. call_func(info_add, 'os.%s' % func, os, func)
  186. def format_groups(groups):
  187. return ', '.join(map(str, groups))
  188. call_func(info_add, 'os.getgroups', os, 'getgroups', formatter=format_groups)
  189. if hasattr(os, 'getlogin'):
  190. try:
  191. login = os.getlogin()
  192. except OSError:
  193. # getlogin() fails with "OSError: [Errno 25] Inappropriate ioctl
  194. # for device" on Travis CI
  195. pass
  196. else:
  197. info_add("os.login", login)
  198. # Environment variables used by the stdlib and tests. Don't log the full
  199. # environment: filter to list to not leak sensitive information.
  200. #
  201. # HTTP_PROXY is not logged because it can contain a password.
  202. ENV_VARS = frozenset((
  203. "APPDATA",
  204. "AR",
  205. "ARCHFLAGS",
  206. "ARFLAGS",
  207. "AUDIODEV",
  208. "CC",
  209. "CFLAGS",
  210. "COLUMNS",
  211. "COMPUTERNAME",
  212. "COMSPEC",
  213. "CPP",
  214. "CPPFLAGS",
  215. "DISPLAY",
  216. "DISTUTILS_DEBUG",
  217. "DISTUTILS_USE_SDK",
  218. "DYLD_LIBRARY_PATH",
  219. "ENSUREPIP_OPTIONS",
  220. "HISTORY_FILE",
  221. "HOME",
  222. "HOMEDRIVE",
  223. "HOMEPATH",
  224. "IDLESTARTUP",
  225. "LANG",
  226. "LDFLAGS",
  227. "LDSHARED",
  228. "LD_LIBRARY_PATH",
  229. "LINES",
  230. "MACOSX_DEPLOYMENT_TARGET",
  231. "MAILCAPS",
  232. "MAKEFLAGS",
  233. "MIXERDEV",
  234. "MSSDK",
  235. "PATH",
  236. "PATHEXT",
  237. "PIP_CONFIG_FILE",
  238. "PLAT",
  239. "POSIXLY_CORRECT",
  240. "PY_SAX_PARSER",
  241. "ProgramFiles",
  242. "ProgramFiles(x86)",
  243. "RUNNING_ON_VALGRIND",
  244. "SDK_TOOLS_BIN",
  245. "SERVER_SOFTWARE",
  246. "SHELL",
  247. "SOURCE_DATE_EPOCH",
  248. "SYSTEMROOT",
  249. "TEMP",
  250. "TERM",
  251. "TILE_LIBRARY",
  252. "TIX_LIBRARY",
  253. "TMP",
  254. "TMPDIR",
  255. "TRAVIS",
  256. "TZ",
  257. "USERPROFILE",
  258. "VIRTUAL_ENV",
  259. "WAYLAND_DISPLAY",
  260. "WINDIR",
  261. "_PYTHON_HOST_PLATFORM",
  262. "_PYTHON_PROJECT_BASE",
  263. "_PYTHON_SYSCONFIGDATA_NAME",
  264. "__PYVENV_LAUNCHER__",
  265. ))
  266. for name, value in os.environ.items():
  267. uname = name.upper()
  268. if (uname in ENV_VARS
  269. # Copy PYTHON* and LC_* variables
  270. or uname.startswith(("PYTHON", "LC_"))
  271. # Visual Studio: VS140COMNTOOLS
  272. or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
  273. info_add('os.environ[%s]' % name, value)
  274. if hasattr(os, 'umask'):
  275. mask = os.umask(0)
  276. os.umask(mask)
  277. info_add("os.umask", '0o%03o' % mask)
  278. def collect_pwd(info_add):
  279. try:
  280. import pwd
  281. except ImportError:
  282. return
  283. import os
  284. uid = os.getuid()
  285. try:
  286. entry = pwd.getpwuid(uid)
  287. except KeyError:
  288. entry = None
  289. info_add('pwd.getpwuid(%s)'% uid,
  290. entry if entry is not None else '<KeyError>')
  291. if entry is None:
  292. # there is nothing interesting to read if the current user identifier
  293. # is not the password database
  294. return
  295. if hasattr(os, 'getgrouplist'):
  296. groups = os.getgrouplist(entry.pw_name, entry.pw_gid)
  297. groups = ', '.join(map(str, groups))
  298. info_add('os.getgrouplist', groups)
  299. def collect_readline(info_add):
  300. try:
  301. import readline
  302. except ImportError:
  303. return
  304. def format_attr(attr, value):
  305. if isinstance(value, int):
  306. return "%#x" % value
  307. else:
  308. return value
  309. attributes = (
  310. "_READLINE_VERSION",
  311. "_READLINE_RUNTIME_VERSION",
  312. "_READLINE_LIBRARY_VERSION",
  313. )
  314. copy_attributes(info_add, readline, 'readline.%s', attributes,
  315. formatter=format_attr)
  316. if not hasattr(readline, "_READLINE_LIBRARY_VERSION"):
  317. # _READLINE_LIBRARY_VERSION has been added to CPython 3.7
  318. doc = getattr(readline, '__doc__', '')
  319. if 'libedit readline' in doc:
  320. info_add('readline.library', 'libedit readline')
  321. elif 'GNU readline' in doc:
  322. info_add('readline.library', 'GNU readline')
  323. def collect_gdb(info_add):
  324. import subprocess
  325. try:
  326. proc = subprocess.Popen(["gdb", "-nx", "--version"],
  327. stdout=subprocess.PIPE,
  328. stderr=subprocess.PIPE,
  329. universal_newlines=True)
  330. version = proc.communicate()[0]
  331. if proc.returncode:
  332. # ignore gdb failure: test_gdb will log the error
  333. return
  334. except OSError:
  335. return
  336. # Only keep the first line
  337. version = version.splitlines()[0]
  338. info_add('gdb_version', version)
  339. def collect_tkinter(info_add):
  340. try:
  341. import _tkinter
  342. except ImportError:
  343. pass
  344. else:
  345. attributes = ('TK_VERSION', 'TCL_VERSION')
  346. copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes)
  347. try:
  348. import tkinter
  349. except ImportError:
  350. pass
  351. else:
  352. tcl = tkinter.Tcl()
  353. patchlevel = tcl.call('info', 'patchlevel')
  354. info_add('tkinter.info_patchlevel', patchlevel)
  355. def collect_time(info_add):
  356. import time
  357. info_add('time.time', time.time())
  358. attributes = (
  359. 'altzone',
  360. 'daylight',
  361. 'timezone',
  362. 'tzname',
  363. )
  364. copy_attributes(info_add, time, 'time.%s', attributes)
  365. if hasattr(time, 'get_clock_info'):
  366. for clock in ('clock', 'monotonic', 'perf_counter',
  367. 'process_time', 'thread_time', 'time'):
  368. try:
  369. # prevent DeprecatingWarning on get_clock_info('clock')
  370. with warnings.catch_warnings(record=True):
  371. clock_info = time.get_clock_info(clock)
  372. except ValueError:
  373. # missing clock like time.thread_time()
  374. pass
  375. else:
  376. info_add('time.get_clock_info(%s)' % clock, clock_info)
  377. def collect_curses(info_add):
  378. try:
  379. import curses
  380. except ImportError:
  381. return
  382. copy_attr(info_add, 'curses.ncurses_version', curses, 'ncurses_version')
  383. def collect_datetime(info_add):
  384. try:
  385. import datetime
  386. except ImportError:
  387. return
  388. info_add('datetime.datetime.now', datetime.datetime.now())
  389. def collect_sysconfig(info_add):
  390. # On Windows, sysconfig is not reliable to get macros used
  391. # to build Python
  392. if MS_WINDOWS:
  393. return
  394. import sysconfig
  395. for name in (
  396. 'ABIFLAGS',
  397. 'ANDROID_API_LEVEL',
  398. 'CC',
  399. 'CCSHARED',
  400. 'CFLAGS',
  401. 'CFLAGSFORSHARED',
  402. 'CONFIG_ARGS',
  403. 'HOST_GNU_TYPE',
  404. 'MACHDEP',
  405. 'MULTIARCH',
  406. 'OPT',
  407. 'PY_CFLAGS',
  408. 'PY_CFLAGS_NODIST',
  409. 'PY_CORE_LDFLAGS',
  410. 'PY_LDFLAGS',
  411. 'PY_LDFLAGS_NODIST',
  412. 'PY_STDMODULE_CFLAGS',
  413. 'Py_DEBUG',
  414. 'Py_ENABLE_SHARED',
  415. 'SHELL',
  416. 'SOABI',
  417. 'prefix',
  418. ):
  419. value = sysconfig.get_config_var(name)
  420. if name == 'ANDROID_API_LEVEL' and not value:
  421. # skip ANDROID_API_LEVEL=0
  422. continue
  423. value = normalize_text(value)
  424. info_add('sysconfig[%s]' % name, value)
  425. PY_CFLAGS = sysconfig.get_config_var('PY_CFLAGS')
  426. NDEBUG = (PY_CFLAGS and '-DNDEBUG' in PY_CFLAGS)
  427. if NDEBUG:
  428. text = 'ignore assertions (macro defined)'
  429. else:
  430. text= 'build assertions (macro not defined)'
  431. info_add('build.NDEBUG',text)
  432. for name in (
  433. 'WITH_DOC_STRINGS',
  434. 'WITH_DTRACE',
  435. 'WITH_FREELISTS',
  436. 'WITH_PYMALLOC',
  437. 'WITH_VALGRIND',
  438. ):
  439. value = sysconfig.get_config_var(name)
  440. if value:
  441. text = 'Yes'
  442. else:
  443. text = 'No'
  444. info_add(f'build.{name}', text)
  445. def collect_ssl(info_add):
  446. import os
  447. try:
  448. import ssl
  449. except ImportError:
  450. return
  451. try:
  452. import _ssl
  453. except ImportError:
  454. _ssl = None
  455. def format_attr(attr, value):
  456. if attr.startswith('OP_'):
  457. return '%#8x' % value
  458. else:
  459. return value
  460. attributes = (
  461. 'OPENSSL_VERSION',
  462. 'OPENSSL_VERSION_INFO',
  463. 'HAS_SNI',
  464. 'OP_ALL',
  465. 'OP_NO_TLSv1_1',
  466. )
  467. copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr)
  468. for name, ctx in (
  469. ('SSLContext', ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)),
  470. ('default_https_context', ssl._create_default_https_context()),
  471. ('stdlib_context', ssl._create_stdlib_context()),
  472. ):
  473. attributes = (
  474. 'minimum_version',
  475. 'maximum_version',
  476. 'protocol',
  477. 'options',
  478. 'verify_mode',
  479. )
  480. copy_attributes(info_add, ctx, f'ssl.{name}.%s', attributes)
  481. env_names = ["OPENSSL_CONF", "SSLKEYLOGFILE"]
  482. if _ssl is not None and hasattr(_ssl, 'get_default_verify_paths'):
  483. parts = _ssl.get_default_verify_paths()
  484. env_names.extend((parts[0], parts[2]))
  485. for name in env_names:
  486. try:
  487. value = os.environ[name]
  488. except KeyError:
  489. continue
  490. info_add('ssl.environ[%s]' % name, value)
  491. def collect_socket(info_add):
  492. try:
  493. import socket
  494. except ImportError:
  495. return
  496. try:
  497. hostname = socket.gethostname()
  498. except (OSError, AttributeError):
  499. # WASI SDK 16.0 does not have gethostname(2).
  500. if sys.platform != "wasi":
  501. raise
  502. else:
  503. info_add('socket.hostname', hostname)
  504. def collect_sqlite(info_add):
  505. try:
  506. import sqlite3
  507. except ImportError:
  508. return
  509. attributes = ('version', 'sqlite_version')
  510. copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes)
  511. def collect_zlib(info_add):
  512. try:
  513. import zlib
  514. except ImportError:
  515. return
  516. attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION')
  517. copy_attributes(info_add, zlib, 'zlib.%s', attributes)
  518. def collect_expat(info_add):
  519. try:
  520. from xml.parsers import expat
  521. except ImportError:
  522. return
  523. attributes = ('EXPAT_VERSION',)
  524. copy_attributes(info_add, expat, 'expat.%s', attributes)
  525. def collect_decimal(info_add):
  526. try:
  527. import _decimal
  528. except ImportError:
  529. return
  530. attributes = ('__libmpdec_version__',)
  531. copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
  532. def collect_testcapi(info_add):
  533. try:
  534. import _testcapi
  535. except ImportError:
  536. return
  537. call_func(info_add, 'pymem.allocator', _testcapi, 'pymem_getallocatorsname')
  538. def collect_resource(info_add):
  539. try:
  540. import resource
  541. except ImportError:
  542. return
  543. limits = [attr for attr in dir(resource) if attr.startswith('RLIMIT_')]
  544. for name in limits:
  545. key = getattr(resource, name)
  546. value = resource.getrlimit(key)
  547. info_add('resource.%s' % name, value)
  548. call_func(info_add, 'resource.pagesize', resource, 'getpagesize')
  549. def collect_test_socket(info_add):
  550. try:
  551. from test import test_socket
  552. except (ImportError, unittest.SkipTest):
  553. return
  554. # all check attributes like HAVE_SOCKET_CAN
  555. attributes = [name for name in dir(test_socket)
  556. if name.startswith('HAVE_')]
  557. copy_attributes(info_add, test_socket, 'test_socket.%s', attributes)
  558. def collect_test_support(info_add):
  559. try:
  560. from test import support
  561. except ImportError:
  562. return
  563. attributes = ('IPV6_ENABLED',)
  564. copy_attributes(info_add, support, 'test_support.%s', attributes)
  565. call_func(info_add, 'test_support._is_gui_available', support, '_is_gui_available')
  566. call_func(info_add, 'test_support.python_is_optimized', support, 'python_is_optimized')
  567. info_add('test_support.check_sanitizer(address=True)',
  568. support.check_sanitizer(address=True))
  569. info_add('test_support.check_sanitizer(memory=True)',
  570. support.check_sanitizer(memory=True))
  571. info_add('test_support.check_sanitizer(ub=True)',
  572. support.check_sanitizer(ub=True))
  573. def collect_cc(info_add):
  574. import subprocess
  575. import sysconfig
  576. CC = sysconfig.get_config_var('CC')
  577. if not CC:
  578. return
  579. try:
  580. import shlex
  581. args = shlex.split(CC)
  582. except ImportError:
  583. args = CC.split()
  584. args.append('--version')
  585. try:
  586. proc = subprocess.Popen(args,
  587. stdout=subprocess.PIPE,
  588. stderr=subprocess.STDOUT,
  589. universal_newlines=True)
  590. except OSError:
  591. # Cannot run the compiler, for example when Python has been
  592. # cross-compiled and installed on the target platform where the
  593. # compiler is missing.
  594. return
  595. stdout = proc.communicate()[0]
  596. if proc.returncode:
  597. # CC --version failed: ignore error
  598. return
  599. text = stdout.splitlines()[0]
  600. text = normalize_text(text)
  601. info_add('CC.version', text)
  602. def collect_gdbm(info_add):
  603. try:
  604. from _gdbm import _GDBM_VERSION
  605. except ImportError:
  606. return
  607. info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
  608. def collect_get_config(info_add):
  609. # Get global configuration variables, _PyPreConfig and _PyCoreConfig
  610. try:
  611. from _testinternalcapi import get_configs
  612. except ImportError:
  613. return
  614. all_configs = get_configs()
  615. for config_type in sorted(all_configs):
  616. config = all_configs[config_type]
  617. for key in sorted(config):
  618. info_add('%s[%s]' % (config_type, key), repr(config[key]))
  619. def collect_subprocess(info_add):
  620. import subprocess
  621. copy_attributes(info_add, subprocess, 'subprocess.%s', ('_USE_POSIX_SPAWN',))
  622. def collect_windows(info_add):
  623. try:
  624. import ctypes
  625. except ImportError:
  626. return
  627. if not hasattr(ctypes, 'WinDLL'):
  628. return
  629. ntdll = ctypes.WinDLL('ntdll')
  630. BOOLEAN = ctypes.c_ubyte
  631. try:
  632. RtlAreLongPathsEnabled = ntdll.RtlAreLongPathsEnabled
  633. except AttributeError:
  634. res = '<function not available>'
  635. else:
  636. RtlAreLongPathsEnabled.restype = BOOLEAN
  637. RtlAreLongPathsEnabled.argtypes = ()
  638. res = bool(RtlAreLongPathsEnabled())
  639. info_add('windows.RtlAreLongPathsEnabled', res)
  640. try:
  641. import _winapi
  642. dll_path = _winapi.GetModuleFileName(sys.dllhandle)
  643. info_add('windows.dll_path', dll_path)
  644. except (ImportError, AttributeError):
  645. pass
  646. import subprocess
  647. try:
  648. # When wmic.exe output is redirected to a pipe,
  649. # it uses the OEM code page
  650. proc = subprocess.Popen(["wmic", "os", "get", "Caption,Version", "/value"],
  651. stdout=subprocess.PIPE,
  652. stderr=subprocess.PIPE,
  653. encoding="oem",
  654. text=True)
  655. output, stderr = proc.communicate()
  656. if proc.returncode:
  657. output = ""
  658. except OSError:
  659. pass
  660. else:
  661. for line in output.splitlines():
  662. line = line.strip()
  663. if line.startswith('Caption='):
  664. line = line.removeprefix('Caption=').strip()
  665. if line:
  666. info_add('windows.version_caption', line)
  667. elif line.startswith('Version='):
  668. line = line.removeprefix('Version=').strip()
  669. if line:
  670. info_add('windows.version', line)
  671. try:
  672. proc = subprocess.Popen(["ver"], shell=True,
  673. stdout=subprocess.PIPE,
  674. stderr=subprocess.PIPE,
  675. text=True)
  676. output = proc.communicate()[0]
  677. if proc.returncode:
  678. output = ""
  679. except OSError:
  680. return
  681. else:
  682. output = output.strip()
  683. line = output.splitlines()[0]
  684. if line:
  685. info_add('windows.ver', line)
  686. def collect_fips(info_add):
  687. try:
  688. import _hashlib
  689. except ImportError:
  690. _hashlib = None
  691. if _hashlib is not None:
  692. call_func(info_add, 'fips.openssl_fips_mode', _hashlib, 'get_fips_mode')
  693. try:
  694. with open("/proc/sys/crypto/fips_enabled", encoding="utf-8") as fp:
  695. line = fp.readline().rstrip()
  696. if line:
  697. info_add('fips.linux_crypto_fips_enabled', line)
  698. except OSError:
  699. pass
  700. def collect_info(info):
  701. error = False
  702. info_add = info.add
  703. for collect_func in (
  704. # collect_urandom() must be the first, to check the getrandom() status.
  705. # Other functions may block on os.urandom() indirectly and so change
  706. # its state.
  707. collect_urandom,
  708. collect_builtins,
  709. collect_cc,
  710. collect_curses,
  711. collect_datetime,
  712. collect_decimal,
  713. collect_expat,
  714. collect_fips,
  715. collect_gdb,
  716. collect_gdbm,
  717. collect_get_config,
  718. collect_locale,
  719. collect_os,
  720. collect_platform,
  721. collect_pwd,
  722. collect_readline,
  723. collect_resource,
  724. collect_socket,
  725. collect_sqlite,
  726. collect_ssl,
  727. collect_subprocess,
  728. collect_sys,
  729. collect_sysconfig,
  730. collect_testcapi,
  731. collect_time,
  732. collect_tkinter,
  733. collect_windows,
  734. collect_zlib,
  735. # Collecting from tests should be last as they have side effects.
  736. collect_test_socket,
  737. collect_test_support,
  738. ):
  739. try:
  740. collect_func(info_add)
  741. except Exception:
  742. error = True
  743. print("ERROR: %s() failed" % (collect_func.__name__),
  744. file=sys.stderr)
  745. traceback.print_exc(file=sys.stderr)
  746. print(file=sys.stderr)
  747. sys.stderr.flush()
  748. return error
  749. def dump_info(info, file=None):
  750. title = "Python debug information"
  751. print(title)
  752. print("=" * len(title))
  753. print()
  754. infos = info.get_infos()
  755. infos = sorted(infos.items())
  756. for key, value in infos:
  757. value = value.replace("\n", " ")
  758. print("%s: %s" % (key, value))
  759. print()
  760. def main():
  761. info = PythonInfo()
  762. error = collect_info(info)
  763. dump_info(info)
  764. if error:
  765. print("Collection failed: exit with error", file=sys.stderr)
  766. sys.exit(1)
  767. if __name__ == "__main__":
  768. main()