| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2017-11-05 07:37:50 -06:00
										 |  |  | Collect various information about Python to help debugging test failures. | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | from __future__ import print_function | 
					
						
							| 
									
										
										
										
											2017-09-19 07:37:24 -07:00
										 |  |  | import errno | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import traceback | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def normalize_text(text): | 
					
						
							|  |  |  |     if text is None: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     text = str(text) | 
					
						
							|  |  |  |     text = re.sub(r'\s+', ' ', text) | 
					
						
							|  |  |  |     return text.strip() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PythonInfo: | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.info = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add(self, key, value): | 
					
						
							|  |  |  |         if key in self.info: | 
					
						
							|  |  |  |             raise ValueError("duplicate key: %r" % key) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         if value is None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not isinstance(value, int): | 
					
						
							|  |  |  |             if not isinstance(value, str): | 
					
						
							|  |  |  |                 # convert other objects like sys.flags to string | 
					
						
							|  |  |  |                 value = str(value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |             value = value.strip() | 
					
						
							|  |  |  |             if not value: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.info[key] = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_infos(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-11-05 07:37:50 -06:00
										 |  |  |         Get information as a key:value dictionary where values are strings. | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return {key: str(value) for key, value in self.info.items()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def copy_attributes(info_add, obj, name_fmt, attributes, *, formatter=None): | 
					
						
							|  |  |  |     for attr in attributes: | 
					
						
							|  |  |  |         value = getattr(obj, attr, None) | 
					
						
							|  |  |  |         if value is None: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         name = name_fmt % attr | 
					
						
							|  |  |  |         if formatter is not None: | 
					
						
							|  |  |  |             value = formatter(attr, value) | 
					
						
							|  |  |  |         info_add(name, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  | def copy_attr(info_add, name, mod, attr_name): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         value = getattr(mod, attr_name) | 
					
						
							|  |  |  |     except AttributeError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     info_add(name, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  | def call_func(info_add, name, mod, func_name, *, formatter=None): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         func = getattr(mod, func_name) | 
					
						
							|  |  |  |     except AttributeError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     value = func() | 
					
						
							|  |  |  |     if formatter is not None: | 
					
						
							|  |  |  |         value = formatter(value) | 
					
						
							|  |  |  |     info_add(name, value) | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def collect_sys(info_add): | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |     attributes = ( | 
					
						
							|  |  |  |         '_framework', | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         'abiflags', | 
					
						
							|  |  |  |         'api_version', | 
					
						
							|  |  |  |         'builtin_module_names', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         'byteorder', | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         'dont_write_bytecode', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         'executable', | 
					
						
							|  |  |  |         'flags', | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         'float_info', | 
					
						
							|  |  |  |         'float_repr_style', | 
					
						
							|  |  |  |         'hash_info', | 
					
						
							|  |  |  |         'hexversion', | 
					
						
							|  |  |  |         'implementation', | 
					
						
							|  |  |  |         'int_info', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         'maxsize', | 
					
						
							|  |  |  |         'maxunicode', | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         'path', | 
					
						
							|  |  |  |         'platform', | 
					
						
							|  |  |  |         'prefix', | 
					
						
							|  |  |  |         'thread_info', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         'version', | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |         'version_info', | 
					
						
							|  |  |  |         'winver', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     copy_attributes(info_add, sys, 'sys.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call_func(info_add, 'sys.androidapilevel', sys, 'getandroidapilevel') | 
					
						
							|  |  |  |     call_func(info_add, 'sys.windowsversion', sys, 'getwindowsversion') | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     encoding = sys.getfilesystemencoding() | 
					
						
							|  |  |  |     if hasattr(sys, 'getfilesystemencodeerrors'): | 
					
						
							|  |  |  |         encoding = '%s/%s' % (encoding, sys.getfilesystemencodeerrors()) | 
					
						
							|  |  |  |     info_add('sys.filesystem_encoding', encoding) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name in ('stdin', 'stdout', 'stderr'): | 
					
						
							|  |  |  |         stream = getattr(sys, name) | 
					
						
							|  |  |  |         if stream is None: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         encoding = getattr(stream, 'encoding', None) | 
					
						
							|  |  |  |         if not encoding: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         errors = getattr(stream, 'errors', None) | 
					
						
							|  |  |  |         if errors: | 
					
						
							|  |  |  |             encoding = '%s/%s' % (encoding, errors) | 
					
						
							|  |  |  |         info_add('sys.%s.encoding' % name, encoding) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 08:41:10 -07:00
										 |  |  |     # Were we compiled --with-pydebug or with #define Py_DEBUG? | 
					
						
							|  |  |  |     Py_DEBUG = hasattr(sys, 'gettotalrefcount') | 
					
						
							|  |  |  |     if Py_DEBUG: | 
					
						
							|  |  |  |         text = 'Yes (sys.gettotalrefcount() present)' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         text = 'No (sys.gettotalrefcount() missing)' | 
					
						
							|  |  |  |     info_add('Py_DEBUG', text) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def collect_platform(info_add): | 
					
						
							|  |  |  |     import platform | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     arch = platform.architecture() | 
					
						
							|  |  |  |     arch = ' '.join(filter(bool, arch)) | 
					
						
							|  |  |  |     info_add('platform.architecture', arch) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     info_add('platform.python_implementation', | 
					
						
							|  |  |  |              platform.python_implementation()) | 
					
						
							|  |  |  |     info_add('platform.platform', | 
					
						
							|  |  |  |              platform.platform(aliased=True)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_locale(info_add): | 
					
						
							|  |  |  |     import locale | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     info_add('locale.encoding', locale.getpreferredencoding(False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-13 17:27:40 +01:00
										 |  |  | def collect_builtins(info_add): | 
					
						
							|  |  |  |     info_add('builtins.float.float_format', float.__getformat__("float")) | 
					
						
							|  |  |  |     info_add('builtins.float.double_format', float.__getformat__("double")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | def collect_os(info_add): | 
					
						
							|  |  |  |     import os | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     def format_attr(attr, value): | 
					
						
							|  |  |  |         if attr in ('supports_follow_symlinks', 'supports_fd', | 
					
						
							|  |  |  |                     'supports_effective_ids'): | 
					
						
							|  |  |  |             return str(sorted(func.__name__ for func in value)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ( | 
					
						
							|  |  |  |         'name', | 
					
						
							|  |  |  |         'supports_bytes_environ', | 
					
						
							|  |  |  |         'supports_effective_ids', | 
					
						
							|  |  |  |         'supports_fd', | 
					
						
							|  |  |  |         'supports_follow_symlinks', | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     copy_attributes(info_add, os, 'os.%s', attributes, formatter=format_attr) | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-13 17:27:40 +01:00
										 |  |  |     call_func(info_add, 'os.cwd', os, 'getcwd') | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     call_func(info_add, 'os.uid', os, 'getuid') | 
					
						
							|  |  |  |     call_func(info_add, 'os.gid', os, 'getgid') | 
					
						
							|  |  |  |     call_func(info_add, 'os.uname', os, 'uname') | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |     def format_groups(groups): | 
					
						
							|  |  |  |         return ', '.join(map(str, groups)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call_func(info_add, 'os.groups', os, 'getgroups', formatter=format_groups) | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if hasattr(os, 'getlogin'): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             login = os.getlogin() | 
					
						
							|  |  |  |         except OSError: | 
					
						
							|  |  |  |             # getlogin() fails with "OSError: [Errno 25] Inappropriate ioctl | 
					
						
							|  |  |  |             # for device" on Travis CI | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             info_add("os.login", login) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |     call_func(info_add, 'os.cpu_count', os, 'cpu_count') | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     call_func(info_add, 'os.loadavg', os, 'getloadavg') | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Get environment variables: filter to list | 
					
						
							|  |  |  |     # to not leak sensitive information | 
					
						
							|  |  |  |     ENV_VARS = ( | 
					
						
							|  |  |  |         "CC", | 
					
						
							|  |  |  |         "COMSPEC", | 
					
						
							|  |  |  |         "DISPLAY", | 
					
						
							|  |  |  |         "DISTUTILS_USE_SDK", | 
					
						
							|  |  |  |         "DYLD_LIBRARY_PATH", | 
					
						
							|  |  |  |         "HOME", | 
					
						
							|  |  |  |         "HOMEDRIVE", | 
					
						
							|  |  |  |         "HOMEPATH", | 
					
						
							|  |  |  |         "LANG", | 
					
						
							|  |  |  |         "LD_LIBRARY_PATH", | 
					
						
							|  |  |  |         "MACOSX_DEPLOYMENT_TARGET", | 
					
						
							|  |  |  |         "MAKEFLAGS", | 
					
						
							|  |  |  |         "MSSDK", | 
					
						
							|  |  |  |         "PATH", | 
					
						
							|  |  |  |         "SDK_TOOLS_BIN", | 
					
						
							|  |  |  |         "SHELL", | 
					
						
							|  |  |  |         "TEMP", | 
					
						
							|  |  |  |         "TERM", | 
					
						
							|  |  |  |         "TMP", | 
					
						
							|  |  |  |         "TMPDIR", | 
					
						
							|  |  |  |         "USERPROFILE", | 
					
						
							|  |  |  |         "WAYLAND_DISPLAY", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     for name, value in os.environ.items(): | 
					
						
							|  |  |  |         uname = name.upper() | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |         if (uname in ENV_VARS | 
					
						
							|  |  |  |            # Copy PYTHON* and LC_* variables | 
					
						
							|  |  |  |            or uname.startswith(("PYTHON", "LC_")) | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |            # Visual Studio: VS140COMNTOOLS | 
					
						
							|  |  |  |            or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))): | 
					
						
							|  |  |  |             info_add('os.environ[%s]' % name, value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     if hasattr(os, 'umask'): | 
					
						
							|  |  |  |         mask = os.umask(0) | 
					
						
							|  |  |  |         os.umask(mask) | 
					
						
							|  |  |  |         info_add("os.umask", '%03o' % mask) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if hasattr(os, 'getrandom'): | 
					
						
							|  |  |  |         # PEP 524: Check if system urandom is initialized | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2017-09-19 07:37:24 -07:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 os.getrandom(1, os.GRND_NONBLOCK) | 
					
						
							|  |  |  |                 state = 'ready (initialized)' | 
					
						
							|  |  |  |             except BlockingIOError as exc: | 
					
						
							|  |  |  |                 state = 'not seeded yet (%s)' % exc | 
					
						
							|  |  |  |             info_add('os.getrandom', state) | 
					
						
							|  |  |  |         except OSError as exc: | 
					
						
							|  |  |  |             # Python was compiled on a more recent Linux version | 
					
						
							|  |  |  |             # than the current Linux kernel: ignore OSError(ENOSYS) | 
					
						
							|  |  |  |             if exc.errno != errno.ENOSYS: | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def collect_readline(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import readline | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def format_attr(attr, value): | 
					
						
							|  |  |  |         if isinstance(value, int): | 
					
						
							|  |  |  |             return "%#x" % value | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ( | 
					
						
							|  |  |  |         "_READLINE_VERSION", | 
					
						
							|  |  |  |         "_READLINE_RUNTIME_VERSION", | 
					
						
							|  |  |  |         "_READLINE_LIBRARY_VERSION", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     copy_attributes(info_add, readline, 'readline.%s', attributes, | 
					
						
							|  |  |  |                     formatter=format_attr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_gdb(info_add): | 
					
						
							|  |  |  |     import subprocess | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         proc = subprocess.Popen(["gdb", "-nx", "--version"], | 
					
						
							|  |  |  |                                 stdout=subprocess.PIPE, | 
					
						
							|  |  |  |                                 stderr=subprocess.PIPE, | 
					
						
							|  |  |  |                                 universal_newlines=True) | 
					
						
							|  |  |  |         version = proc.communicate()[0] | 
					
						
							|  |  |  |     except OSError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Only keep the first line | 
					
						
							|  |  |  |     version = version.splitlines()[0] | 
					
						
							|  |  |  |     info_add('gdb_version', version) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_tkinter(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import _tkinter | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         attributes = ('TK_VERSION', 'TCL_VERSION') | 
					
						
							|  |  |  |         copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import tkinter | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         tcl = tkinter.Tcl() | 
					
						
							|  |  |  |         patchlevel = tcl.call('info', 'patchlevel') | 
					
						
							|  |  |  |         info_add('tkinter.info_patchlevel', patchlevel) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_time(info_add): | 
					
						
							|  |  |  |     import time | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |     attributes = ( | 
					
						
							|  |  |  |         'altzone', | 
					
						
							|  |  |  |         'daylight', | 
					
						
							|  |  |  |         'timezone', | 
					
						
							|  |  |  |         'tzname', | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     copy_attributes(info_add, time, 'time.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |     if hasattr(time, 'get_clock_info'): | 
					
						
							|  |  |  |         for clock in ('time', 'perf_counter'): | 
					
						
							|  |  |  |             tinfo = time.get_clock_info(clock) | 
					
						
							|  |  |  |             info_add('time.%s' % clock, tinfo) | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_sysconfig(info_add): | 
					
						
							|  |  |  |     import sysconfig | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name in ( | 
					
						
							|  |  |  |         'ABIFLAGS', | 
					
						
							|  |  |  |         'ANDROID_API_LEVEL', | 
					
						
							|  |  |  |         'CC', | 
					
						
							|  |  |  |         'CCSHARED', | 
					
						
							|  |  |  |         'CFLAGS', | 
					
						
							|  |  |  |         'CFLAGSFORSHARED', | 
					
						
							|  |  |  |         'CONFIG_ARGS', | 
					
						
							|  |  |  |         'HOST_GNU_TYPE', | 
					
						
							|  |  |  |         'MACHDEP', | 
					
						
							|  |  |  |         'MULTIARCH', | 
					
						
							|  |  |  |         'OPT', | 
					
						
							|  |  |  |         'PY_CFLAGS', | 
					
						
							|  |  |  |         'PY_CFLAGS_NODIST', | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |         'PY_LDFLAGS', | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         'Py_DEBUG', | 
					
						
							|  |  |  |         'Py_ENABLE_SHARED', | 
					
						
							|  |  |  |         'SHELL', | 
					
						
							|  |  |  |         'SOABI', | 
					
						
							|  |  |  |         'prefix', | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         value = sysconfig.get_config_var(name) | 
					
						
							|  |  |  |         if name == 'ANDROID_API_LEVEL' and not value: | 
					
						
							|  |  |  |             # skip ANDROID_API_LEVEL=0 | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         value = normalize_text(value) | 
					
						
							|  |  |  |         info_add('sysconfig[%s]' % name, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_ssl(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import ssl | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def format_attr(attr, value): | 
					
						
							|  |  |  |         if attr.startswith('OP_'): | 
					
						
							|  |  |  |             return '%#8x' % value | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2017-08-18 12:08:47 +02:00
										 |  |  |             return value | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     attributes = ( | 
					
						
							|  |  |  |         'OPENSSL_VERSION', | 
					
						
							|  |  |  |         'OPENSSL_VERSION_INFO', | 
					
						
							|  |  |  |         'HAS_SNI', | 
					
						
							|  |  |  |         'OP_ALL', | 
					
						
							|  |  |  |         'OP_NO_TLSv1_1', | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_socket(info_add): | 
					
						
							|  |  |  |     import socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     hostname = socket.gethostname() | 
					
						
							|  |  |  |     info_add('socket.hostname', hostname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_sqlite(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import sqlite3 | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ('version', 'sqlite_version') | 
					
						
							|  |  |  |     copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_zlib(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import zlib | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION') | 
					
						
							|  |  |  |     copy_attributes(info_add, zlib, 'zlib.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 22:13:11 +02:00
										 |  |  | def collect_expat(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         from xml.parsers import expat | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ('EXPAT_VERSION',) | 
					
						
							|  |  |  |     copy_attributes(info_add, expat, 'expat.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_decimal(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import _decimal | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ('__libmpdec_version__',) | 
					
						
							|  |  |  |     copy_attributes(info_add, _decimal, '_decimal.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  | def collect_testcapi(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import _testcapi | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call_func(info_add, 'pymem.allocator', _testcapi, 'pymem_getallocatorsname') | 
					
						
							|  |  |  |     copy_attr(info_add, 'pymem.with_pymalloc', _testcapi, 'WITH_PYMALLOC') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-13 17:27:40 +01:00
										 |  |  | def collect_resource(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import resource | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     limits = [attr for attr in dir(resource) if attr.startswith('RLIMIT_')] | 
					
						
							|  |  |  |     for name in limits: | 
					
						
							|  |  |  |         key = getattr(resource, name) | 
					
						
							|  |  |  |         value = resource.getrlimit(key) | 
					
						
							|  |  |  |         info_add('resource.%s' % name, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_test_socket(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         from test import test_socket | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # all check attributes like HAVE_SOCKET_CAN | 
					
						
							|  |  |  |     attributes = [name for name in dir(test_socket) | 
					
						
							|  |  |  |                   if name.startswith('HAVE_')] | 
					
						
							|  |  |  |     copy_attributes(info_add, test_socket, 'test_socket.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collect_test_support(info_add): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         from test import support | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     attributes = ('IPV6_ENABLED',) | 
					
						
							|  |  |  |     copy_attributes(info_add, support, 'test_support.%s', attributes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call_func(info_add, 'test_support._is_gui_available', support, '_is_gui_available') | 
					
						
							|  |  |  |     call_func(info_add, 'test_support.python_is_optimized', support, 'python_is_optimized') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  | def collect_info(info): | 
					
						
							|  |  |  |     error = False | 
					
						
							|  |  |  |     info_add = info.add | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for collect_func in ( | 
					
						
							|  |  |  |         # collect_os() should be the first, to check the getrandom() status | 
					
						
							|  |  |  |         collect_os, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-13 17:27:40 +01:00
										 |  |  |         collect_builtins, | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |         collect_gdb, | 
					
						
							|  |  |  |         collect_locale, | 
					
						
							|  |  |  |         collect_platform, | 
					
						
							|  |  |  |         collect_readline, | 
					
						
							|  |  |  |         collect_socket, | 
					
						
							|  |  |  |         collect_sqlite, | 
					
						
							|  |  |  |         collect_ssl, | 
					
						
							|  |  |  |         collect_sys, | 
					
						
							|  |  |  |         collect_sysconfig, | 
					
						
							|  |  |  |         collect_time, | 
					
						
							|  |  |  |         collect_tkinter, | 
					
						
							|  |  |  |         collect_zlib, | 
					
						
							| 
									
										
										
										
											2017-08-17 22:13:11 +02:00
										 |  |  |         collect_expat, | 
					
						
							|  |  |  |         collect_decimal, | 
					
						
							| 
									
										
										
										
											2017-11-29 17:20:38 +01:00
										 |  |  |         collect_testcapi, | 
					
						
							| 
									
										
										
										
											2017-12-13 17:27:40 +01:00
										 |  |  |         collect_resource, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Collecting from tests should be last as they have side effects. | 
					
						
							|  |  |  |         collect_test_socket, | 
					
						
							|  |  |  |         collect_test_support, | 
					
						
							| 
									
										
										
										
											2017-08-17 16:40:51 +02:00
										 |  |  |     ): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             collect_func(info_add) | 
					
						
							|  |  |  |         except Exception as exc: | 
					
						
							|  |  |  |             error = True | 
					
						
							|  |  |  |             print("ERROR: %s() failed" % (collect_func.__name__), | 
					
						
							|  |  |  |                   file=sys.stderr) | 
					
						
							|  |  |  |             traceback.print_exc(file=sys.stderr) | 
					
						
							|  |  |  |             print(file=sys.stderr) | 
					
						
							|  |  |  |             sys.stderr.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def dump_info(info, file=None): | 
					
						
							|  |  |  |     title = "Python debug information" | 
					
						
							|  |  |  |     print(title) | 
					
						
							|  |  |  |     print("=" * len(title)) | 
					
						
							|  |  |  |     print() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     infos = info.get_infos() | 
					
						
							|  |  |  |     infos = sorted(infos.items()) | 
					
						
							|  |  |  |     for key, value in infos: | 
					
						
							|  |  |  |         value = value.replace("\n", " ") | 
					
						
							|  |  |  |         print("%s: %s" % (key, value)) | 
					
						
							|  |  |  |     print() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     info = PythonInfo() | 
					
						
							|  |  |  |     error = collect_info(info) | 
					
						
							|  |  |  |     dump_info(info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if error: | 
					
						
							|  |  |  |         print("Collection failed: exit with error", file=sys.stderr) | 
					
						
							|  |  |  |         sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     main() |