[Python-Dev] [Python-checkins] cpython: Issue #16799: Switched from getopt to argparse style in regrtest's argument (original) (raw)

Eli Bendersky eliben at gmail.com
Thu Aug 29 18:50:08 CEST 2013


Great job, Serhiy. In general, eating our own dogfood is a great idea. The more we use new Python features in our own stuff, the better.

Eli

On Thu, Aug 29, 2013 at 2:27 AM, serhiy.storchaka < python-checkins at python.org> wrote:

http://hg.python.org/cpython/rev/997de0edc5bd changeset: 85444:997de0edc5bd parent: 85442:676bbd5b0254 user: Serhiy Storchaka <storchaka at gmail.com> date: Thu Aug 29 12:26:23 2013 +0300 summary: Issue #16799: Switched from getopt to argparse style in regrtest's argument parsing. Added more tests for regrtest's argument parsing.

files: Lib/test/regrtest.py | 529 +++++++++++-------------- Lib/test/testregrtest.py | 328 ++++++++++++--- Misc/NEWS | 3 + 3 files changed, 500 insertions(+), 360 deletions(-)

diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -233,18 +233,20 @@ # We add help explicitly to control what argument group it renders under. group.addargument('-h', '--help', action='help', help='show this help message and exit') - group.addargument('--timeout', metavar='TIMEOUT', + group.addargument('--timeout', metavar='TIMEOUT', type=float, help='dump the traceback and exit if a test takes ' 'more than TIMEOUT seconds; disabled if TIMEOUT ' 'is negative or equals to zero') - group.addargument('--wait', action='storetrue', help='wait for user ' - 'input, e.g., allow a debugger to be attached') + group.addargument('--wait', action='storetrue', + help='wait for user input, e.g., allow a debugger ' + 'to be attached') group.addargument('--slaveargs', metavar='ARGS') - group.addargument('-S', '--start', metavar='START', help='the name of ' - 'the test at which to start.' + moredetails) + group.addargument('-S', '--start', metavar='START', + help='the name of the test at which to start.' + + moredetails) group = parser.addargumentgroup('Verbosity') - group.addargument('-v', '--verbose', action='storetrue', + group.addargument('-v', '--verbose', action='count', help='run tests in verbose mode with output to stdout') group.addargument('-w', '--verbose2', action='storetrue', help='re-run failed tests in verbose mode') @@ -254,7 +256,7 @@ help='print traceback for failed tests') group.addargument('-q', '--quiet', action='storetrue', help='no output unless one or more tests fail') - group.addargument('-o', '--slow', action='storetrue', + group.addargument('-o', '--slow', action='storetrue', dest='printslow', help='print the slowest 10 tests') group.addargument('--header', action='storetrue', help='print header with interpreter info') @@ -262,45 +264,60 @@ group = parser.addargumentgroup('Selecting tests') group.addargument('-r', '--randomize', action='storetrue', help='randomize test execution order.' + moredetails) - group.addargument('--randseed', metavar='SEED', help='pass a random seed ' - 'to reproduce a previous random run') - group.addargument('-f', '--fromfile', metavar='FILE', help='read names ' - 'of tests to run from a file.' + moredetails) + group.addargument('--randseed', metavar='SEED', + dest='randomseed', type=int, + help='pass a random seed to reproduce a previous ' + 'random run') + group.addargument('-f', '--fromfile', metavar='FILE', + help='read names of tests to run from a file.' + + moredetails) group.addargument('-x', '--exclude', action='storetrue', help='arguments are tests to exclude') - group.addargument('-s', '--single', action='storetrue', help='single ' - 'step through a set of tests.' + moredetails) - group.addargument('-m', '--match', metavar='PAT', help='match test cases ' - 'and methods with glob pattern PAT') - group.addargument('-G', '--failfast', action='storetrue', help='fail as ' - 'soon as a test fails (only with -v or -W)') - group.addargument('-u', '--use', metavar='RES1,RES2,...', help='specify ' - 'which special resource intensive tests to run.' + - moredetails) - group.addargument('-M', '--memlimit', metavar='LIMIT', help='run very ' - 'large memory-consuming tests.' + moredetails) + group.addargument('-s', '--single', action='storetrue', + help='single step through a set of tests.' + + moredetails) + group.addargument('-m', '--match', metavar='PAT', + dest='matchtests', + help='match test cases and methods with glob pattern PAT') + group.addargument('-G', '--failfast', action='storetrue', + help='fail as soon as a test fails (only with -v or -W)') + group.addargument('-u', '--use', metavar='RES1,RES2,...', + action='append', type=resourceslist, + help='specify which special resource intensive tests ' + 'to run.' + moredetails) + group.addargument('-M', '--memlimit', metavar='LIMIT', + help='run very large memory-consuming tests.' + + moredetails) group.addargument('--testdir', metavar='DIR', + type=relativefilename, help='execute test files in the specified directory ' '(instead of the Python stdlib test suite)') group = parser.addargumentgroup('Special runs') - group.addargument('-l', '--findleaks', action='storetrue', help='if GC ' - 'is available detect tests that leak memory') + group.addargument('-l', '--findleaks', action='storetrue', + help='if GC is available detect tests that leak memory') group.addargument('-L', '--runleaks', action='storetrue', help='run the leaks(1) command just before exit.' + - moredetails) + moredetails) group.addargument('-R', '--huntrleaks', metavar='RUNCOUNTS', + type=huntrleaks, help='search for reference leaks (needs debug build, ' 'very slow).' + moredetails) group.addargument('-j', '--multiprocess', metavar='PROCESSES', + dest='usemp', type=int, help='run PROCESSES processes at once') - group.addargument('-T', '--coverage', action='storetrue', help='turn on ' - 'code coverage tracing using the trace module') + group.addargument('-T', '--coverage', action='storetrue', + dest='trace', + help='turn on code coverage tracing using the trace ' + 'module') group.addargument('-D', '--coverdir', metavar='DIR', + type=relativefilename, help='directory where coverage files are put') - group.addargument('-N', '--nocoverdir', action='storetrue', + group.addargument('-N', '--nocoverdir', + action='storeconst', const=None, dest='coverdir', help='put coverage files alongside modules') group.addargument('-t', '--threshold', metavar='THRESHOLD', + type=int, help='call gc.setthreshold(THRESHOLD)') group.addargument('-n', '--nowindows', action='storetrue', help='suppress error message boxes on Windows') @@ -313,43 +330,103 @@ return parser -# TODO: remove this function as described in issue #16799, for example. -# We use this function since regrtest.main() was originally written to use -# getopt for parsing. -def convertnamespacetogetopt(ns): - """Convert an argparse.Namespace object to a getopt-style opts list. +def relativefilename(string): + # CWD is replaced with a temporary dir before calling main(), so we + # join it with the saved CWD so it ends up where the user expects. + return os.path.join(support.SAVEDCWD, string) - The return value of this function mimics the first element of - getopt.getopt()'s (opts, args) return value. In addition, the (option, - value) pairs in the opts list are sorted by option and use the long - option string. The args part of (opts, args) can be mimicked by the - args attribute of the Namespace object we are using in regrtest. - """ - opts = [] - argsdict = vars(ns) - for key in sorted(argsdict.keys()): - if key == 'args': +def huntrleaks(string): + args = string.split(':') + if len(args) not in (2, 3): + raise argparse.ArgumentTypeError( + 'needs 2 or 3 colon-separated arguments') + nwarmup = int(args[0]) if args[0] else 5 + ntracked = int(args[1]) if args[1] else 4 + fname = args[2] if len(args) > 2 and args[2] else 'reflog.txt' + return nwarmup, ntracked, fname + +def resourceslist(string): + u = [x.lower() for x in string.split(',')] + for r in u: + if r == 'all' or r == 'none': continue - val = argsdict[key] - # Don't continue if val equals '' because this means an option - # accepting a value was provided the empty string. Such values should - # show up in the returned opts list. - if val is None or val is False: - continue - if val is True: - # Then an option with action storetrue was passed. getopt - # includes these with value '' in the opts list. - val = '' - opts.append(('--' + key, val)) - return opts + if r[0] == '-': + r = r[1:] + if r not in RESOURCENAMES: + raise argparse.ArgumentTypeError('invalid resource: ' + r) + return u - -def main(tests=None, testdir=None, verbose=0, quiet=False, +def parseargs(args, **kwargs): + # Defaults + ns = argparse.Namespace(testdir=None, verbose=0, quiet=False, exclude=False, single=False, randomize=False, fromfile=None, findleaks=False, useresources=None, trace=False, coverdir='coverage', runleaks=False, huntrleaks=False, verbose2=False, printslow=False, randomseed=None, usemp=None, verbose3=False, forever=False, - header=False, failfast=False, matchtests=None): + header=False, failfast=False, matchtests=None) + for k, v in kwargs.items(): + if not hasattr(ns, k): + raise TypeError('%r is an invalid keyword argument ' + 'for this function' % k) + setattr(ns, k, v) + if ns.useresources is None: + ns.useresources = [] + + parser = createparser() + parser.parseargs(args=args, namespace=ns) + + if ns.single and ns.fromfile: + parser.error("-s and -f don't go together!") + if ns.usemp and ns.trace: + parser.error("-T and -j don't go together!") + if ns.usemp and ns.findleaks: + parser.error("-l and -j don't go together!") + if ns.usemp and ns.memlimit: + parser.error("-M and -j don't go together!") + if ns.failfast and not (ns.verbose or ns.verbose3): + parser.error("-G/--failfast needs either -v or -W") + + if ns.quiet: + ns.verbose = 0 + if ns.timeout is not None: + if hasattr(faulthandler, 'dumptracebacklater'): + if ns.timeout <= 0:_ _+ ns.timeout = None_ _+ else:_ _+ print("Warning: The timeout option requires "_ _+ "faulthandler.dumptracebacklater")_ _+ ns.timeout = None_ _+ if ns.usemp is not None:_ _+ if ns.usemp <= 0:_ _+ # Use all cores + extras for tests that like to sleep_ _+ ns.usemp = 2 + (os.cpucount() or 1)_ _+ if ns.usemp == 1:_ _+ ns.usemp = None_ _+ if ns.use:_ _+ for a in ns.use:_ _+ for r in a:_ _+ if r == 'all':_ _+ ns.useresources[:] = RESOURCENAMES_ _+ continue_ _+ if r == 'none':_ _+ del ns.useresources[:]_ _+ continue_ _+ remove = False_ _+ if r[0] == '-':_ _+ remove = True_ _+ r = r[1:]_ _+ if remove:_ _+ if r in ns.useresources:_ _+ ns.useresources.remove(r)_ _+ elif r not in ns.useresources:_ _+ ns.useresources.append(r)_ _+ if ns.randomseed is not None:_ _+ ns.randomize = True_ _+_ _+ return ns_ _+_ _+_ _+def main(tests=None, **kwargs):_ _"""Execute a test suite._ _This also parses command-line options and modifies its behavior_ _@@ -372,7 +449,6 @@_ _directly to set the values that would normally be set by flags_ _on the command line._ _"""_ _-_ _# Display the Python traceback on fatal errors (e.g. segfault)_ _faulthandler.enable(allthreads=True)_ _@@ -389,174 +465,48 @@_ _support.recordoriginalstdout(sys.stdout)_ _- parser = createparser()_ _- ns = parser.parseargs()_ _- opts = convertnamespacetogetopt(ns)_ _- args = ns.args_ _- usage = parser.error_ _+ ns = parseargs(sys.argv[1:], **kwargs)_ _- # Defaults_ _- if randomseed is None:_ _- randomseed = random.randrange(10000000)_ _- if useresources is None:_ _- useresources = []_ _- debug = False_ _- start = None_ _- timeout = None_ _- for o, a in opts:_ _- if o in ('-v', '--verbose'):_ _- verbose += 1_ _- elif o in ('-w', '--verbose2'):_ _- verbose2 = True_ _- elif o in ('-d', '--debug'):_ _- debug = True_ _- elif o in ('-W', '--verbose3'):_ _- verbose3 = True_ _- elif o in ('-G', '--failfast'):_ _- failfast = True_ _- elif o in ('-q', '--quiet'):_ _- quiet = True;_ _- verbose = 0_ _- elif o in ('-x', '--exclude'):_ _- exclude = True_ _- elif o in ('-S', '--start'):_ _- start = a_ _- elif o in ('-s', '--single'):_ _- single = True_ _- elif o in ('-o', '--slow'):_ _- printslow = True_ _- elif o in ('-r', '--randomize'):_ _- randomize = True_ _- elif o == '--randseed':_ _- randomize = True_ _- randomseed = int(a)_ _- elif o in ('-f', '--fromfile'):_ _- fromfile = a_ _- elif o in ('-m', '--match'):_ _- matchtests = a_ _- elif o in ('-l', '--findleaks'):_ _- findleaks = True_ _- elif o in ('-L', '--runleaks'):_ _- runleaks = True_ _- elif o in ('-t', '--threshold'):_ _- import gc_ _- gc.setthreshold(int(a))_ _- elif o in ('-T', '--coverage'):_ _- trace = True_ _- elif o in ('-D', '--coverdir'):_ _- # CWD is replaced with a temporary dir before calling main(),_ _so we_ _- # need join it with the saved CWD so it goes where the user_ _expects._ _- coverdir = os.path.join(support.SAVEDCWD, a)_ _- elif o in ('-N', '--nocoverdir'):_ _- coverdir = None_ _- elif o in ('-R', '--huntrleaks'):_ _- huntrleaks = a.split(':')_ _- if len(huntrleaks) not in (2, 3):_ _- print(a, huntrleaks)_ _- usage('-R takes 2 or 3 colon-separated arguments')_ _- if not huntrleaks[0]:_ _- huntrleaks[0] = 5_ _- else:_ _- huntrleaks[0] = int(huntrleaks[0])_ _- if not huntrleaks[1]:_ _- huntrleaks[1] = 4_ _- else:_ _- huntrleaks[1] = int(huntrleaks[1])_ _- if len(huntrleaks) == 2 or not huntrleaks[2]:_ _- huntrleaks[2:] = ["reflog.txt"]_ _- # Avoid false positives due to various caches_ _- # filling slowly with random data:_ _- warmcaches()_ _- elif o in ('-M', '--memlimit'):_ _- support.setmemlimit(a)_ _- elif o in ('-u', '--use'):_ _- u = [x.lower() for x in a.split(',')]_ _- for r in u:_ _- if r == 'all':_ _- useresources[:] = RESOURCENAMES_ _- continue_ _- if r == 'none':_ _- del useresources[:]_ _- continue_ _- remove = False_ _- if r[0] == '-':_ _- remove = True_ _- r = r[1:]_ _- if r not in RESOURCENAMES:_ _- usage('Invalid -u/--use option: ' + a)_ _- if remove:_ _- if r in useresources:_ _- useresources.remove(r)_ _- elif r not in useresources:_ _- useresources.append(r)_ _- elif o in ('-n', '--nowindows'):_ _- import msvcrt_ _- msvcrt.SetErrorMode(msvcrt.SEMFAILCRITICALERRORS|_ _- msvcrt.SEMNOALIGNMENTFAULTEXCEPT|_ _- msvcrt.SEMNOGPFAULTERRORBOX|_ _- msvcrt.SEMNOOPENFILEERRORBOX)_ _- try:_ _- msvcrt.CrtSetReportMode_ _- except AttributeError:_ _- # release build_ _- pass_ _- else:_ _- for m in [msvcrt.CRTWARN, msvcrt.CRTERROR,_ _msvcrt.CRTASSERT]:_ _- msvcrt.CrtSetReportMode(m, msvcrt.CRTDBGMODEFILE)_ _- msvcrt.CrtSetReportFile(m, msvcrt.CRTDBGFILESTDERR)_ _- elif o in ('-F', '--forever'):_ _- forever = True_ _- elif o in ('-j', '--multiprocess'):_ _- usemp = int(a)_ _- if usemp <= 0:_ _- # Use all cores + extras for tests that like to sleep_ _- usemp = 2 + (os.cpucount() or 1)_ _- if usemp == 1:_ _- usemp = None_ _- elif o == '--header':_ _- header = True_ _- elif o == '--slaveargs':_ _- args, kwargs = json.loads(a)_ _- try:_ _- result = runtest(*args, **kwargs)_ _- except KeyboardInterrupt:_ _- result = INTERRUPTED, ''_ _- except BaseException as e:_ _- traceback.printexc()_ _- result = CHILDERROR, str(e)_ _- sys.stdout.flush()_ _- print() # Force a newline (just in case)_ _- print(json.dumps(result))_ _- sys.exit(0)_ _- elif o == '--testdir':_ _- # CWD is replaced with a temporary dir before calling main(),_ _so we_ _- # join it with the saved CWD so it ends up where the user_ _expects._ _- testdir = os.path.join(support.SAVEDCWD, a)_ _- elif o == '--timeout':_ _- if hasattr(faulthandler, 'dumptracebacklater'):_ _- timeout = float(a)_ _- if timeout <= 0:_ _- timeout = None_ _- else:_ _- print("Warning: The timeout option requires "_ _- "faulthandler.dumptracebacklater")_ _- timeout = None_ _- elif o == '--wait':_ _- input("Press any key to continue...")_ _+ if ns.huntrleaks:_ _+ # Avoid false positives due to various caches_ _+ # filling slowly with random data:_ _+ warmcaches()_ _+ if ns.memlimit is not None:_ _+ support.setmemlimit(ns.memlimit)_ _+ if ns.threshold is not None:_ _+ import gc_ _+ gc.setthreshold(ns.threshold)_ _+ if ns.nowindows:_ _+ import msvcrt_ _+ msvcrt.SetErrorMode(msvcrt.SEMFAILCRITICALERRORS|_ _+ msvcrt.SEMNOALIGNMENTFAULTEXCEPT|_ _+ msvcrt.SEMNOGPFAULTERRORBOX|_ _+ msvcrt.SEMNOOPENFILEERRORBOX)_ _+ try:_ _+ msvcrt.CrtSetReportMode_ _+ except AttributeError:_ _+ # release build_ _+ pass_ _else:_ _- print(("No handler for option {}. Please report this as a_ _bug "_ _- "at http://bugs.python.org.").format(o),_ _file=sys.stderr)_ _- sys.exit(1)_ _- if single and fromfile:_ _- usage("-s and -f don't go together!")_ _- if usemp and trace:_ _- usage("-T and -j don't go together!")_ _- if usemp and findleaks:_ _- usage("-l and -j don't go together!")_ _- if usemp and support.maxmemuse:_ _- usage("-M and -j don't go together!")_ _- if failfast and not (verbose or verbose3):_ _- usage("-G/--failfast needs either -v or -W")_ _+ for m in [msvcrt.CRTWARN, msvcrt.CRTERROR,_ _msvcrt.CRTASSERT]:_ _+ msvcrt.CrtSetReportMode(m, msvcrt.CRTDBGMODEFILE)_ _+ msvcrt.CrtSetReportFile(m, msvcrt.CRTDBGFILESTDERR)_ _+ if ns.wait:_ _+ input("Press any key to continue...")_ _+_ _+ if ns.slaveargs is not None:_ _+ args, kwargs = json.loads(ns.slaveargs)_ _+ try:_ _+ result = runtest(*args, **kwargs)_ _+ except KeyboardInterrupt:_ _+ result = INTERRUPTED, ''_ _+ except BaseException as e:_ _+ traceback.printexc()_ _+ result = CHILDERROR, str(e)_ _+ sys.stdout.flush()_ _+ print() # Force a newline (just in case)_ _+ print(json.dumps(result))_ _+ sys.exit(0)_ _good = []_ _bad = []_ _@@ -565,12 +515,12 @@_ _environmentchanged = []_ _interrupted = False_ _- if findleaks:_ _+ if ns.findleaks:_ _try:_ _import gc_ _except ImportError:_ _print('No GC available, disabling findleaks.')_ _- findleaks = False_ _+ ns.findleaks = False_ _else:_ _# Uncomment the line below to report garbage that is not_ _# freeable by reference counting alone. By default only_ _@@ -578,42 +528,40 @@_ _#gc.setdebug(gc.DEBUGSAVEALL)_ _foundgarbage = []_ _- if single:_ _+ if ns.single:_ _filename = os.path.join(TEMPDIR, 'pynexttest')_ _try:_ _- fp = open(filename, 'r')_ _- nexttest = fp.read().strip()_ _- tests = [nexttest]_ _- fp.close()_ _+ with open(filename, 'r') as fp:_ _+ nexttest = fp.read().strip()_ _+ tests = [nexttest]_ _except OSError:_ _pass_ _- if fromfile:_ _+ if ns.fromfile:_ _tests = []_ _- fp = open(os.path.join(support.SAVEDCWD, fromfile))_ _- countpat = re.compile(r'[\s*\d+/\s*\d+]')_ _- for line in fp:_ _- line = countpat.sub('', line)_ _- guts = line.split() # assuming no test has whitespace in its_ _name_ _- if guts and not guts[0].startswith('#'):_ _- tests.extend(guts)_ _- fp.close()_ _+ with open(os.path.join(support.SAVEDCWD, ns.fromfile)) as fp:_ _+ countpat = re.compile(r'[\s*\d+/\s*\d+]')_ _+ for line in fp:_ _+ line = countpat.sub('', line)_ _+ guts = line.split() # assuming no test has whitespace in_ _its name_ _+ if guts and not guts[0].startswith('#'):_ _+ tests.extend(guts)_ _# Strip .py extensions._ _- removepy(args)_ _+ removepy(ns.args)_ _removepy(tests)_ _stdtests = STDTESTS[:]_ _nottests = NOTTESTS.copy()_ _- if exclude:_ _- for arg in args:_ _+ if ns.exclude:_ _+ for arg in ns.args:_ _if arg in stdtests:_ _stdtests.remove(arg)_ _nottests.add(arg)_ _- args = []_ _+ ns.args = []_ _# For a partial run, we do not need to clutter the output._ _- if verbose or header or not (quiet or single or tests or args):_ _+ if ns.verbose or ns.header or not (ns.quiet or ns.single or tests or_ _ns.args):_ _# Print basic platform information_ _print("==", platform.pythonimplementation(),_ _*sys.version.split())_ _print("== ", platform.platform(aliased=True),_ _@@ -623,37 +571,39 @@_ _# if testdir is set, then we are not running the python tests suite,_ _so_ _# don't add default tests to be executed or skipped (pass empty_ _values)_ _- if testdir:_ _- alltests = findtests(testdir, list(), set())_ _+ if ns.testdir:_ _+ alltests = findtests(ns.testdir, list(), set())_ _else:_ _- alltests = findtests(testdir, stdtests, nottests)_ _+ alltests = findtests(ns.testdir, stdtests, nottests)_ _- selected = tests or args or alltests_ _- if single:_ _+ selected = tests or ns.args or alltests_ _+ if ns.single:_ _selected = selected[:1]_ _try:_ _nextsingletest = alltests[alltests.index(selected[0])+1]_ _except IndexError:_ _nextsingletest = None_ _# Remove all the selected tests that precede start if it's set._ _- if start:_ _+ if ns.start:_ _try:_ _- del selected[:selected.index(start)]_ _+ del selected[:selected.index(ns.start)]_ _except ValueError:_ _- print("Couldn't find starting test (%s), using all tests" %_ _start)_ _- if randomize:_ _- random.seed(randomseed)_ _- print("Using random seed", randomseed)_ _+ print("Couldn't find starting test (%s), using all tests" %_ _ns.start)_ _+ if ns.randomize:_ _+ if ns.randomseed is None:_ _+ ns.randomseed = random.randrange(10000000)_ _+ random.seed(ns.randomseed)_ _+ print("Using random seed", ns.randomseed)_ _random.shuffle(selected)_ _- if trace:_ _+ if ns.trace:_ _import trace, tempfile_ _tracer = trace.Trace(ignoredirs=[sys.baseprefix,_ _sys.baseexecprefix,_ _tempfile.gettempdir()],_ _trace=False, count=True)_ _testtimes = []_ _- support.verbose = verbose # Tell tests to be moderately quiet_ _- support.useresources = useresources_ _+ support.verbose = ns.verbose # Tell tests to be moderately quiet_ _+ support.useresources = ns.useresources_ _savemodules = sys.modules.keys()_ _def accumulateresult(test, result):_ _@@ -671,7 +621,7 @@_ _skipped.append(test)_ _resourcedenieds.append(test)_ _- if forever:_ _+ if ns.forever:_ _def testforever(tests=list(selected)):_ _while True:_ _for test in tests:_ _@@ -686,7 +636,7 @@_ _testcount = '/{}'.format(len(selected))_ _testcountwidth = len(testcount) - 1_ _- if usemp:_ _+ if ns.usemp:_ _try:_ _from threading import Thread_ _except ImportError:_ _@@ -710,11 +660,12 @@_ _output.put((None, None, None, None))_ _return_ _argstuple = (_ _- (test, verbose, quiet),_ _- dict(huntrleaks=huntrleaks,_ _useresources=useresources,_ _- debug=debug, outputonfailure=verbose3,_ _- timeout=timeout, failfast=failfast,_ _- matchtests=matchtests)_ _+ (test, ns.verbose, ns.quiet),_ _+ dict(huntrleaks=ns.huntrleaks,_ _+ useresources=ns.useresources,_ _+ debug=ns.debug,_ _outputonfailure=ns.verbose3,_ _+ timeout=ns.timeout, failfast=ns.failfast,_ _+ matchtests=ns.matchtests)_ _)_ _# -E is needed by some tests, e.g. testimport_ _# Running the child from the same working directory_ _ensures_ _@@ -743,19 +694,19 @@_ _except BaseException:_ _output.put((None, None, None, None))_ _raise_ _- workers = [Thread(target=work) for i in range(usemp)]_ _+ workers = [Thread(target=work) for i in range(ns.usemp)]_ _for worker in workers:_ _worker.start()_ _finished = 0_ _testindex = 1_ _try:_ _- while finished < usemp:_ _+ while finished < ns.usemp:_ _test, stdout, stderr, result = output.get()_ _if test is None:_ _finished += 1_ _continue_ _accumulateresult(test, result)_ _- if not quiet:_ _+ if not ns.quiet:_ _fmt = "[{1:{0}}{2}/{3}] {4}" if bad else_ _"[{1:{0}}{2}] {4}"_ _print(fmt.format(_ _testcountwidth, testindex, testcount,_ _@@ -778,29 +729,30 @@_ _worker.join()_ _else:_ _for testindex, test in enumerate(tests, 1):_ _- if not quiet:_ _+ if not ns.quiet:_ _fmt = "[{1:{0}}{2}/{3}] {4}" if bad else "[{1:{0}}{2}]_ _{4}"_ _print(fmt.format(_ _testcountwidth, testindex, testcount, len(bad),_ _test))_ _sys.stdout.flush()_ _- if trace:_ _+ if ns.trace:_ _# If we're tracing code coverage, then we don't exit with_ _status_ _# if on a false return value from main._ _- tracer.runctx('runtest(test, verbose, quiet,_ _timeout=timeout)',_ _+ tracer.runctx('runtest(test, ns.verbose, ns.quiet,_ _timeout=ns.timeout)',_ _globals=globals(), locals=vars())_ _else:_ _try:_ _- result = runtest(test, verbose, quiet, huntrleaks,_ _debug,_ _- outputonfailure=verbose3,_ _- timeout=timeout, failfast=failfast,_ _- matchtests=matchtests)_ _+ result = runtest(test, ns.verbose, ns.quiet,_ _+ ns.huntrleaks, ns.debug,_ _+ outputonfailure=ns.verbose3,_ _+ timeout=ns.timeout,_ _failfast=ns.failfast,_ _+ matchtests=ns.matchtests)_ _accumulateresult(test, result)_ _except KeyboardInterrupt:_ _interrupted = True_ _break_ _except:_ _raise_ _- if findleaks:_ _+ if ns.findleaks:_ _gc.collect()_ _if gc.garbage:_ _print("Warning: test created", len(gc.garbage), end='_ _')_ _@@ -821,11 +773,11 @@_ _omitted = set(selected) - set(good) - set(bad) - set(skipped)_ _print(count(len(omitted), "test"), "omitted:")_ _printlist(omitted)_ _- if good and not quiet:_ _+ if good and not ns.quiet:_ _if not bad and not skipped and not interrupted and len(good) > 1: print("All", end=' ') print(count(len(good), "test"), "OK.") - if printslow: + if ns.printslow: testtimes.sort(reverse=True) print("10 slowest tests:") for time, test in testtimes[:10]: @@ -839,18 +791,19 @@ print("{} altered the execution environment:".format( count(len(environmentchanged), "test"))) printlist(environmentchanged) - if skipped and not quiet: + if skipped and not ns.quiet: print(count(len(skipped), "test"), "skipped:") printlist(skipped) - if verbose2 and bad: + if ns.verbose2 and bad: print("Re-running failed tests in verbose mode") for test in bad: print("Re-running test %r in verbose mode" % test) sys.stdout.flush() try: - verbose = True - ok = runtest(test, True, quiet, huntrleaks, debug, timeout=timeout) + ns.verbose = True + ok = runtest(test, True, ns.quiet, ns.huntrleaks, ns.debug, + timeout=ns.timeout) except KeyboardInterrupt: # print a newline separate from the ^C print() @@ -858,18 +811,18 @@ except: raise - if single: + if ns.single: if nextsingletest: with open(filename, 'w') as fp: fp.write(nextsingletest + '\n') else: os.unlink(filename) - if trace: + if ns.trace: r = tracer.results() - r.writeresults(showmissing=True, summary=True, coverdir=coverdir) + r.writeresults(showmissing=True, summary=True, coverdir=ns.coverdir) - if runleaks: + if ns.runleaks: os.system("leaks %d" % os.getpid()) sys.exit(len(bad) > 0 or interrupted) diff --git a/Lib/test/testregrtest.py b/Lib/test/testregrtest.py --- a/Lib/test/testregrtest.py +++ b/Lib/test/testregrtest.py @@ -4,97 +4,281 @@ import argparse import getopt +import os.path import unittest from test import regrtest, support -def oldparseargs(args): - """Parse arguments as regrtest did strictly prior to 3.4. - - Raises getopt.GetoptError on bad arguments. - """ - return getopt.getopt(args, 'hvqxsoS:rf:lu:t:TD:NLR:FdwWM:nj:Gm:', - ['help', 'verbose', 'verbose2', 'verbose3', 'quiet', - 'exclude', 'single', 'slow', 'randomize', 'fromfile=', 'findleaks', - 'use=', 'threshold=', 'coverdir=', 'nocoverdir', - 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', - 'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug', - 'start=', 'nowindows', 'header', 'testdir=', 'timeout=', 'wait', - 'failfast', 'match=']) - class ParseArgsTestCase(unittest.TestCase): - """Test that regrtest's parsing code matches the prior getopt behavior.""" + """Test regrtest's argument parsing.""" - def parseargs(self, args): - # This is the same logic as that used in regrtest.main() - parser = regrtest.createparser() - ns = parser.parseargs(args=args) - opts = regrtest.convertnamespacetogetopt(ns) - return opts, ns.args + def checkError(self, args, msg): + with support.capturedstderr() as err, self.assertRaises(SystemExit): + regrtest.parseargs(args) + self.assertIn(msg, err.getvalue()) - def checkargs(self, args, expected=None): - """ - The expected parameter is for cases when the behavior of the new - parseargs differs from the old (but deliberately so). - """ - if expected is None: - try: - expected = oldparseargs(args) - except getopt.GetoptError: - # Suppress usage string output when an argparse.ArgumentError - # error is raised. - with support.capturedstderr(): - self.assertRaises(SystemExit, self.parseargs, args) - return - # The new parseargs() sorts by long option string. - expected[0].sort() - actual = self.parseargs(args) - self.assertEqual(actual, expected) + def testhelp(self): + for opt in '-h', '--help': + with self.subTest(opt=opt): _+ with support.capturedstdout() as out, _ + self.assertRaises(SystemExit): + regrtest.parseargs([opt]) + self.assertIn('Run Python regression tests.', out.getvalue()) + + def testtimeout(self): + ns = regrtest.parseargs(['--timeout', '4.2']) + self.assertEqual(ns.timeout, 4.2) + self.checkError(['--timeout'], 'expected one argument') + self.checkError(['--timeout', 'foo'], 'invalid float value') + + def testwait(self): + ns = regrtest.parseargs(['--wait']) + self.assertTrue(ns.wait) + + def testslaveargs(self): + ns = regrtest.parseargs(['--slaveargs', '[[], {}]']) + self.assertEqual(ns.slaveargs, '[[], {}]') + self.checkError(['--slaveargs'], 'expected one argument') + + def teststart(self): + for opt in '-S', '--start': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, 'foo']) + self.assertEqual(ns.start, 'foo') + self.checkError([opt], 'expected one argument') + + def testverbose(self): + ns = regrtest.parseargs(['-v']) + self.assertEqual(ns.verbose, 1) + ns = regrtest.parseargs(['-vvv']) + self.assertEqual(ns.verbose, 3) + ns = regrtest.parseargs(['--verbose']) + self.assertEqual(ns.verbose, 1) + ns = regrtest.parseargs(['--verbose'] * 3) + self.assertEqual(ns.verbose, 3) + ns = regrtest.parseargs([]) + self.assertEqual(ns.verbose, 0) + + def testverbose2(self): + for opt in '-w', '--verbose2': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.verbose2) + + def testverbose3(self): + for opt in '-W', '--verbose3': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.verbose3) + + def testdebug(self): + for opt in '-d', '--debug': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.debug) + + def testquiet(self): + for opt in '-q', '--quiet': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.quiet) + self.assertEqual(ns.verbose, 0) + + def testslow(self): + for opt in '-o', '--slow': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.printslow) + + def testheader(self): + ns = regrtest.parseargs(['--header']) + self.assertTrue(ns.header) + + def testrandomize(self): + for opt in '-r', '--randomize': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.randomize) + + def testrandseed(self): + ns = regrtest.parseargs(['--randseed', '12345']) + self.assertEqual(ns.randomseed, 12345) + self.assertTrue(ns.randomize) + self.checkError(['--randseed'], 'expected one argument') + self.checkError(['--randseed', 'foo'], 'invalid int value') + + def testfromfile(self): + for opt in '-f', '--fromfile': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, 'foo']) + self.assertEqual(ns.fromfile, 'foo') + self.checkError([opt], 'expected one argument') + self.checkError([opt, 'foo', '-s'], "don't go together") + + def testexclude(self): + for opt in '-x', '--exclude': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.exclude) + + def testsingle(self): + for opt in '-s', '--single': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.single) + self.checkError([opt, '-f', 'foo'], "don't go together") + + def testmatch(self): + for opt in '-m', '--match': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, 'pattern']) + self.assertEqual(ns.matchtests, 'pattern') + self.checkError([opt], 'expected one argument') + + def testfailfast(self): + for opt in '-G', '--failfast': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, '-v']) + self.assertTrue(ns.failfast) + ns = regrtest.parseargs([opt, '-W']) + self.assertTrue(ns.failfast) + self.checkError([opt], '-G/--failfast needs either -v or -W') + + def testuse(self): + for opt in '-u', '--use': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, 'gui,network']) + self.assertEqual(ns.useresources, ['gui', 'network']) + ns = regrtest.parseargs([opt, 'gui,none,network']) + self.assertEqual(ns.useresources, ['network']) + expected = list(regrtest.RESOURCENAMES) + expected.remove('gui') + ns = regrtest.parseargs([opt, 'all,-gui']) + self.assertEqual(ns.useresources, expected) + self.checkError([opt], 'expected one argument') + self.checkError([opt, 'foo'], 'invalid resource') + + def testmemlimit(self): + for opt in '-M', '--memlimit': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, '4G']) + self.assertEqual(ns.memlimit, '4G') + self.checkError([opt], 'expected one argument') + + def testtestdir(self): + ns = regrtest.parseargs(['--testdir', 'foo']) + self.assertEqual(ns.testdir, os.path.join(support.SAVEDCWD, 'foo')) + self.checkError(['--testdir'], 'expected one argument') + + def testfindleaks(self): + for opt in '-l', '--findleaks': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.findleaks) + + def testfindleaks(self): + for opt in '-L', '--runleaks': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.runleaks) + + def testfindleaks(self): + for opt in '-R', '--huntrleaks': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, ':']) + self.assertEqual(ns.huntrleaks, (5, 4, 'reflog.txt')) + ns = regrtest.parseargs([opt, '6:']) + self.assertEqual(ns.huntrleaks, (6, 4, 'reflog.txt')) + ns = regrtest.parseargs([opt, ':3']) + self.assertEqual(ns.huntrleaks, (5, 3, 'reflog.txt')) + ns = regrtest.parseargs([opt, '6:3:leaks.log']) + self.assertEqual(ns.huntrleaks, (6, 3, 'leaks.log')) + self.checkError([opt], 'expected one argument') + self.checkError([opt, '6'], + 'needs 2 or 3 colon-separated arguments') + self.checkError([opt, 'foo:'], 'invalid huntrleaks value') + self.checkError([opt, '6:foo'], 'invalid huntrleaks value') + + def testmultiprocess(self): + for opt in '-j', '--multiprocess': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, '2']) + self.assertEqual(ns.usemp, 2) + self.checkError([opt], 'expected one argument') + self.checkError([opt, 'foo'], 'invalid int value') + self.checkError([opt, '2', '-T'], "don't go together") + self.checkError([opt, '2', '-l'], "don't go together") + self.checkError([opt, '2', '-M', '4G'], "don't go together") + + def testfindleaks(self): + for opt in '-T', '--coverage': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.trace) + + def testcoverdir(self): + for opt in '-D', '--coverdir': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, 'foo']) + self.assertEqual(ns.coverdir, + os.path.join(support.SAVEDCWD, 'foo')) + self.checkError([opt], 'expected one argument') + + def testnocoverdir(self): + for opt in '-N', '--nocoverdir': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertIsNone(ns.coverdir) + + def testthreshold(self): + for opt in '-t', '--threshold': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt, '1000']) + self.assertEqual(ns.threshold, 1000) + self.checkError([opt], 'expected one argument') + self.checkError([opt, 'foo'], 'invalid int value') + + def testnowindows(self): + for opt in '-n', '--nowindows': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.nowindows) + + def testforever(self): + for opt in '-F', '--forever': + with self.subTest(opt=opt): + ns = regrtest.parseargs([opt]) + self.assertTrue(ns.forever) + def testunrecognizedargument(self): - self.checkargs(['--xxx']) - - def testvaluenotprovided(self): - self.checkargs(['--start']) - - def testshortoption(self): - # getopt returns the short option whereas argparse returns the long. - expected = ([('--quiet', '')], []) - self.checkargs(['-q'], expected=expected) - - def testlongoption(self): - self.checkargs(['--quiet']) + self.checkError(['--xxx'], 'usage:') def testlongoption_partial(self): - self.checkargs(['--qui']) + ns = regrtest.parseargs(['--qui']) + self.assertTrue(ns.quiet) + self.assertEqual(ns.verbose, 0) def testtwooptions(self): - self.checkargs(['--quiet', '--exclude']) - - def testoptionwithvalue(self): - self.checkargs(['--start', 'foo']) + ns = regrtest.parseargs(['--quiet', '--exclude']) + self.assertTrue(ns.quiet) + self.assertEqual(ns.verbose, 0) + self.assertTrue(ns.exclude) def testoptionwithemptystringvalue(self): - self.checkargs(['--start', '']) + ns = regrtest.parseargs(['--start', '']) + self.assertEqual(ns.start, '') def testarg(self): - self.checkargs(['foo']) + ns = regrtest.parseargs(['foo']) + self.assertEqual(ns.args, ['foo']) def testoptionandarg(self): - self.checkargs(['--quiet', 'foo']) + ns = regrtest.parseargs(['--quiet', 'foo']) + self.assertTrue(ns.quiet) + self.assertEqual(ns.verbose, 0) + self.assertEqual(ns.args, ['foo']) - def testfromfile(self): - self.checkargs(['--fromfile', 'file']) - - def testmatch(self): - self.checkargs(['--match', 'pattern']) - - def testrandomize(self): - self.checkargs(['--randomize']) - - -def testmain(): - support.rununittest(name) if name == 'main': - testmain() + unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -153,6 +153,9 @@ Tests ----- +- Issue #16799: Switched from getopt to argparse style in regrtest's argument + parsing. Added more tests for regrtest's argument parsing. + - Issue #18792: Use "127.0.0.1" or "::1" instead of "localhost" as much as possible, since "localhost" goes through a DNS lookup under recent Windows versions. -- Repository URL: http://hg.python.org/cpython


Python-checkins mailing list Python-checkins at python.org http://mail.python.org/mailman/listinfo/python-checkins -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.python.org/pipermail/python-dev/attachments/20130829/aa4f6185/attachment-0001.html>



More information about the Python-Dev mailing list