pythoninfo: sync with master (#4843)
This commit is contained in:
parent
3ff488c082
commit
2316c68640
|
@ -1,7 +1,8 @@
|
|||
"""
|
||||
Collect various informations about Python to help debugging test failures.
|
||||
Collect various information about Python to help debugging test failures.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import errno
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
@ -26,8 +27,8 @@ class PythonInfo:
|
|||
if value is None:
|
||||
return
|
||||
|
||||
if not isinstance(value, (int, long)):
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, int):
|
||||
if not isinstance(value, str):
|
||||
# convert other objects like sys.flags to string
|
||||
value = str(value)
|
||||
|
||||
|
@ -39,7 +40,7 @@ class PythonInfo:
|
|||
|
||||
def get_infos(self):
|
||||
"""
|
||||
Get informations as a key:value dictionary where values are strings.
|
||||
Get information as a key:value dictionary where values are strings.
|
||||
"""
|
||||
return {key: str(value) for key, value in self.info.items()}
|
||||
|
||||
|
@ -55,6 +56,14 @@ def copy_attributes(info_add, obj, name_fmt, attributes, formatter=None):
|
|||
info_add(name, value)
|
||||
|
||||
|
||||
def copy_attr(info_add, name, mod, attr_name):
|
||||
try:
|
||||
value = getattr(mod, attr_name)
|
||||
except AttributeError:
|
||||
return
|
||||
info_add(name, value)
|
||||
|
||||
|
||||
def call_func(info_add, name, mod, func_name, formatter=None):
|
||||
try:
|
||||
func = getattr(mod, func_name)
|
||||
|
@ -68,6 +77,8 @@ def call_func(info_add, name, mod, func_name, formatter=None):
|
|||
|
||||
def collect_sys(info_add):
|
||||
attributes = (
|
||||
'_framework',
|
||||
'abiflags',
|
||||
'api_version',
|
||||
'builtin_module_names',
|
||||
'byteorder',
|
||||
|
@ -76,20 +87,28 @@ def collect_sys(info_add):
|
|||
'flags',
|
||||
'float_info',
|
||||
'float_repr_style',
|
||||
'hash_info',
|
||||
'hexversion',
|
||||
'maxint',
|
||||
'implementation',
|
||||
'int_info',
|
||||
'maxsize',
|
||||
'maxunicode',
|
||||
'path',
|
||||
'platform',
|
||||
'prefix',
|
||||
'thread_info',
|
||||
'version',
|
||||
'version_info',
|
||||
'winver',
|
||||
)
|
||||
copy_attributes(info_add, sys, 'sys.%s', attributes)
|
||||
|
||||
call_func(info_add, 'sys.androidapilevel', sys, 'getandroidapilevel')
|
||||
call_func(info_add, 'sys.windowsversion', sys, 'getwindowsversion')
|
||||
|
||||
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'):
|
||||
|
@ -129,26 +148,43 @@ def collect_platform(info_add):
|
|||
def collect_locale(info_add):
|
||||
import locale
|
||||
|
||||
info_add('locale.encoding', locale.getpreferredencoding(True))
|
||||
info_add('locale.encoding', locale.getpreferredencoding(False))
|
||||
|
||||
|
||||
def collect_builtins(info_add):
|
||||
info_add('builtins.float.float_format', float.__getformat__("float"))
|
||||
info_add('builtins.float.double_format', float.__getformat__("double"))
|
||||
|
||||
|
||||
def collect_os(info_add):
|
||||
import os
|
||||
|
||||
attributes = ('name',)
|
||||
copy_attributes(info_add, os, 'os.%s', attributes)
|
||||
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
|
||||
|
||||
info_add("os.cwd", os.getcwd())
|
||||
attributes = (
|
||||
'name',
|
||||
'supports_bytes_environ',
|
||||
'supports_effective_ids',
|
||||
'supports_fd',
|
||||
'supports_follow_symlinks',
|
||||
)
|
||||
copy_attributes(info_add, os, 'os.%s', attributes, formatter=format_attr)
|
||||
|
||||
call_func(info_add, 'os.cwd', os, 'getcwd')
|
||||
|
||||
call_func(info_add, 'os.uid', os, 'getuid')
|
||||
call_func(info_add, 'os.gid', os, 'getgid')
|
||||
call_func(info_add, 'os.uname', os, 'uname')
|
||||
|
||||
if hasattr(os, 'getgroups'):
|
||||
groups = os.getgroups()
|
||||
groups = map(str, groups)
|
||||
groups = ', '.join(groups)
|
||||
info_add("os.groups", groups)
|
||||
def format_groups(groups):
|
||||
return ', '.join(map(str, groups))
|
||||
|
||||
call_func(info_add, 'os.groups', os, 'getgroups', formatter=format_groups)
|
||||
|
||||
if hasattr(os, 'getlogin'):
|
||||
try:
|
||||
|
@ -160,6 +196,7 @@ def collect_os(info_add):
|
|||
else:
|
||||
info_add("os.login", login)
|
||||
|
||||
call_func(info_add, 'os.cpu_count', os, 'cpu_count')
|
||||
call_func(info_add, 'os.loadavg', os, 'getloadavg')
|
||||
|
||||
# Get environment variables: filter to list
|
||||
|
@ -190,7 +227,9 @@ def collect_os(info_add):
|
|||
)
|
||||
for name, value in os.environ.items():
|
||||
uname = name.upper()
|
||||
if (uname in ENV_VARS or uname.startswith(("PYTHON", "LC_"))
|
||||
if (uname in ENV_VARS
|
||||
# Copy PYTHON* and LC_* variables
|
||||
or uname.startswith(("PYTHON", "LC_"))
|
||||
# Visual Studio: VS140COMNTOOLS
|
||||
or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
|
||||
info_add('os.environ[%s]' % name, value)
|
||||
|
@ -200,13 +239,20 @@ def collect_os(info_add):
|
|||
os.umask(mask)
|
||||
info_add("os.umask", '%03o' % mask)
|
||||
|
||||
try:
|
||||
cpu_count = os.sysconf('SC_NPROCESSORS_ONLN')
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
else:
|
||||
if cpu_count:
|
||||
info_add('os.sysconf(SC_NPROCESSORS_ONLN)', cpu_count)
|
||||
if hasattr(os, 'getrandom'):
|
||||
# PEP 524: Check if system urandom is initialized
|
||||
try:
|
||||
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
|
||||
|
||||
|
||||
def collect_readline(info_add):
|
||||
|
@ -216,7 +262,7 @@ def collect_readline(info_add):
|
|||
return
|
||||
|
||||
def format_attr(attr, value):
|
||||
if isinstance(value, (int, long)):
|
||||
if isinstance(value, int):
|
||||
return "%#x" % value
|
||||
else:
|
||||
return value
|
||||
|
@ -224,6 +270,7 @@ def collect_readline(info_add):
|
|||
attributes = (
|
||||
"_READLINE_VERSION",
|
||||
"_READLINE_RUNTIME_VERSION",
|
||||
"_READLINE_LIBRARY_VERSION",
|
||||
)
|
||||
copy_attributes(info_add, readline, 'readline.%s', attributes,
|
||||
formatter=format_attr)
|
||||
|
@ -256,11 +303,11 @@ def collect_tkinter(info_add):
|
|||
copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes)
|
||||
|
||||
try:
|
||||
import Tkinter
|
||||
import tkinter
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
tcl = Tkinter.Tcl()
|
||||
tcl = tkinter.Tcl()
|
||||
patchlevel = tcl.call('info', 'patchlevel')
|
||||
info_add('tkinter.info_patchlevel', patchlevel)
|
||||
|
||||
|
@ -276,6 +323,11 @@ def collect_time(info_add):
|
|||
)
|
||||
copy_attributes(info_add, time, 'time.%s', attributes)
|
||||
|
||||
if hasattr(time, 'get_clock_info'):
|
||||
for clock in ('time', 'perf_counter'):
|
||||
tinfo = time.get_clock_info(clock)
|
||||
info_add('time.%s' % clock, tinfo)
|
||||
|
||||
|
||||
def collect_sysconfig(info_add):
|
||||
import sysconfig
|
||||
|
@ -287,7 +339,6 @@ def collect_sysconfig(info_add):
|
|||
'CCSHARED',
|
||||
'CFLAGS',
|
||||
'CFLAGSFORSHARED',
|
||||
'PY_LDFLAGS',
|
||||
'CONFIG_ARGS',
|
||||
'HOST_GNU_TYPE',
|
||||
'MACHDEP',
|
||||
|
@ -295,6 +346,7 @@ def collect_sysconfig(info_add):
|
|||
'OPT',
|
||||
'PY_CFLAGS',
|
||||
'PY_CFLAGS_NODIST',
|
||||
'PY_LDFLAGS',
|
||||
'Py_DEBUG',
|
||||
'Py_ENABLE_SHARED',
|
||||
'SHELL',
|
||||
|
@ -368,15 +420,62 @@ def collect_expat(info_add):
|
|||
copy_attributes(info_add, expat, 'expat.%s', attributes)
|
||||
|
||||
|
||||
def collect_multiprocessing(info_add):
|
||||
def collect_decimal(info_add):
|
||||
try:
|
||||
import multiprocessing
|
||||
import _decimal
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
cpu_count = multiprocessing.cpu_count()
|
||||
if cpu_count:
|
||||
info_add('multiprocessing.cpu_count', cpu_count)
|
||||
attributes = ('__libmpdec_version__',)
|
||||
copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
|
||||
|
||||
|
||||
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')
|
||||
|
||||
|
||||
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')
|
||||
|
||||
|
||||
def collect_info(info):
|
||||
|
@ -384,10 +483,12 @@ def collect_info(info):
|
|||
info_add = info.add
|
||||
|
||||
for collect_func in (
|
||||
collect_expat,
|
||||
# collect_os() should be the first, to check the getrandom() status
|
||||
collect_os,
|
||||
|
||||
collect_builtins,
|
||||
collect_gdb,
|
||||
collect_locale,
|
||||
collect_os,
|
||||
collect_platform,
|
||||
collect_readline,
|
||||
collect_socket,
|
||||
|
@ -398,7 +499,14 @@ def collect_info(info):
|
|||
collect_time,
|
||||
collect_tkinter,
|
||||
collect_zlib,
|
||||
collect_multiprocessing,
|
||||
collect_expat,
|
||||
collect_decimal,
|
||||
collect_testcapi,
|
||||
collect_resource,
|
||||
|
||||
# Collecting from tests should be last as they have side effects.
|
||||
collect_test_socket,
|
||||
collect_test_support,
|
||||
):
|
||||
try:
|
||||
collect_func(info_add)
|
||||
|
|
|
@ -35,7 +35,7 @@ def try_address(host, port=0, family=socket.AF_INET):
|
|||
|
||||
HOST = test_support.HOST
|
||||
MSG = b'Michael Gilfix was here\n'
|
||||
SUPPORTS_IPV6 = socket.has_ipv6 and try_address('::1', family=socket.AF_INET6)
|
||||
SUPPORTS_IPV6 = test_support.IPV6_ENABLED
|
||||
|
||||
try:
|
||||
import thread
|
||||
|
|
Loading…
Reference in New Issue