blob: 1c99f2b09e094584b715623f505432a1d2e58d3a [file] [log] [blame]
Victor Stinner24f949e2016-03-22 15:14:09 +01001import datetime
Victor Stinner5f9d3ac2015-10-03 00:21:12 +02002import faulthandler
Victor Stinner3844fe52015-09-26 10:38:01 +02003import os
Victor Stinner3844fe52015-09-26 10:38:01 +02004import platform
Victor Stinnerdad20e42015-09-29 22:48:52 +02005import random
6import re
Victor Stinnerdad20e42015-09-29 22:48:52 +02007import sys
8import sysconfig
9import tempfile
10import textwrap
Victor Stinner24f949e2016-03-22 15:14:09 +010011import time
Victor Stinner3909e582015-10-11 10:37:25 +020012from test.libregrtest.cmdline import _parse_args
Victor Stinner3844fe52015-09-26 10:38:01 +020013from test.libregrtest.runtest import (
Victor Stinner6f20a2e2015-09-30 02:32:11 +020014 findtests, runtest,
Victor Stinner3909e582015-10-11 10:37:25 +020015 STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED,
16 INTERRUPTED, CHILD_ERROR)
Victor Stinnera2045022015-09-30 02:17:28 +020017from test.libregrtest.setup import setup_tests
Victor Stinner3844fe52015-09-26 10:38:01 +020018from test import support
19try:
Victor Stinnerdad20e42015-09-29 22:48:52 +020020 import gc
21except ImportError:
22 gc = None
Victor Stinner3844fe52015-09-26 10:38:01 +020023
24
Victor Stinner3844fe52015-09-26 10:38:01 +020025# When tests are run from the Python build directory, it is best practice
26# to keep the test files in a subfolder. This eases the cleanup of leftover
27# files using the "make distclean" command.
28if sysconfig.is_python_build():
29 TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build')
30else:
31 TEMPDIR = tempfile.gettempdir()
32TEMPDIR = os.path.abspath(TEMPDIR)
33
34
Victor Stinnerdad20e42015-09-29 22:48:52 +020035class Regrtest:
Victor Stinner3844fe52015-09-26 10:38:01 +020036 """Execute a test suite.
37
38 This also parses command-line options and modifies its behavior
39 accordingly.
40
41 tests -- a list of strings containing test names (optional)
42 testdir -- the directory in which to look for tests (optional)
43
44 Users other than the Python test suite will certainly want to
45 specify testdir; if it's omitted, the directory containing the
46 Python test suite is searched for.
47
48 If the tests argument is omitted, the tests listed on the
49 command-line will be used. If that's empty, too, then all *.py
50 files beginning with test_ will be used.
51
52 The other default arguments (verbose, quiet, exclude,
53 single, randomize, findleaks, use_resources, trace, coverdir,
54 print_slow, and random_seed) allow programmers calling main()
55 directly to set the values that would normally be set by flags
56 on the command line.
57 """
Victor Stinnerdad20e42015-09-29 22:48:52 +020058 def __init__(self):
59 # Namespace of command line options
60 self.ns = None
Victor Stinner3844fe52015-09-26 10:38:01 +020061
Victor Stinnerdad20e42015-09-29 22:48:52 +020062 # tests
63 self.tests = []
64 self.selected = []
Victor Stinner3844fe52015-09-26 10:38:01 +020065
Victor Stinnerdad20e42015-09-29 22:48:52 +020066 # test results
67 self.good = []
68 self.bad = []
69 self.skipped = []
70 self.resource_denieds = []
71 self.environment_changed = []
72 self.interrupted = False
Victor Stinner3844fe52015-09-26 10:38:01 +020073
Victor Stinnerdad20e42015-09-29 22:48:52 +020074 # used by --slow
75 self.test_times = []
Victor Stinner3844fe52015-09-26 10:38:01 +020076
Victor Stinnerdad20e42015-09-29 22:48:52 +020077 # used by --coverage, trace.Trace instance
78 self.tracer = None
Victor Stinner3844fe52015-09-26 10:38:01 +020079
Victor Stinnerdad20e42015-09-29 22:48:52 +020080 # used by --findleaks, store for gc.garbage
81 self.found_garbage = []
Victor Stinner3844fe52015-09-26 10:38:01 +020082
Victor Stinnerdad20e42015-09-29 22:48:52 +020083 # used to display the progress bar "[ 3/100]"
Victor Stinner24f949e2016-03-22 15:14:09 +010084 self.start_time = time.monotonic()
Victor Stinnerdad20e42015-09-29 22:48:52 +020085 self.test_count = ''
86 self.test_count_width = 1
Victor Stinner3844fe52015-09-26 10:38:01 +020087
Victor Stinnerdad20e42015-09-29 22:48:52 +020088 # used by --single
89 self.next_single_test = None
90 self.next_single_filename = None
Victor Stinner3844fe52015-09-26 10:38:01 +020091
Victor Stinnerdad20e42015-09-29 22:48:52 +020092 def accumulate_result(self, test, result):
Victor Stinner3844fe52015-09-26 10:38:01 +020093 ok, test_time = result
Victor Stinner3909e582015-10-11 10:37:25 +020094 if ok not in (CHILD_ERROR, INTERRUPTED):
95 self.test_times.append((test_time, test))
Victor Stinner3844fe52015-09-26 10:38:01 +020096 if ok == PASSED:
Victor Stinnerdad20e42015-09-29 22:48:52 +020097 self.good.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +020098 elif ok == FAILED:
Victor Stinnerdad20e42015-09-29 22:48:52 +020099 self.bad.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200100 elif ok == ENV_CHANGED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200101 self.environment_changed.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200102 elif ok == SKIPPED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200103 self.skipped.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200104 elif ok == RESOURCE_DENIED:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200105 self.skipped.append(test)
106 self.resource_denieds.append(test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200107
Victor Stinner24f949e2016-03-22 15:14:09 +0100108 def time_delta(self):
109 seconds = time.monotonic() - self.start_time
110 return datetime.timedelta(seconds=int(seconds))
111
Victor Stinnerdad20e42015-09-29 22:48:52 +0200112 def display_progress(self, test_index, test):
113 if self.ns.quiet:
114 return
Brett Cannon11faa212015-10-02 16:20:49 -0700115 if self.bad and not self.ns.pgo:
Victor Stinner24f949e2016-03-22 15:14:09 +0100116 fmt = "{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}"
Brett Cannon11faa212015-10-02 16:20:49 -0700117 else:
Victor Stinner24f949e2016-03-22 15:14:09 +0100118 fmt = "{time} [{test_index:{count_width}}{test_count}] {test_name}"
119 line = fmt.format(count_width=self.test_count_width,
120 test_index=test_index,
121 test_count=self.test_count,
122 nbad=len(self.bad),
123 test_name=test,
124 time=self.time_delta())
125 print(line, flush=True)
Victor Stinner3844fe52015-09-26 10:38:01 +0200126
Victor Stinner234cbef2015-09-30 01:13:53 +0200127 def parse_args(self, kwargs):
128 ns = _parse_args(sys.argv[1:], **kwargs)
129
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200130 if ns.timeout and not hasattr(faulthandler, 'dump_traceback_later'):
131 print("Warning: The timeout option requires "
132 "faulthandler.dump_traceback_later", file=sys.stderr)
133 ns.timeout = None
134
Victor Stinner234cbef2015-09-30 01:13:53 +0200135 if ns.threshold is not None and gc is None:
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200136 print('No GC available, ignore --threshold.', file=sys.stderr)
Victor Stinner234cbef2015-09-30 01:13:53 +0200137 ns.threshold = None
138
139 if ns.findleaks:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200140 if gc is not None:
141 # Uncomment the line below to report garbage that is not
142 # freeable by reference counting alone. By default only
143 # garbage that is not collectable by the GC is reported.
144 pass
145 #gc.set_debug(gc.DEBUG_SAVEALL)
146 else:
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200147 print('No GC available, disabling --findleaks',
148 file=sys.stderr)
Victor Stinner234cbef2015-09-30 01:13:53 +0200149 ns.findleaks = False
Victor Stinnerdad20e42015-09-29 22:48:52 +0200150
Victor Stinnerdad20e42015-09-29 22:48:52 +0200151 # Strip .py extensions.
Victor Stinner234cbef2015-09-30 01:13:53 +0200152 removepy(ns.args)
153
154 return ns
Victor Stinnerdad20e42015-09-29 22:48:52 +0200155
Victor Stinnerdad20e42015-09-29 22:48:52 +0200156 def find_tests(self, tests):
157 self.tests = tests
158
159 if self.ns.single:
160 self.next_single_filename = os.path.join(TEMPDIR, 'pynexttest')
161 try:
162 with open(self.next_single_filename, 'r') as fp:
163 next_test = fp.read().strip()
164 self.tests = [next_test]
165 except OSError:
166 pass
167
168 if self.ns.fromfile:
169 self.tests = []
170 with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp:
171 count_pat = re.compile(r'\[\s*\d+/\s*\d+\]')
172 for line in fp:
173 line = count_pat.sub('', line)
174 guts = line.split() # assuming no test has whitespace in its name
175 if guts and not guts[0].startswith('#'):
176 self.tests.extend(guts)
177
178 removepy(self.tests)
179
180 stdtests = STDTESTS[:]
181 nottests = NOTTESTS.copy()
182 if self.ns.exclude:
183 for arg in self.ns.args:
184 if arg in stdtests:
185 stdtests.remove(arg)
186 nottests.add(arg)
187 self.ns.args = []
188
Victor Stinnerdad20e42015-09-29 22:48:52 +0200189 # if testdir is set, then we are not running the python tests suite, so
190 # don't add default tests to be executed or skipped (pass empty values)
191 if self.ns.testdir:
192 alltests = findtests(self.ns.testdir, list(), set())
193 else:
194 alltests = findtests(self.ns.testdir, stdtests, nottests)
195
196 self.selected = self.tests or self.ns.args or alltests
197 if self.ns.single:
198 self.selected = self.selected[:1]
199 try:
200 pos = alltests.index(self.selected[0])
201 self.next_single_test = alltests[pos + 1]
202 except IndexError:
203 pass
204
Victor Stinnerc7eab052015-09-30 00:59:35 +0200205 # Remove all the selected tests that precede start if it's set.
Victor Stinnerdad20e42015-09-29 22:48:52 +0200206 if self.ns.start:
207 try:
208 del self.selected[:self.selected.index(self.ns.start)]
209 except ValueError:
Victor Stinner6448b802015-09-29 23:43:33 +0200210 print("Couldn't find starting test (%s), using all tests"
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200211 % self.ns.start, file=sys.stderr)
Victor Stinnerdad20e42015-09-29 22:48:52 +0200212
213 if self.ns.randomize:
214 if self.ns.random_seed is None:
215 self.ns.random_seed = random.randrange(10000000)
216 random.seed(self.ns.random_seed)
Victor Stinnerdad20e42015-09-29 22:48:52 +0200217 random.shuffle(self.selected)
218
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200219 def list_tests(self):
220 for name in self.selected:
221 print(name)
222
Victor Stinner6f20a2e2015-09-30 02:32:11 +0200223 def rerun_failed_tests(self):
224 self.ns.verbose = True
225 self.ns.failfast = False
226 self.ns.verbose3 = False
227 self.ns.match_tests = None
228
229 print("Re-running failed tests in verbose mode")
230 for test in self.bad[:]:
231 print("Re-running test %r in verbose mode" % test, flush=True)
232 try:
233 self.ns.verbose = True
234 ok = runtest(self.ns, test)
235 except KeyboardInterrupt:
236 # print a newline separate from the ^C
237 print()
238 break
239 else:
240 if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}:
241 self.bad.remove(test)
242 else:
243 if self.bad:
244 print(count(len(self.bad), 'test'), "failed again:")
245 printlist(self.bad)
246
Victor Stinnerdad20e42015-09-29 22:48:52 +0200247 def display_result(self):
248 if self.interrupted:
249 # print a newline after ^C
250 print()
251 print("Test suite interrupted by signal SIGINT.")
Victor Stinner6448b802015-09-29 23:43:33 +0200252 executed = set(self.good) | set(self.bad) | set(self.skipped)
253 omitted = set(self.selected) - executed
Victor Stinnerdad20e42015-09-29 22:48:52 +0200254 print(count(len(omitted), "test"), "omitted:")
255 printlist(omitted)
256
Brett Cannon11faa212015-10-02 16:20:49 -0700257 # If running the test suite for PGO then no one cares about
258 # results.
259 if self.ns.pgo:
260 return
261
Victor Stinnerdad20e42015-09-29 22:48:52 +0200262 if self.good and not self.ns.quiet:
Victor Stinner6448b802015-09-29 23:43:33 +0200263 if (not self.bad
264 and not self.skipped
265 and not self.interrupted
266 and len(self.good) > 1):
Victor Stinnerdad20e42015-09-29 22:48:52 +0200267 print("All", end=' ')
268 print(count(len(self.good), "test"), "OK.")
269
270 if self.ns.print_slow:
271 self.test_times.sort(reverse=True)
272 print("10 slowest tests:")
273 for time, test in self.test_times[:10]:
274 print("%s: %.1fs" % (test, time))
275
276 if self.bad:
277 print(count(len(self.bad), "test"), "failed:")
278 printlist(self.bad)
279
280 if self.environment_changed:
281 print("{} altered the execution environment:".format(
282 count(len(self.environment_changed), "test")))
283 printlist(self.environment_changed)
284
285 if self.skipped and not self.ns.quiet:
286 print(count(len(self.skipped), "test"), "skipped:")
287 printlist(self.skipped)
288
Victor Stinnerbd1a72c2015-09-29 23:36:27 +0200289 def run_tests_sequential(self):
Victor Stinnerc7eab052015-09-30 00:59:35 +0200290 if self.ns.trace:
291 import trace
Victor Stinnera53a8182015-10-01 00:53:09 +0200292 self.tracer = trace.Trace(trace=False, count=True)
Victor Stinnerc7eab052015-09-30 00:59:35 +0200293
Victor Stinnerdad20e42015-09-29 22:48:52 +0200294 save_modules = sys.modules.keys()
295
296 for test_index, test in enumerate(self.tests, 1):
297 self.display_progress(test_index, test)
Victor Stinnerc7eab052015-09-30 00:59:35 +0200298 if self.tracer:
Victor Stinner3844fe52015-09-26 10:38:01 +0200299 # If we're tracing code coverage, then we don't exit with status
300 # if on a false return value from main.
Victor Stinner6f20a2e2015-09-30 02:32:11 +0200301 cmd = ('result = runtest(self.ns, test); '
302 'self.accumulate_result(test, result)')
Victor Stinnerdad20e42015-09-29 22:48:52 +0200303 self.tracer.runctx(cmd, globals=globals(), locals=vars())
Victor Stinner3844fe52015-09-26 10:38:01 +0200304 else:
305 try:
Victor Stinner6f20a2e2015-09-30 02:32:11 +0200306 result = runtest(self.ns, test)
Victor Stinner3844fe52015-09-26 10:38:01 +0200307 except KeyboardInterrupt:
Victor Stinner3909e582015-10-11 10:37:25 +0200308 self.accumulate_result(test, (INTERRUPTED, None))
Victor Stinnerdad20e42015-09-29 22:48:52 +0200309 self.interrupted = True
Victor Stinner3844fe52015-09-26 10:38:01 +0200310 break
Victor Stinner3909e582015-10-11 10:37:25 +0200311 else:
312 self.accumulate_result(test, result)
Victor Stinnerbd1a72c2015-09-29 23:36:27 +0200313
Victor Stinnerdad20e42015-09-29 22:48:52 +0200314 if self.ns.findleaks:
Victor Stinner3844fe52015-09-26 10:38:01 +0200315 gc.collect()
316 if gc.garbage:
317 print("Warning: test created", len(gc.garbage), end=' ')
318 print("uncollectable object(s).")
319 # move the uncollectable objects somewhere so we don't see
320 # them again
Victor Stinnerdad20e42015-09-29 22:48:52 +0200321 self.found_garbage.extend(gc.garbage)
Victor Stinner3844fe52015-09-26 10:38:01 +0200322 del gc.garbage[:]
Victor Stinnerbd1a72c2015-09-29 23:36:27 +0200323
Victor Stinner3844fe52015-09-26 10:38:01 +0200324 # Unload the newly imported modules (best effort finalization)
325 for module in sys.modules.keys():
326 if module not in save_modules and module.startswith("test."):
327 support.unload(module)
328
Victor Stinnerb4084352015-09-30 02:39:22 +0200329 def _test_forever(self, tests):
330 while True:
331 for test in tests:
332 yield test
333 if self.bad:
334 return
Victor Stinner3844fe52015-09-26 10:38:01 +0200335
Victor Stinnerb4084352015-09-30 02:39:22 +0200336 def run_tests(self):
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200337 # For a partial run, we do not need to clutter the output.
338 if (self.ns.verbose
339 or self.ns.header
Brett Cannon11faa212015-10-02 16:20:49 -0700340 or not (self.ns.pgo or self.ns.quiet or self.ns.single
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200341 or self.tests or self.ns.args)):
342 # Print basic platform information
343 print("==", platform.python_implementation(), *sys.version.split())
344 print("== ", platform.platform(aliased=True),
345 "%s-endian" % sys.byteorder)
346 print("== ", "hash algorithm:", sys.hash_info.algorithm,
347 "64bit" if sys.maxsize > 2**32 else "32bit")
348 print("== ", os.getcwd())
349 print("Testing with flags:", sys.flags)
350
351 if self.ns.randomize:
352 print("Using random seed", self.ns.random_seed)
353
Victor Stinnerdad20e42015-09-29 22:48:52 +0200354 if self.ns.forever:
Victor Stinner9a142142015-09-30 13:51:17 +0200355 self.tests = self._test_forever(list(self.selected))
Victor Stinnerdad20e42015-09-29 22:48:52 +0200356 self.test_count = ''
357 self.test_count_width = 3
358 else:
359 self.tests = iter(self.selected)
360 self.test_count = '/{}'.format(len(self.selected))
361 self.test_count_width = len(self.test_count) - 1
362
363 if self.ns.use_mp:
Victor Stinner56e05dd2015-09-29 23:15:38 +0200364 from test.libregrtest.runtest_mp import run_tests_multiprocess
365 run_tests_multiprocess(self)
Victor Stinnerdad20e42015-09-29 22:48:52 +0200366 else:
Victor Stinnerbd1a72c2015-09-29 23:36:27 +0200367 self.run_tests_sequential()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200368
369 def finalize(self):
370 if self.next_single_filename:
371 if self.next_single_test:
372 with open(self.next_single_filename, 'w') as fp:
373 fp.write(self.next_single_test + '\n')
Victor Stinner3844fe52015-09-26 10:38:01 +0200374 else:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200375 os.unlink(self.next_single_filename)
Victor Stinner3844fe52015-09-26 10:38:01 +0200376
Victor Stinnerc7eab052015-09-30 00:59:35 +0200377 if self.tracer:
Victor Stinnerdad20e42015-09-29 22:48:52 +0200378 r = self.tracer.results()
379 r.write_results(show_missing=True, summary=True,
380 coverdir=self.ns.coverdir)
Victor Stinner3844fe52015-09-26 10:38:01 +0200381
Victor Stinner24f949e2016-03-22 15:14:09 +0100382 print("Total duration: %s" % self.time_delta())
383
Victor Stinnerdad20e42015-09-29 22:48:52 +0200384 if self.ns.runleaks:
385 os.system("leaks %d" % os.getpid())
Victor Stinner3844fe52015-09-26 10:38:01 +0200386
Victor Stinnerdad20e42015-09-29 22:48:52 +0200387 def main(self, tests=None, **kwargs):
Victor Stinner234cbef2015-09-30 01:13:53 +0200388 self.ns = self.parse_args(kwargs)
389
Victor Stinnerdad20e42015-09-29 22:48:52 +0200390 if self.ns.slaveargs is not None:
Victor Stinner56e05dd2015-09-29 23:15:38 +0200391 from test.libregrtest.runtest_mp import run_tests_slave
392 run_tests_slave(self.ns.slaveargs)
Victor Stinnerecef6222015-09-30 01:39:28 +0200393
Victor Stinnerc7eab052015-09-30 00:59:35 +0200394 if self.ns.wait:
395 input("Press any key to continue...")
396
Victor Stinnera2045022015-09-30 02:17:28 +0200397 setup_tests(self.ns)
Victor Stinnerecef6222015-09-30 01:39:28 +0200398
Victor Stinnerdad20e42015-09-29 22:48:52 +0200399 self.find_tests(tests)
Victor Stinnerc7eab052015-09-30 00:59:35 +0200400
Victor Stinner5f9d3ac2015-10-03 00:21:12 +0200401 if self.ns.list_tests:
402 self.list_tests()
403 sys.exit(0)
404
405 self.run_tests()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200406 self.display_result()
Victor Stinner6f20a2e2015-09-30 02:32:11 +0200407
408 if self.ns.verbose2 and self.bad:
409 self.rerun_failed_tests()
410
Victor Stinnerdad20e42015-09-29 22:48:52 +0200411 self.finalize()
412 sys.exit(len(self.bad) > 0 or self.interrupted)
Victor Stinner3844fe52015-09-26 10:38:01 +0200413
414
Victor Stinner3844fe52015-09-26 10:38:01 +0200415def removepy(names):
416 if not names:
417 return
418 for idx, name in enumerate(names):
419 basename, ext = os.path.splitext(name)
420 if ext == '.py':
421 names[idx] = basename
422
423
424def count(n, word):
425 if n == 1:
426 return "%d %s" % (n, word)
427 else:
428 return "%d %ss" % (n, word)
429
430
431def printlist(x, width=70, indent=4):
432 """Print the elements of iterable x to stdout.
433
434 Optional arg width (default 70) is the maximum line length.
435 Optional arg indent (default 4) is the number of blanks with which to
436 begin each line.
437 """
438
Victor Stinner3844fe52015-09-26 10:38:01 +0200439 blanks = ' ' * indent
440 # Print the sorted list: 'x' may be a '--random' list or a set()
Victor Stinnerdad20e42015-09-29 22:48:52 +0200441 print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
442 initial_indent=blanks, subsequent_indent=blanks))
443
444
445def main(tests=None, **kwargs):
446 Regrtest().main(tests=tests, **kwargs)
Victor Stinner3844fe52015-09-26 10:38:01 +0200447
448
449def main_in_temp_cwd():
450 """Run main() in a temporary working directory."""
451 if sysconfig.is_python_build():
452 try:
453 os.mkdir(TEMPDIR)
454 except FileExistsError:
455 pass
456
457 # Define a writable temp dir that will be used as cwd while running
458 # the tests. The name of the dir includes the pid to allow parallel
459 # testing (see the -j option).
460 test_cwd = 'test_python_{}'.format(os.getpid())
461 test_cwd = os.path.join(TEMPDIR, test_cwd)
462
463 # Run the tests in a context manager that temporarily changes the CWD to a
464 # temporary and writable directory. If it's not possible to create or
465 # change the CWD, the original CWD will be used. The original CWD is
466 # available from support.SAVEDCWD.
467 with support.temp_cwd(test_cwd, quiet=True):
468 main()