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