import math import os.path import sys import sysconfig import textwrap from test import support MS_WINDOWS = (sys.platform == 'win32') def format_duration(seconds): ms = math.ceil(seconds * 1e3) seconds, ms = divmod(ms, 1000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) parts = [] if hours: parts.append('%s hour' % hours) if minutes: parts.append('%s min' % minutes) if seconds: if parts: # 2 min 1 sec parts.append('%s sec' % seconds) else: # 1.0 sec parts.append('%.1f sec' % (seconds + ms / 1000)) if not parts: return '%s ms' % ms parts = parts[:2] return ' '.join(parts) def strip_py_suffix(names: list[str]): if not names: return for idx, name in enumerate(names): basename, ext = os.path.splitext(name) if ext == '.py': names[idx] = basename def count(n, word): if n == 1: return "%d %s" % (n, word) else: return "%d %ss" % (n, word) def printlist(x, width=70, indent=4, file=None): """Print the elements of iterable x to stdout. Optional arg width (default 70) is the maximum line length. Optional arg indent (default 4) is the number of blanks with which to begin each line. """ blanks = ' ' * indent # Print the sorted list: 'x' may be a '--random' list or a set() print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width, initial_indent=blanks, subsequent_indent=blanks), file=file) def print_warning(msg): support.print_warning(msg) orig_unraisablehook = None def regrtest_unraisable_hook(unraisable): global orig_unraisablehook support.environment_altered = True support.print_warning("Unraisable exception") old_stderr = sys.stderr try: support.flush_std_streams() sys.stderr = support.print_warning.orig_stderr orig_unraisablehook(unraisable) sys.stderr.flush() finally: sys.stderr = old_stderr def setup_unraisable_hook(): global orig_unraisablehook orig_unraisablehook = sys.unraisablehook sys.unraisablehook = regrtest_unraisable_hook orig_threading_excepthook = None def regrtest_threading_excepthook(args): global orig_threading_excepthook support.environment_altered = True support.print_warning(f"Uncaught thread exception: {args.exc_type.__name__}") old_stderr = sys.stderr try: support.flush_std_streams() sys.stderr = support.print_warning.orig_stderr orig_threading_excepthook(args) sys.stderr.flush() finally: sys.stderr = old_stderr def setup_threading_excepthook(): global orig_threading_excepthook import threading orig_threading_excepthook = threading.excepthook threading.excepthook = regrtest_threading_excepthook def clear_caches(): # Clear the warnings registry, so they can be displayed again for mod in sys.modules.values(): if hasattr(mod, '__warningregistry__'): del mod.__warningregistry__ # Flush standard output, so that buffered data is sent to the OS and # associated Python objects are reclaimed. for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__): if stream is not None: stream.flush() try: re = sys.modules['re'] except KeyError: pass else: re.purge() try: _strptime = sys.modules['_strptime'] except KeyError: pass else: _strptime._regex_cache.clear() try: urllib_parse = sys.modules['urllib.parse'] except KeyError: pass else: urllib_parse.clear_cache() try: urllib_request = sys.modules['urllib.request'] except KeyError: pass else: urllib_request.urlcleanup() try: linecache = sys.modules['linecache'] except KeyError: pass else: linecache.clearcache() try: mimetypes = sys.modules['mimetypes'] except KeyError: pass else: mimetypes._default_mime_types() try: filecmp = sys.modules['filecmp'] except KeyError: pass else: filecmp._cache.clear() try: struct = sys.modules['struct'] except KeyError: pass else: struct._clearcache() try: doctest = sys.modules['doctest'] except KeyError: pass else: doctest.master = None try: ctypes = sys.modules['ctypes'] except KeyError: pass else: ctypes._reset_cache() try: typing = sys.modules['typing'] except KeyError: pass else: for f in typing._cleanups: f() try: fractions = sys.modules['fractions'] except KeyError: pass else: fractions._hash_algorithm.cache_clear() try: inspect = sys.modules['inspect'] except KeyError: pass else: inspect._shadowed_dict_from_mro_tuple.cache_clear() def get_build_info(): # Get most important configure and build options as a list of strings. # Example: ['debug', 'ASAN+MSAN'] or ['release', 'LTO+PGO']. config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' cflags = sysconfig.get_config_var('PY_CFLAGS') or '' cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or '' ldflags_nodist = sysconfig.get_config_var('PY_LDFLAGS_NODIST') or '' build = [] # --disable-gil if sysconfig.get_config_var('Py_NOGIL'): build.append("nogil") if hasattr(sys, 'gettotalrefcount'): # --with-pydebug build.append('debug') if '-DNDEBUG' in (cflags + cflags_nodist): build.append('without_assert') else: build.append('release') if '--with-assertions' in config_args: build.append('with_assert') elif '-DNDEBUG' not in (cflags + cflags_nodist): build.append('with_assert') # --enable-framework=name framework = sysconfig.get_config_var('PYTHONFRAMEWORK') if framework: build.append(f'framework={framework}') # --enable-shared shared = int(sysconfig.get_config_var('PY_ENABLE_SHARED') or '0') if shared: build.append('shared') # --with-lto optimizations = [] if '-flto=thin' in ldflags_nodist: optimizations.append('ThinLTO') elif '-flto' in ldflags_nodist: optimizations.append('LTO') # --enable-optimizations pgo_options = ( # GCC '-fprofile-use', # clang: -fprofile-instr-use=code.profclangd '-fprofile-instr-use', # ICC "-prof-use", ) if any(option in cflags_nodist for option in pgo_options): optimizations.append('PGO') if optimizations: build.append('+'.join(optimizations)) # --with-address-sanitizer sanitizers = [] if support.check_sanitizer(address=True): sanitizers.append("ASAN") # --with-memory-sanitizer if support.check_sanitizer(memory=True): sanitizers.append("MSAN") # --with-undefined-behavior-sanitizer if support.check_sanitizer(ub=True): sanitizers.append("UBSAN") if sanitizers: build.append('+'.join(sanitizers)) # --with-trace-refs if hasattr(sys, 'getobjects'): build.append("TraceRefs") # --enable-pystats if hasattr(sys, '_stats_on'): build.append("pystats") # --with-valgrind if sysconfig.get_config_var('WITH_VALGRIND'): build.append("valgrind") # --with-dtrace if sysconfig.get_config_var('WITH_DTRACE'): build.append("dtrace") return build