1import math
2import os.path
3import sys
4import textwrap
5from test import support
6
7
8def format_duration(seconds):
9    ms = math.ceil(seconds * 1e3)
10    seconds, ms = divmod(ms, 1000)
11    minutes, seconds = divmod(seconds, 60)
12    hours, minutes = divmod(minutes, 60)
13
14    parts = []
15    if hours:
16        parts.append('%s hour' % hours)
17    if minutes:
18        parts.append('%s min' % minutes)
19    if seconds:
20        if parts:
21            # 2 min 1 sec
22            parts.append('%s sec' % seconds)
23        else:
24            # 1.0 sec
25            parts.append('%.1f sec' % (seconds + ms / 1000))
26    if not parts:
27        return '%s ms' % ms
28
29    parts = parts[:2]
30    return ' '.join(parts)
31
32
33def removepy(names):
34    if not names:
35        return
36    for idx, name in enumerate(names):
37        basename, ext = os.path.splitext(name)
38        if ext == '.py':
39            names[idx] = basename
40
41
42def count(n, word):
43    if n == 1:
44        return "%d %s" % (n, word)
45    else:
46        return "%d %ss" % (n, word)
47
48
49def printlist(x, width=70, indent=4, file=None):
50    """Print the elements of iterable x to stdout.
51
52    Optional arg width (default 70) is the maximum line length.
53    Optional arg indent (default 4) is the number of blanks with which to
54    begin each line.
55    """
56
57    blanks = ' ' * indent
58    # Print the sorted list: 'x' may be a '--random' list or a set()
59    print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
60                        initial_indent=blanks, subsequent_indent=blanks),
61          file=file)
62
63
64def print_warning(msg):
65    support.print_warning(msg)
66
67
68orig_unraisablehook = None
69
70
71def flush_std_streams():
72    if sys.stdout is not None:
73        sys.stdout.flush()
74    if sys.stderr is not None:
75        sys.stderr.flush()
76
77
78def regrtest_unraisable_hook(unraisable):
79    global orig_unraisablehook
80    support.environment_altered = True
81    print_warning("Unraisable exception")
82    old_stderr = sys.stderr
83    try:
84        flush_std_streams()
85        sys.stderr = sys.__stderr__
86        orig_unraisablehook(unraisable)
87        sys.stderr.flush()
88    finally:
89        sys.stderr = old_stderr
90
91
92def setup_unraisable_hook():
93    global orig_unraisablehook
94    orig_unraisablehook = sys.unraisablehook
95    sys.unraisablehook = regrtest_unraisable_hook
96
97
98orig_threading_excepthook = None
99
100
101def regrtest_threading_excepthook(args):
102    global orig_threading_excepthook
103    support.environment_altered = True
104    print_warning(f"Uncaught thread exception: {args.exc_type.__name__}")
105    old_stderr = sys.stderr
106    try:
107        flush_std_streams()
108        sys.stderr = sys.__stderr__
109        orig_threading_excepthook(args)
110        sys.stderr.flush()
111    finally:
112        sys.stderr = old_stderr
113
114
115def setup_threading_excepthook():
116    global orig_threading_excepthook
117    import threading
118    orig_threading_excepthook = threading.excepthook
119    threading.excepthook = regrtest_threading_excepthook
120
121
122def clear_caches():
123    # Clear the warnings registry, so they can be displayed again
124    for mod in sys.modules.values():
125        if hasattr(mod, '__warningregistry__'):
126            del mod.__warningregistry__
127
128    # Flush standard output, so that buffered data is sent to the OS and
129    # associated Python objects are reclaimed.
130    for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__):
131        if stream is not None:
132            stream.flush()
133
134    # Clear assorted module caches.
135    # Don't worry about resetting the cache if the module is not loaded
136    try:
137        distutils_dir_util = sys.modules['distutils.dir_util']
138    except KeyError:
139        pass
140    else:
141        distutils_dir_util._path_created.clear()
142
143    try:
144        re = sys.modules['re']
145    except KeyError:
146        pass
147    else:
148        re.purge()
149
150    try:
151        _strptime = sys.modules['_strptime']
152    except KeyError:
153        pass
154    else:
155        _strptime._regex_cache.clear()
156
157    try:
158        urllib_parse = sys.modules['urllib.parse']
159    except KeyError:
160        pass
161    else:
162        urllib_parse.clear_cache()
163
164    try:
165        urllib_request = sys.modules['urllib.request']
166    except KeyError:
167        pass
168    else:
169        urllib_request.urlcleanup()
170
171    try:
172        linecache = sys.modules['linecache']
173    except KeyError:
174        pass
175    else:
176        linecache.clearcache()
177
178    try:
179        mimetypes = sys.modules['mimetypes']
180    except KeyError:
181        pass
182    else:
183        mimetypes._default_mime_types()
184
185    try:
186        filecmp = sys.modules['filecmp']
187    except KeyError:
188        pass
189    else:
190        filecmp._cache.clear()
191
192    try:
193        struct = sys.modules['struct']
194    except KeyError:
195        pass
196    else:
197        struct._clearcache()
198
199    try:
200        doctest = sys.modules['doctest']
201    except KeyError:
202        pass
203    else:
204        doctest.master = None
205
206    try:
207        ctypes = sys.modules['ctypes']
208    except KeyError:
209        pass
210    else:
211        ctypes._reset_cache()
212
213    try:
214        typing = sys.modules['typing']
215    except KeyError:
216        pass
217    else:
218        for f in typing._cleanups:
219            f()
220
221    support.gc_collect()
222