python (3.12.0)
1 import math
2 import os.path
3 import sys
4 import sysconfig
5 import textwrap
6 from test import support
7
8
9 def format_duration(seconds):
10 ms = math.ceil(seconds * 1e3)
11 seconds, ms = divmod(ms, 1000)
12 minutes, seconds = divmod(seconds, 60)
13 hours, minutes = divmod(minutes, 60)
14
15 parts = []
16 if hours:
17 parts.append('%s hour' % hours)
18 if minutes:
19 parts.append('%s min' % minutes)
20 if seconds:
21 if parts:
22 # 2 min 1 sec
23 parts.append('%s sec' % seconds)
24 else:
25 # 1.0 sec
26 parts.append('%.1f sec' % (seconds + ms / 1000))
27 if not parts:
28 return '%s ms' % ms
29
30 parts = parts[:2]
31 return ' '.join(parts)
32
33
34 def strip_py_suffix(names: list[str]):
35 if not names:
36 return
37 for idx, name in enumerate(names):
38 basename, ext = os.path.splitext(name)
39 if ext == '.py':
40 names[idx] = basename
41
42
43 def count(n, word):
44 if n == 1:
45 return "%d %s" % (n, word)
46 else:
47 return "%d %ss" % (n, word)
48
49
50 def printlist(x, width=70, indent=4, file=None):
51 """Print the elements of iterable x to stdout.
52
53 Optional arg width (default 70) is the maximum line length.
54 Optional arg indent (default 4) is the number of blanks with which to
55 begin each line.
56 """
57
58 blanks = ' ' * indent
59 # Print the sorted list: 'x' may be a '--random' list or a set()
60 print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
61 initial_indent=blanks, subsequent_indent=blanks),
62 file=file)
63
64
65 def print_warning(msg):
66 support.print_warning(msg)
67
68
69 orig_unraisablehook = None
70
71
72 def regrtest_unraisable_hook(unraisable):
73 global orig_unraisablehook
74 support.environment_altered = True
75 support.print_warning("Unraisable exception")
76 old_stderr = sys.stderr
77 try:
78 support.flush_std_streams()
79 sys.stderr = support.print_warning.orig_stderr
80 orig_unraisablehook(unraisable)
81 sys.stderr.flush()
82 finally:
83 sys.stderr = old_stderr
84
85
86 def setup_unraisable_hook():
87 global orig_unraisablehook
88 orig_unraisablehook = sys.unraisablehook
89 sys.unraisablehook = regrtest_unraisable_hook
90
91
92 orig_threading_excepthook = None
93
94
95 def regrtest_threading_excepthook(args):
96 global orig_threading_excepthook
97 support.environment_altered = True
98 support.print_warning(f"Uncaught thread exception: {args.exc_type.__name__}")
99 old_stderr = sys.stderr
100 try:
101 support.flush_std_streams()
102 sys.stderr = support.print_warning.orig_stderr
103 orig_threading_excepthook(args)
104 sys.stderr.flush()
105 finally:
106 sys.stderr = old_stderr
107
108
109 def setup_threading_excepthook():
110 global orig_threading_excepthook
111 import threading
112 orig_threading_excepthook = threading.excepthook
113 threading.excepthook = regrtest_threading_excepthook
114
115
116 def clear_caches():
117 # Clear the warnings registry, so they can be displayed again
118 for mod in sys.modules.values():
119 if hasattr(mod, '__warningregistry__'):
120 del mod.__warningregistry__
121
122 # Flush standard output, so that buffered data is sent to the OS and
123 # associated Python objects are reclaimed.
124 for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__):
125 if stream is not None:
126 stream.flush()
127
128 try:
129 re = sys.modules['re']
130 except KeyError:
131 pass
132 else:
133 re.purge()
134
135 try:
136 _strptime = sys.modules['_strptime']
137 except KeyError:
138 pass
139 else:
140 _strptime._regex_cache.clear()
141
142 try:
143 urllib_parse = sys.modules['urllib.parse']
144 except KeyError:
145 pass
146 else:
147 urllib_parse.clear_cache()
148
149 try:
150 urllib_request = sys.modules['urllib.request']
151 except KeyError:
152 pass
153 else:
154 urllib_request.urlcleanup()
155
156 try:
157 linecache = sys.modules['linecache']
158 except KeyError:
159 pass
160 else:
161 linecache.clearcache()
162
163 try:
164 mimetypes = sys.modules['mimetypes']
165 except KeyError:
166 pass
167 else:
168 mimetypes._default_mime_types()
169
170 try:
171 filecmp = sys.modules['filecmp']
172 except KeyError:
173 pass
174 else:
175 filecmp._cache.clear()
176
177 try:
178 struct = sys.modules['struct']
179 except KeyError:
180 pass
181 else:
182 struct._clearcache()
183
184 try:
185 doctest = sys.modules['doctest']
186 except KeyError:
187 pass
188 else:
189 doctest.master = None
190
191 try:
192 ctypes = sys.modules['ctypes']
193 except KeyError:
194 pass
195 else:
196 ctypes._reset_cache()
197
198 try:
199 typing = sys.modules['typing']
200 except KeyError:
201 pass
202 else:
203 for f in typing._cleanups:
204 f()
205
206 try:
207 fractions = sys.modules['fractions']
208 except KeyError:
209 pass
210 else:
211 fractions._hash_algorithm.cache_clear()
212
213 try:
214 inspect = sys.modules['inspect']
215 except KeyError:
216 pass
217 else:
218 inspect._shadowed_dict_from_mro_tuple.cache_clear()
219
220
221 def get_build_info():
222 # Get most important configure and build options as a list of strings.
223 # Example: ['debug', 'ASAN+MSAN'] or ['release', 'LTO+PGO'].
224
225 config_args = sysconfig.get_config_var('CONFIG_ARGS') or ''
226 cflags = sysconfig.get_config_var('PY_CFLAGS') or ''
227 cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') or ''
228 ldflags_nodist = sysconfig.get_config_var('PY_LDFLAGS_NODIST') or ''
229
230 build = []
231 if hasattr(sys, 'gettotalrefcount'):
232 # --with-pydebug
233 build.append('debug')
234
235 if '-DNDEBUG' in (cflags + cflags_nodist):
236 build.append('without_assert')
237 else:
238 build.append('release')
239
240 if '--with-assertions' in config_args:
241 build.append('with_assert')
242 elif '-DNDEBUG' not in (cflags + cflags_nodist):
243 build.append('with_assert')
244
245 # --enable-framework=name
246 framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
247 if framework:
248 build.append(f'framework={framework}')
249
250 # --enable-shared
251 shared = int(sysconfig.get_config_var('PY_ENABLE_SHARED') or '0')
252 if shared:
253 build.append('shared')
254
255 # --with-lto
256 optimizations = []
257 if '-flto=thin' in ldflags_nodist:
258 optimizations.append('ThinLTO')
259 elif '-flto' in ldflags_nodist:
260 optimizations.append('LTO')
261
262 # --enable-optimizations
263 pgo_options = (
264 # GCC
265 '-fprofile-use',
266 # clang: -fprofile-instr-use=code.profclangd
267 '-fprofile-instr-use',
268 # ICC
269 "-prof-use",
270 )
271 if any(option in cflags_nodist for option in pgo_options):
272 optimizations.append('PGO')
273 if optimizations:
274 build.append('+'.join(optimizations))
275
276 # --with-address-sanitizer
277 sanitizers = []
278 if support.check_sanitizer(address=True):
279 sanitizers.append("ASAN")
280 # --with-memory-sanitizer
281 if support.check_sanitizer(memory=True):
282 sanitizers.append("MSAN")
283 # --with-undefined-behavior-sanitizer
284 if support.check_sanitizer(ub=True):
285 sanitizers.append("UBSAN")
286 if sanitizers:
287 build.append('+'.join(sanitizers))
288
289 # --with-trace-refs
290 if hasattr(sys, 'getobjects'):
291 build.append("TraceRefs")
292 # --enable-pystats
293 if hasattr(sys, '_stats_on'):
294 build.append("pystats")
295 # --with-valgrind
296 if sysconfig.get_config_var('WITH_VALGRIND'):
297 build.append("valgrind")
298 # --with-dtrace
299 if sysconfig.get_config_var('WITH_DTRACE'):
300 build.append("dtrace")
301
302 return build