blob: 63388c957ce0c9e013c8be0c74d7309b4396d242 [file] [log] [blame]
Victor Stinner3844fe52015-09-26 10:38:01 +02001import faulthandler
2import json
3import os
Victor Stinner3844fe52015-09-26 10:38:01 +02004import platform
Victor Stinnerdad20e42015-09-29 22:48:52 +02005import random
6import re
7import signal
8import sys
9import sysconfig
10import tempfile
11import textwrap
Victor Stinner3844fe52015-09-26 10:38:01 +020012import traceback
13import unittest
14from test.libregrtest.runtest import (
15 findtests, runtest, run_test_in_subprocess,
16 STDTESTS, NOTTESTS,
17 PASSED, FAILED, ENV_CHANGED, SKIPPED,
18 RESOURCE_DENIED, INTERRUPTED, CHILD_ERROR)
19from test.libregrtest.refleak import warm_caches
20from test.libregrtest.cmdline import _parse_args
21from test import support
22try:
Victor Stinnerdad20e42015-09-29 22:48:52 +020023 import gc
24except ImportError:
25 gc = None
26try:
Victor Stinner3844fe52015-09-26 10:38:01 +020027 import threading
28except ImportError:
29 threading = None
30
31
Victor Stinner3844fe52015-09-26 10:38:01 +020032# When tests are run from the Python build directory, it is best practice
33# to keep the test files in a subfolder. This eases the cleanup of leftover
34# files using the "make distclean" command.
35if sysconfig.is_python_build():
36 TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build')
37else:
38 TEMPDIR = tempfile.gettempdir()
39TEMPDIR = os.path.abspath(TEMPDIR)
40
41
Victor Stinnerdad20e42015-09-29 22:48:52 +020042def slave_runner(slaveargs):
43 args, kwargs = json.loads(slaveargs)
44 if kwargs.get('huntrleaks'):
45 unittest.BaseTestSuite._cleanup = False
46 try:
47 result = runtest(*args, **kwargs)
48 except KeyboardInterrupt:
49 result = INTERRUPTED, ''
50 except BaseException as e:
51 traceback.print_exc()
52 result = CHILD_ERROR, str(e)
53 sys.stdout.flush()
54 print() # Force a newline (just in case)
55 print(json.dumps(result))
56 sys.exit(0)
57
58
59def setup_python():
60 # Display the Python traceback on fatal errors (e.g. segfault)
61 faulthandler.enable(all_threads=True)
62
63 # Display the Python traceback on SIGALRM or SIGUSR1 signal
64 signals = []
65 if hasattr(signal, 'SIGALRM'):
66 signals.append(signal.SIGALRM)
67 if hasattr(signal, 'SIGUSR1'):
68 signals.append(signal.SIGUSR1)
69 for signum in signals:
70 faulthandler.register(signum, chain=True)
71
72 replace_stdout()
73 support.record_original_stdout(sys.stdout)
74
75 # Some times __path__ and __file__ are not absolute (e.g. while running from
76 # Lib/) and, if we change the CWD to run the tests in a temporary dir, some
77 # imports might fail. This affects only the modules imported before os.chdir().
78 # These modules are searched first in sys.path[0] (so '' -- the CWD) and if
79 # they are found in the CWD their __file__ and __path__ will be relative (this
80 # happens before the chdir). All the modules imported after the chdir, are
81 # not found in the CWD, and since the other paths in sys.path[1:] are absolute
82 # (site.py absolutize them), the __file__ and __path__ will be absolute too.
83 # Therefore it is necessary to absolutize manually the __file__ and __path__ of
84 # the packages to prevent later imports to fail when the CWD is different.
85 for module in sys.modules.values():
86 if hasattr(module, '__path__'):
87 module.__path__ = [os.path.abspath(path) for path in module.__path__]
88 if hasattr(module, '__file__'):
89 module.__file__ = os.path.abspath(module.__file__)
90
91 # MacOSX (a.k.a. Darwin) has a default stack size that is too small
92 # for deeply recursive regular expressions. We see this as crashes in
93 # the Python test suite when running test_re.py and test_sre.py. The
94 # fix is to set the stack limit to 2048.
95 # This approach may also be useful for other Unixy platforms that
96 # suffer from small default stack limits.
97 if sys.platform == 'darwin':
98 try:
99 import resource
100 except ImportError:
101 pass
102 else:
103 soft, hard = resource.getrlimit(resource.RLIMIT_STACK)
104 newsoft = min(hard, max(soft, 1024*2048))
105 resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard))
106
107
108class Regrtest:
Victor Stinner3844fe52015-09-26 10:38:01 +0200109 """Execute a test suite.
110
111 This also parses command-line options and modifies its behavior
112 accordingly.
113
114 tests -- a list of strings containing test names (optional)
115 testdir -- the directory in which to look for tests (optional)
116
117 Users other than the Python test suite will certainly want to
118 specify testdir; if it's omitted, the directory containing the
119 Python test suite is searched for.
120
121 If the tests argument is omitted, the tests listed on the
122 command-line will be used. If that's empty, too, then all *.py
123 files beginning with test_ will be used.
124
125 The other default arguments (verbose, quiet, exclude,
126 single, randomize, findleaks, use_resources, trace, coverdir,
127 print_slow, and random_seed) allow programmers calling main()
128 directly to set the values that would normally be set by flags
129 on the command line.
130 """
Victor Stinnerdad20e42015-09-29 22:48:52 +0200131 def __init__(self):
132 # Namespace of command line options
133 self.ns = None
Victor Stinner3844fe52015-09-26 10:38:01 +0200134
Victor Stinnerdad20e42015-09-29 22:48:52 +0200135 # tests
136 self.tests = []
137 self.selected = []
Victor Stinner3844fe52015-09-26 10:38:01 +0200138
Victor Stinnerdad20e42015-09-29 22:48:52 +0200139 # test results
140 self.good = []
141 self.bad = []
142 self.skipped = []
143 self.resource_denieds = []
144 self.environment_changed = []
145 self.interrupted = False
Victor Stinner3844fe52015-09-26 10:38:01 +0200146
Victor Stinnerdad20e42015-09-29 22:48:52 +0200147 # used by --slow
148 self.test_times = []
Victor Stinner3844fe52015-09-26 10:38:01 +0200149
Victor Stinnerdad20e42015-09-29 22:48:52 +0200150 # used by --coverage, trace.Trace instance
151 self.tracer = None
Victor Stinner3844fe52015-09-26 10:38:01 +0200152
Victor Stinnerdad20e42015-09-29 22:48:52 +0200153 # used by --findleaks, store for gc.garbage
154 self.found_garbage = []
Victor Stinner3844fe52015-09-26 10:38:01 +0200155
Victor Stinnerdad20e42015-09-29 22:48:52 +0200156 # used to display the progress bar "[ 3/100]"
157 self.test_count = ''
158 self.test_count_width = 1
Victor Stinner3844fe52015-09-26 10:38:01 +0200159
Victor Stinnerdad20e42015-09-29 22:48:52 +0200160 # used by --single
161 self.next_single_test = None
162 self.next_single_filename = None
Victor Stinner3844fe52015-09-26 10:38:01 +0200163
Victor Stinnerdad20e42015-09-29 22:48:52 +0200164 def accumulate_result(self, test, result):
Victor Stinner3844fe52015-09-26 10:38:01 +0200165 ok, test_time = result
Victor Stinnerdad20e42015-09-29 22:48:52 +0200166 self.test_times.append((test_time, test))
Victor Stinner3844fe52015-09-26 10:38:01 +0200167 if ok == PASSED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200168 self.good.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200169 elif ok == FAILED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200170 self.bad.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200171 elif ok == ENV_CHANGED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200172 self.environment_changed.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200173 elif ok == SKIPPED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200174 self.skipped.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200175 elif ok == RESOURCE_DENIED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200176 self.skipped.append(test)
177 self.resource_denieds.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200178
Victor Stinnerdad20e42015-09-29 22:48:52 +0200179 def display_progress(self, test_index, test):
180 if self.ns.quiet:
181 return
182 fmt = "[{1:{0}}{2}/{3}] {4}" if self.bad else "[{1:{0}}{2}] {4}"
183 print(fmt.format(
184 self.test_count_width, test_index, self.test_count, len(self.bad), test))
185 sys.stdout.flush()
Victor Stinner3844fe52015-09-26 10:38:01 +0200186
Victor Stinnerdad20e42015-09-29 22:48:52 +0200187 def setup_regrtest(self):
188 if self.ns.huntrleaks:
189 # Avoid false positives due to various caches
190 # filling slowly with random data:
191 warm_caches()
192
193 if self.ns.memlimit is not None:
194 support.set_memlimit(self.ns.memlimit)
195
196 if self.ns.threshold is not None:
197 if gc is not None:
198 gc.set_threshold(self.ns.threshold)
199 else:
200 print('No GC available, ignore --threshold.')
201
202 if self.ns.nowindows:
203 import msvcrt
204 msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS|
205 msvcrt.SEM_NOALIGNMENTFAULTEXCEPT|
206 msvcrt.SEM_NOGPFAULTERRORBOX|
207 msvcrt.SEM_NOOPENFILEERRORBOX)
208 try:
209 msvcrt.CrtSetReportMode
210 except AttributeError:
211 # release build
212 pass
213 else:
214 for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]:
215 msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE)
216 msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR)
217
218 if self.ns.findleaks:
219 if gc is not None:
220 # Uncomment the line below to report garbage that is not
221 # freeable by reference counting alone. By default only
222 # garbage that is not collectable by the GC is reported.
223 pass
224 #gc.set_debug(gc.DEBUG_SAVEALL)
225 else:
226 print('No GC available, disabling --findleaks')
227 self.ns.findleaks = False
228
229 if self.ns.huntrleaks:
230 unittest.BaseTestSuite._cleanup = False
231
232 # Strip .py extensions.
233 removepy(self.ns.args)
234
235 if self.ns.trace:
236 import trace
237 self.tracer = trace.Trace(ignoredirs=[sys.base_prefix,
238 sys.base_exec_prefix,
239 tempfile.gettempdir()],
240 trace=False, count=True)
241
242 def find_tests(self, tests):
243 self.tests = tests
244
245 if self.ns.single:
246 self.next_single_filename = os.path.join(TEMPDIR, 'pynexttest')
247 try:
248 with open(self.next_single_filename, 'r') as fp:
249 next_test = fp.read().strip()
250 self.tests = [next_test]
251 except OSError:
252 pass
253
254 if self.ns.fromfile:
255 self.tests = []
256 with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp:
257 count_pat = re.compile(r'\[\s*\d+/\s*\d+\]')
258 for line in fp:
259 line = count_pat.sub('', line)
260 guts = line.split() # assuming no test has whitespace in its name
261 if guts and not guts[0].startswith('#'):
262 self.tests.extend(guts)
263
264 removepy(self.tests)
265
266 stdtests = STDTESTS[:]
267 nottests = NOTTESTS.copy()
268 if self.ns.exclude:
269 for arg in self.ns.args:
270 if arg in stdtests:
271 stdtests.remove(arg)
272 nottests.add(arg)
273 self.ns.args = []
274
275 # For a partial run, we do not need to clutter the output.
276 if self.ns.verbose or self.ns.header or not (self.ns.quiet or self.ns.single or self.tests or self.ns.args):
277 # Print basic platform information
278 print("==", platform.python_implementation(), *sys.version.split())
279 print("== ", platform.platform(aliased=True),
280 "%s-endian" % sys.byteorder)
281 print("== ", "hash algorithm:", sys.hash_info.algorithm,
282 "64bit" if sys.maxsize > 2**32 else "32bit")
283 print("== ", os.getcwd())
284 print("Testing with flags:", sys.flags)
285
286 # if testdir is set, then we are not running the python tests suite, so
287 # don't add default tests to be executed or skipped (pass empty values)
288 if self.ns.testdir:
289 alltests = findtests(self.ns.testdir, list(), set())
290 else:
291 alltests = findtests(self.ns.testdir, stdtests, nottests)
292
293 self.selected = self.tests or self.ns.args or alltests
294 if self.ns.single:
295 self.selected = self.selected[:1]
296 try:
297 pos = alltests.index(self.selected[0])
298 self.next_single_test = alltests[pos + 1]
299 except IndexError:
300 pass
301
302 # Remove all the self.selected tests that precede start if it's set.
303 if self.ns.start:
304 try:
305 del self.selected[:self.selected.index(self.ns.start)]
306 except ValueError:
307 print("Couldn't find starting test (%s), using all tests" % self.ns.start)
308
309 if self.ns.randomize:
310 if self.ns.random_seed is None:
311 self.ns.random_seed = random.randrange(10000000)
312 random.seed(self.ns.random_seed)
313 print("Using random seed", self.ns.random_seed)
314 random.shuffle(self.selected)
315
316 def display_result(self):
317 if self.interrupted:
318 # print a newline after ^C
319 print()
320 print("Test suite interrupted by signal SIGINT.")
321 omitted = set(self.selected) - set(self.good) - set(self.bad) - set(self.skipped)
322 print(count(len(omitted), "test"), "omitted:")
323 printlist(omitted)
324
325 if self.good and not self.ns.quiet:
326 if not self.bad and not self.skipped and not self.interrupted and len(self.good) > 1:
327 print("All", end=' ')
328 print(count(len(self.good), "test"), "OK.")
329
330 if self.ns.print_slow:
331 self.test_times.sort(reverse=True)
332 print("10 slowest tests:")
333 for time, test in self.test_times[:10]:
334 print("%s: %.1fs" % (test, time))
335
336 if self.bad:
337 print(count(len(self.bad), "test"), "failed:")
338 printlist(self.bad)
339
340 if self.environment_changed:
341 print("{} altered the execution environment:".format(
342 count(len(self.environment_changed), "test")))
343 printlist(self.environment_changed)
344
345 if self.skipped and not self.ns.quiet:
346 print(count(len(self.skipped), "test"), "skipped:")
347 printlist(self.skipped)
348
349 if self.ns.verbose2 and self.bad:
350 print("Re-running failed tests in verbose mode")
351 for test in self.bad[:]:
352 print("Re-running test %r in verbose mode" % test)
353 sys.stdout.flush()
354 try:
355 self.ns.verbose = True
356 ok = runtest(test, True, self.ns.quiet, self.ns.huntrleaks,
357 timeout=self.ns.timeout)
358 except KeyboardInterrupt:
359 # print a newline separate from the ^C
360 print()
361 break
362 else:
363 if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}:
364 self.bad.remove(test)
365 else:
366 if self.bad:
367 print(count(len(self.bad), 'test'), "failed again:")
368 printlist(self.bad)
369
370 def _run_tests_mp(self):
Victor Stinner3844fe52015-09-26 10:38:01 +0200371 try:
372 from threading import Thread
373 except ImportError:
374 print("Multiprocess option requires thread support")
375 sys.exit(2)
376 from queue import Queue
Victor Stinnerdad20e42015-09-29 22:48:52 +0200377
Victor Stinner3844fe52015-09-26 10:38:01 +0200378 debug_output_pat = re.compile(r"\[\d+ refs, \d+ blocks\]$")
379 output = Queue()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200380 pending = MultiprocessTests(self.tests)
381
Victor Stinner3844fe52015-09-26 10:38:01 +0200382 def work():
383 # A worker thread.
384 try:
385 while True:
386 try:
387 test = next(pending)
388 except StopIteration:
389 output.put((None, None, None, None))
390 return
Victor Stinnerdad20e42015-09-29 22:48:52 +0200391 retcode, stdout, stderr = run_test_in_subprocess(test, self.ns)
Victor Stinner3844fe52015-09-26 10:38:01 +0200392 # Strip last refcount output line if it exists, since it
393 # comes from the shutdown of the interpreter in the subcommand.
394 stderr = debug_output_pat.sub("", stderr)
395 stdout, _, result = stdout.strip().rpartition("\n")
396 if retcode != 0:
397 result = (CHILD_ERROR, "Exit code %s" % retcode)
398 output.put((test, stdout.rstrip(), stderr.rstrip(), result))
399 return
400 if not result:
401 output.put((None, None, None, None))
402 return
403 result = json.loads(result)
404 output.put((test, stdout.rstrip(), stderr.rstrip(), result))
405 except BaseException:
406 output.put((None, None, None, None))
407 raise
Victor Stinnerdad20e42015-09-29 22:48:52 +0200408
409 workers = [Thread(target=work) for i in range(self.ns.use_mp)]
Victor Stinner3844fe52015-09-26 10:38:01 +0200410 for worker in workers:
411 worker.start()
412 finished = 0
413 test_index = 1
414 try:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200415 while finished < self.ns.use_mp:
Victor Stinner3844fe52015-09-26 10:38:01 +0200416 test, stdout, stderr, result = output.get()
417 if test is None:
418 finished += 1
419 continue
Victor Stinnerdad20e42015-09-29 22:48:52 +0200420 self.accumulate_result(test, result)
421 self.display_progress(test_index, test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200422 if stdout:
423 print(stdout)
424 if stderr:
425 print(stderr, file=sys.stderr)
426 sys.stdout.flush()
427 sys.stderr.flush()
428 if result[0] == INTERRUPTED:
429 raise KeyboardInterrupt
430 if result[0] == CHILD_ERROR:
431 raise Exception("Child error on {}: {}".format(test, result[1]))
432 test_index += 1
433 except KeyboardInterrupt:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200434 self.interrupted = True
Victor Stinner3844fe52015-09-26 10:38:01 +0200435 pending.interrupted = True
436 for worker in workers:
437 worker.join()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200438
439 def _run_tests_sequential(self):
440 save_modules = sys.modules.keys()
441
442 for test_index, test in enumerate(self.tests, 1):
443 self.display_progress(test_index, test)
444 if self.ns.trace:
Victor Stinner3844fe52015-09-26 10:38:01 +0200445 # If we're tracing code coverage, then we don't exit with status
446 # if on a false return value from main.
Victor Stinnerdad20e42015-09-29 22:48:52 +0200447 cmd = 'runtest(test, self.ns.verbose, self.ns.quiet, timeout=self.ns.timeout)'
448 self.tracer.runctx(cmd, globals=globals(), locals=vars())
Victor Stinner3844fe52015-09-26 10:38:01 +0200449 else:
450 try:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200451 result = runtest(test, self.ns.verbose, self.ns.quiet,
452 self.ns.huntrleaks,
453 output_on_failure=self.ns.verbose3,
454 timeout=self.ns.timeout, failfast=self.ns.failfast,
455 match_tests=self.ns.match_tests)
456 self.accumulate_result(test, result)
Victor Stinner3844fe52015-09-26 10:38:01 +0200457 except KeyboardInterrupt:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200458 self.interrupted = True
Victor Stinner3844fe52015-09-26 10:38:01 +0200459 break
Victor Stinnerdad20e42015-09-29 22:48:52 +0200460 if self.ns.findleaks:
Victor Stinner3844fe52015-09-26 10:38:01 +0200461 gc.collect()
462 if gc.garbage:
463 print("Warning: test created", len(gc.garbage), end=' ')
464 print("uncollectable object(s).")
465 # move the uncollectable objects somewhere so we don't see
466 # them again
Victor Stinnerdad20e42015-09-29 22:48:52 +0200467 self.found_garbage.extend(gc.garbage)
Victor Stinner3844fe52015-09-26 10:38:01 +0200468 del gc.garbage[:]
469 # Unload the newly imported modules (best effort finalization)
470 for module in sys.modules.keys():
471 if module not in save_modules and module.startswith("test."):
472 support.unload(module)
473
Victor Stinnerdad20e42015-09-29 22:48:52 +0200474 def run_tests(self):
475 support.verbose = self.ns.verbose # Tell tests to be moderately quiet
476 support.use_resources = self.ns.use_resources
Victor Stinner3844fe52015-09-26 10:38:01 +0200477
Victor Stinnerdad20e42015-09-29 22:48:52 +0200478 if self.ns.forever:
479 def test_forever(tests):
480 while True:
481 for test in tests:
482 yield test
483 if self.bad:
484 return
485 self.tests = test_forever(list(self.selected))
486 self.test_count = ''
487 self.test_count_width = 3
488 else:
489 self.tests = iter(self.selected)
490 self.test_count = '/{}'.format(len(self.selected))
491 self.test_count_width = len(self.test_count) - 1
492
493 if self.ns.use_mp:
494 self._run_tests_mp()
495 else:
496 self._run_tests_sequential()
497
498 def finalize(self):
499 if self.next_single_filename:
500 if self.next_single_test:
501 with open(self.next_single_filename, 'w') as fp:
502 fp.write(self.next_single_test + '\n')
Victor Stinner3844fe52015-09-26 10:38:01 +0200503 else:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200504 os.unlink(self.next_single_filename)
Victor Stinner3844fe52015-09-26 10:38:01 +0200505
Victor Stinnerdad20e42015-09-29 22:48:52 +0200506 if self.ns.trace:
507 r = self.tracer.results()
508 r.write_results(show_missing=True, summary=True,
509 coverdir=self.ns.coverdir)
Victor Stinner3844fe52015-09-26 10:38:01 +0200510
Victor Stinnerdad20e42015-09-29 22:48:52 +0200511 if self.ns.runleaks:
512 os.system("leaks %d" % os.getpid())
Victor Stinner3844fe52015-09-26 10:38:01 +0200513
Victor Stinnerdad20e42015-09-29 22:48:52 +0200514 def main(self, tests=None, **kwargs):
515 setup_python()
516 self.ns = _parse_args(sys.argv[1:], **kwargs)
517 self.setup_regrtest()
518 if self.ns.wait:
519 input("Press any key to continue...")
520 if self.ns.slaveargs is not None:
521 slave_runner(self.ns.slaveargs)
522 self.find_tests(tests)
523 self.run_tests()
524 self.display_result()
525 self.finalize()
526 sys.exit(len(self.bad) > 0 or self.interrupted)
Victor Stinner3844fe52015-09-26 10:38:01 +0200527
528
529# We do not use a generator so multiple threads can call next().
530class MultiprocessTests(object):
531
532 """A thread-safe iterator over tests for multiprocess mode."""
533
534 def __init__(self, tests):
535 self.interrupted = False
536 self.lock = threading.Lock()
537 self.tests = tests
538
539 def __iter__(self):
540 return self
541
542 def __next__(self):
543 with self.lock:
544 if self.interrupted:
545 raise StopIteration('tests interrupted')
546 return next(self.tests)
547
548
549def replace_stdout():
550 """Set stdout encoder error handler to backslashreplace (as stderr error
551 handler) to avoid UnicodeEncodeError when printing a traceback"""
552 import atexit
553
554 stdout = sys.stdout
555 sys.stdout = open(stdout.fileno(), 'w',
556 encoding=stdout.encoding,
557 errors="backslashreplace",
558 closefd=False,
559 newline='\n')
560
561 def restore_stdout():
562 sys.stdout.close()
563 sys.stdout = stdout
564 atexit.register(restore_stdout)
565
566
567def removepy(names):
568 if not names:
569 return
570 for idx, name in enumerate(names):
571 basename, ext = os.path.splitext(name)
572 if ext == '.py':
573 names[idx] = basename
574
575
576def count(n, word):
577 if n == 1:
578 return "%d %s" % (n, word)
579 else:
580 return "%d %ss" % (n, word)
581
582
583def printlist(x, width=70, indent=4):
584 """Print the elements of iterable x to stdout.
585
586 Optional arg width (default 70) is the maximum line length.
587 Optional arg indent (default 4) is the number of blanks with which to
588 begin each line.
589 """
590
Victor Stinner3844fe52015-09-26 10:38:01 +0200591 blanks = ' ' * indent
592 # Print the sorted list: 'x' may be a '--random' list or a set()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200593 print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
594 initial_indent=blanks, subsequent_indent=blanks))
595
596
597def main(tests=None, **kwargs):
598 Regrtest().main(tests=tests, **kwargs)
Victor Stinner3844fe52015-09-26 10:38:01 +0200599
600
601def main_in_temp_cwd():
602 """Run main() in a temporary working directory."""
603 if sysconfig.is_python_build():
604 try:
605 os.mkdir(TEMPDIR)
606 except FileExistsError:
607 pass
608
609 # Define a writable temp dir that will be used as cwd while running
610 # the tests. The name of the dir includes the pid to allow parallel
611 # testing (see the -j option).
612 test_cwd = 'test_python_{}'.format(os.getpid())
613 test_cwd = os.path.join(TEMPDIR, test_cwd)
614
615 # Run the tests in a context manager that temporarily changes the CWD to a
616 # temporary and writable directory. If it's not possible to create or
617 # change the CWD, the original CWD will be used. The original CWD is
618 # available from support.SAVEDCWD.
619 with support.temp_cwd(test_cwd, quiet=True):
620 main()