From 4710c53dcad1ebf3755f3efb9e80ac24bd72a9b2 Mon Sep 17 00:00:00 2001 From: darylm503 Date: Mon, 16 Apr 2012 22:12:42 +0000 Subject: AppPkg/Applications/Python: Add Python 2.7.2 sources since the release of Python 2.7.3 made them unavailable from the python.org web site. These files are a subset of the python-2.7.2.tgz distribution from python.org. Changed files from PyMod-2.7.2 have been copied into the corresponding directories of this tree, replacing the original files in the distribution. Signed-off-by: daryl.mcdaniel@intel.com git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13197 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Python/Python-2.7.2/Lib/test/test_argparse.py | 4646 ++++++++++++++++++++ 1 file changed, 4646 insertions(+) create mode 100644 AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_argparse.py (limited to 'AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_argparse.py') diff --git a/AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_argparse.py b/AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_argparse.py new file mode 100644 index 0000000000..76edc8a18f --- /dev/null +++ b/AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_argparse.py @@ -0,0 +1,4646 @@ +# Author: Steven J. Bethard . + +import codecs +import inspect +import os +import shutil +import stat +import sys +import textwrap +import tempfile +import unittest +import argparse + +from StringIO import StringIO + +class StdIOBuffer(StringIO): + pass + +from test import test_support + +class TestCase(unittest.TestCase): + + def assertEqual(self, obj1, obj2): + if obj1 != obj2: + print('') + print(repr(obj1)) + print(repr(obj2)) + print(obj1) + print(obj2) + super(TestCase, self).assertEqual(obj1, obj2) + + def setUp(self): + # The tests assume that line wrapping occurs at 80 columns, but this + # behaviour can be overridden by setting the COLUMNS environment + # variable. To ensure that this assumption is true, unset COLUMNS. + env = test_support.EnvironmentVarGuard() + env.unset("COLUMNS") + self.addCleanup(env.__exit__) + + +class TempDirMixin(object): + + def setUp(self): + self.temp_dir = tempfile.mkdtemp() + self.old_dir = os.getcwd() + os.chdir(self.temp_dir) + + def tearDown(self): + os.chdir(self.old_dir) + shutil.rmtree(self.temp_dir, True) + + def create_readonly_file(self, filename): + file_path = os.path.join(self.temp_dir, filename) + with open(file_path, 'w') as file: + file.write(filename) + os.chmod(file_path, stat.S_IREAD) + +class Sig(object): + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +class NS(object): + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + sorted_items = sorted(self.__dict__.items()) + kwarg_str = ', '.join(['%s=%r' % tup for tup in sorted_items]) + return '%s(%s)' % (type(self).__name__, kwarg_str) + + __hash__ = None + + def __eq__(self, other): + return vars(self) == vars(other) + + def __ne__(self, other): + return not (self == other) + + +class ArgumentParserError(Exception): + + def __init__(self, message, stdout=None, stderr=None, error_code=None): + Exception.__init__(self, message, stdout, stderr) + self.message = message + self.stdout = stdout + self.stderr = stderr + self.error_code = error_code + + +def stderr_to_parser_error(parse_args, *args, **kwargs): + # if this is being called recursively and stderr or stdout is already being + # redirected, simply call the function and let the enclosing function + # catch the exception + if isinstance(sys.stderr, StdIOBuffer) or isinstance(sys.stdout, StdIOBuffer): + return parse_args(*args, **kwargs) + + # if this is not being called recursively, redirect stderr and + # use it as the ArgumentParserError message + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = StdIOBuffer() + sys.stderr = StdIOBuffer() + try: + try: + result = parse_args(*args, **kwargs) + for key in list(vars(result)): + if getattr(result, key) is sys.stdout: + setattr(result, key, old_stdout) + if getattr(result, key) is sys.stderr: + setattr(result, key, old_stderr) + return result + except SystemExit: + code = sys.exc_info()[1].code + stdout = sys.stdout.getvalue() + stderr = sys.stderr.getvalue() + raise ArgumentParserError("SystemExit", stdout, stderr, code) + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + + +class ErrorRaisingArgumentParser(argparse.ArgumentParser): + + def parse_args(self, *args, **kwargs): + parse_args = super(ErrorRaisingArgumentParser, self).parse_args + return stderr_to_parser_error(parse_args, *args, **kwargs) + + def exit(self, *args, **kwargs): + exit = super(ErrorRaisingArgumentParser, self).exit + return stderr_to_parser_error(exit, *args, **kwargs) + + def error(self, *args, **kwargs): + error = super(ErrorRaisingArgumentParser, self).error + return stderr_to_parser_error(error, *args, **kwargs) + + +class ParserTesterMetaclass(type): + """Adds parser tests using the class attributes. + + Classes of this type should specify the following attributes: + + argument_signatures -- a list of Sig objects which specify + the signatures of Argument objects to be created + failures -- a list of args lists that should cause the parser + to fail + successes -- a list of (initial_args, options, remaining_args) tuples + where initial_args specifies the string args to be parsed, + options is a dict that should match the vars() of the options + parsed out of initial_args, and remaining_args should be any + remaining unparsed arguments + """ + + def __init__(cls, name, bases, bodydict): + if name == 'ParserTestCase': + return + + # default parser signature is empty + if not hasattr(cls, 'parser_signature'): + cls.parser_signature = Sig() + if not hasattr(cls, 'parser_class'): + cls.parser_class = ErrorRaisingArgumentParser + + # --------------------------------------- + # functions for adding optional arguments + # --------------------------------------- + def no_groups(parser, argument_signatures): + """Add all arguments directly to the parser""" + for sig in argument_signatures: + parser.add_argument(*sig.args, **sig.kwargs) + + def one_group(parser, argument_signatures): + """Add all arguments under a single group in the parser""" + group = parser.add_argument_group('foo') + for sig in argument_signatures: + group.add_argument(*sig.args, **sig.kwargs) + + def many_groups(parser, argument_signatures): + """Add each argument in its own group to the parser""" + for i, sig in enumerate(argument_signatures): + group = parser.add_argument_group('foo:%i' % i) + group.add_argument(*sig.args, **sig.kwargs) + + # -------------------------- + # functions for parsing args + # -------------------------- + def listargs(parser, args): + """Parse the args by passing in a list""" + return parser.parse_args(args) + + def sysargs(parser, args): + """Parse the args by defaulting to sys.argv""" + old_sys_argv = sys.argv + sys.argv = [old_sys_argv[0]] + args + try: + return parser.parse_args() + finally: + sys.argv = old_sys_argv + + # class that holds the combination of one optional argument + # addition method and one arg parsing method + class AddTests(object): + + def __init__(self, tester_cls, add_arguments, parse_args): + self._add_arguments = add_arguments + self._parse_args = parse_args + + add_arguments_name = self._add_arguments.__name__ + parse_args_name = self._parse_args.__name__ + for test_func in [self.test_failures, self.test_successes]: + func_name = test_func.__name__ + names = func_name, add_arguments_name, parse_args_name + test_name = '_'.join(names) + + def wrapper(self, test_func=test_func): + test_func(self) + try: + wrapper.__name__ = test_name + except TypeError: + pass + setattr(tester_cls, test_name, wrapper) + + def _get_parser(self, tester): + args = tester.parser_signature.args + kwargs = tester.parser_signature.kwargs + parser = tester.parser_class(*args, **kwargs) + self._add_arguments(parser, tester.argument_signatures) + return parser + + def test_failures(self, tester): + parser = self._get_parser(tester) + for args_str in tester.failures: + args = args_str.split() + raises = tester.assertRaises + raises(ArgumentParserError, parser.parse_args, args) + + def test_successes(self, tester): + parser = self._get_parser(tester) + for args, expected_ns in tester.successes: + if isinstance(args, str): + args = args.split() + result_ns = self._parse_args(parser, args) + tester.assertEqual(expected_ns, result_ns) + + # add tests for each combination of an optionals adding method + # and an arg parsing method + for add_arguments in [no_groups, one_group, many_groups]: + for parse_args in [listargs, sysargs]: + AddTests(cls, add_arguments, parse_args) + +bases = TestCase, +ParserTestCase = ParserTesterMetaclass('ParserTestCase', bases, {}) + +# =============== +# Optionals tests +# =============== + +class TestOptionalsSingleDash(ParserTestCase): + """Test an Optional with a single-dash option string""" + + argument_signatures = [Sig('-x')] + failures = ['-x', 'a', '--foo', '-x --foo', '-x -y'] + successes = [ + ('', NS(x=None)), + ('-x a', NS(x='a')), + ('-xa', NS(x='a')), + ('-x -1', NS(x='-1')), + ('-x-1', NS(x='-1')), + ] + + +class TestOptionalsSingleDashCombined(ParserTestCase): + """Test an Optional with a single-dash option string""" + + argument_signatures = [ + Sig('-x', action='store_true'), + Sig('-yyy', action='store_const', const=42), + Sig('-z'), + ] + failures = ['a', '--foo', '-xa', '-x --foo', '-x -z', '-z -x', + '-yx', '-yz a', '-yyyx', '-yyyza', '-xyza'] + successes = [ + ('', NS(x=False, yyy=None, z=None)), + ('-x', NS(x=True, yyy=None, z=None)), + ('-za', NS(x=False, yyy=None, z='a')), + ('-z a', NS(x=False, yyy=None, z='a')), + ('-xza', NS(x=True, yyy=None, z='a')), + ('-xz a', NS(x=True, yyy=None, z='a')), + ('-x -za', NS(x=True, yyy=None, z='a')), + ('-x -z a', NS(x=True, yyy=None, z='a')), + ('-y', NS(x=False, yyy=42, z=None)), + ('-yyy', NS(x=False, yyy=42, z=None)), + ('-x -yyy -za', NS(x=True, yyy=42, z='a')), + ('-x -yyy -z a', NS(x=True, yyy=42, z='a')), + ] + + +class TestOptionalsSingleDashLong(ParserTestCase): + """Test an Optional with a multi-character single-dash option string""" + + argument_signatures = [Sig('-foo')] + failures = ['-foo', 'a', '--foo', '-foo --foo', '-foo -y', '-fooa'] + successes = [ + ('', NS(foo=None)), + ('-foo a', NS(foo='a')), + ('-foo -1', NS(foo='-1')), + ('-fo a', NS(foo='a')), + ('-f a', NS(foo='a')), + ] + + +class TestOptionalsSingleDashSubsetAmbiguous(ParserTestCase): + """Test Optionals where option strings are subsets of each other""" + + argument_signatures = [Sig('-f'), Sig('-foobar'), Sig('-foorab')] + failures = ['-f', '-foo', '-fo', '-foo b', '-foob', '-fooba', '-foora'] + successes = [ + ('', NS(f=None, foobar=None, foorab=None)), + ('-f a', NS(f='a', foobar=None, foorab=None)), + ('-fa', NS(f='a', foobar=None, foorab=None)), + ('-foa', NS(f='oa', foobar=None, foorab=None)), + ('-fooa', NS(f='ooa', foobar=None, foorab=None)), + ('-foobar a', NS(f=None, foobar='a', foorab=None)), + ('-foorab a', NS(f=None, foobar=None, foorab='a')), + ] + + +class TestOptionalsSingleDashAmbiguous(ParserTestCase): + """Test Optionals that partially match but are not subsets""" + + argument_signatures = [Sig('-foobar'), Sig('-foorab')] + failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b'] + successes = [ + ('', NS(foobar=None, foorab=None)), + ('-foob a', NS(foobar='a', foorab=None)), + ('-foor a', NS(foobar=None, foorab='a')), + ('-fooba a', NS(foobar='a', foorab=None)), + ('-foora a', NS(foobar=None, foorab='a')), + ('-foobar a', NS(foobar='a', foorab=None)), + ('-foorab a', NS(foobar=None, foorab='a')), + ] + + +class TestOptionalsNumeric(ParserTestCase): + """Test an Optional with a short opt string""" + + argument_signatures = [Sig('-1', dest='one')] + failures = ['-1', 'a', '-1 --foo', '-1 -y', '-1 -1', '-1 -2'] + successes = [ + ('', NS(one=None)), + ('-1 a', NS(one='a')), + ('-1a', NS(one='a')), + ('-1-2', NS(one='-2')), + ] + + +class TestOptionalsDoubleDash(ParserTestCase): + """Test an Optional with a double-dash option string""" + + argument_signatures = [Sig('--foo')] + failures = ['--foo', '-f', '-f a', 'a', '--foo -x', '--foo --bar'] + successes = [ + ('', NS(foo=None)), + ('--foo a', NS(foo='a')), + ('--foo=a', NS(foo='a')), + ('--foo -2.5', NS(foo='-2.5')), + ('--foo=-2.5', NS(foo='-2.5')), + ] + + +class TestOptionalsDoubleDashPartialMatch(ParserTestCase): + """Tests partial matching with a double-dash option string""" + + argument_signatures = [ + Sig('--badger', action='store_true'), + Sig('--bat'), + ] + failures = ['--bar', '--b', '--ba', '--b=2', '--ba=4', '--badge 5'] + successes = [ + ('', NS(badger=False, bat=None)), + ('--bat X', NS(badger=False, bat='X')), + ('--bad', NS(badger=True, bat=None)), + ('--badg', NS(badger=True, bat=None)), + ('--badge', NS(badger=True, bat=None)), + ('--badger', NS(badger=True, bat=None)), + ] + + +class TestOptionalsDoubleDashPrefixMatch(ParserTestCase): + """Tests when one double-dash option string is a prefix of another""" + + argument_signatures = [ + Sig('--badger', action='store_true'), + Sig('--ba'), + ] + failures = ['--bar', '--b', '--ba', '--b=2', '--badge 5'] + successes = [ + ('', NS(badger=False, ba=None)), + ('--ba X', NS(badger=False, ba='X')), + ('--ba=X', NS(badger=False, ba='X')), + ('--bad', NS(badger=True, ba=None)), + ('--badg', NS(badger=True, ba=None)), + ('--badge', NS(badger=True, ba=None)), + ('--badger', NS(badger=True, ba=None)), + ] + + +class TestOptionalsSingleDoubleDash(ParserTestCase): + """Test an Optional with single- and double-dash option strings""" + + argument_signatures = [ + Sig('-f', action='store_true'), + Sig('--bar'), + Sig('-baz', action='store_const', const=42), + ] + failures = ['--bar', '-fbar', '-fbaz', '-bazf', '-b B', 'B'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('-f', NS(f=True, bar=None, baz=None)), + ('--ba B', NS(f=False, bar='B', baz=None)), + ('-f --bar B', NS(f=True, bar='B', baz=None)), + ('-f -b', NS(f=True, bar=None, baz=42)), + ('-ba -f', NS(f=True, bar=None, baz=42)), + ] + + +class TestOptionalsAlternatePrefixChars(ParserTestCase): + """Test an Optional with option strings with custom prefixes""" + + parser_signature = Sig(prefix_chars='+:/', add_help=False) + argument_signatures = [ + Sig('+f', action='store_true'), + Sig('::bar'), + Sig('/baz', action='store_const', const=42), + ] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('+f', NS(f=True, bar=None, baz=None)), + ('::ba B', NS(f=False, bar='B', baz=None)), + ('+f ::bar B', NS(f=True, bar='B', baz=None)), + ('+f /b', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)), + ] + + +class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase): + """When ``-`` not in prefix_chars, default operators created for help + should use the prefix_chars in use rather than - or -- + http://bugs.python.org/issue9444""" + + parser_signature = Sig(prefix_chars='+:/', add_help=True) + argument_signatures = [ + Sig('+f', action='store_true'), + Sig('::bar'), + Sig('/baz', action='store_const', const=42), + ] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('+f', NS(f=True, bar=None, baz=None)), + ('::ba B', NS(f=False, bar='B', baz=None)), + ('+f ::bar B', NS(f=True, bar='B', baz=None)), + ('+f /b', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)) + ] + + +class TestOptionalsAlternatePrefixCharsMultipleShortArgs(ParserTestCase): + """Verify that Optionals must be called with their defined prefixes""" + + parser_signature = Sig(prefix_chars='+-', add_help=False) + argument_signatures = [ + Sig('-x', action='store_true'), + Sig('+y', action='store_true'), + Sig('+z', action='store_true'), + ] + failures = ['-w', + '-xyz', + '+x', + '-y', + '+xyz', + ] + successes = [ + ('', NS(x=False, y=False, z=False)), + ('-x', NS(x=True, y=False, z=False)), + ('+y -x', NS(x=True, y=True, z=False)), + ('+yz -x', NS(x=True, y=True, z=True)), + ] + + +class TestOptionalsShortLong(ParserTestCase): + """Test a combination of single- and double-dash option strings""" + + argument_signatures = [ + Sig('-v', '--verbose', '-n', '--noisy', action='store_true'), + ] + failures = ['--x --verbose', '-N', 'a', '-v x'] + successes = [ + ('', NS(verbose=False)), + ('-v', NS(verbose=True)), + ('--verbose', NS(verbose=True)), + ('-n', NS(verbose=True)), + ('--noisy', NS(verbose=True)), + ] + + +class TestOptionalsDest(ParserTestCase): + """Tests various means of setting destination""" + + argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')] + failures = ['a'] + successes = [ + ('--foo-bar f', NS(foo_bar='f', zabbaz=None)), + ('--baz g', NS(foo_bar=None, zabbaz='g')), + ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i')), + ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j')), + ] + + +class TestOptionalsDefault(ParserTestCase): + """Tests specifying a default for an Optional""" + + argument_signatures = [Sig('-x'), Sig('-y', default=42)] + failures = ['a'] + successes = [ + ('', NS(x=None, y=42)), + ('-xx', NS(x='x', y=42)), + ('-yy', NS(x=None, y='y')), + ] + + +class TestOptionalsNargsDefault(ParserTestCase): + """Tests not specifying the number of args for an Optional""" + + argument_signatures = [Sig('-x')] + failures = ['a', '-x'] + successes = [ + ('', NS(x=None)), + ('-x a', NS(x='a')), + ] + + +class TestOptionalsNargs1(ParserTestCase): + """Tests specifying the 1 arg for an Optional""" + + argument_signatures = [Sig('-x', nargs=1)] + failures = ['a', '-x'] + successes = [ + ('', NS(x=None)), + ('-x a', NS(x=['a'])), + ] + + +class TestOptionalsNargs3(ParserTestCase): + """Tests specifying the 3 args for an Optional""" + + argument_signatures = [Sig('-x', nargs=3)] + failures = ['a', '-x', '-x a', '-x a b', 'a -x', 'a -x b'] + successes = [ + ('', NS(x=None)), + ('-x a b c', NS(x=['a', 'b', 'c'])), + ] + + +class TestOptionalsNargsOptional(ParserTestCase): + """Tests specifying an Optional arg for an Optional""" + + argument_signatures = [ + Sig('-w', nargs='?'), + Sig('-x', nargs='?', const=42), + Sig('-y', nargs='?', default='spam'), + Sig('-z', nargs='?', type=int, const='42', default='84'), + ] + failures = ['2'] + successes = [ + ('', NS(w=None, x=None, y='spam', z=84)), + ('-w', NS(w=None, x=None, y='spam', z=84)), + ('-w 2', NS(w='2', x=None, y='spam', z=84)), + ('-x', NS(w=None, x=42, y='spam', z=84)), + ('-x 2', NS(w=None, x='2', y='spam', z=84)), + ('-y', NS(w=None, x=None, y=None, z=84)), + ('-y 2', NS(w=None, x=None, y='2', z=84)), + ('-z', NS(w=None, x=None, y='spam', z=42)), + ('-z 2', NS(w=None, x=None, y='spam', z=2)), + ] + + +class TestOptionalsNargsZeroOrMore(ParserTestCase): + """Tests specifying an args for an Optional that accepts zero or more""" + + argument_signatures = [ + Sig('-x', nargs='*'), + Sig('-y', nargs='*', default='spam'), + ] + failures = ['a'] + successes = [ + ('', NS(x=None, y='spam')), + ('-x', NS(x=[], y='spam')), + ('-x a', NS(x=['a'], y='spam')), + ('-x a b', NS(x=['a', 'b'], y='spam')), + ('-y', NS(x=None, y=[])), + ('-y a', NS(x=None, y=['a'])), + ('-y a b', NS(x=None, y=['a', 'b'])), + ] + + +class TestOptionalsNargsOneOrMore(ParserTestCase): + """Tests specifying an args for an Optional that accepts one or more""" + + argument_signatures = [ + Sig('-x', nargs='+'), + Sig('-y', nargs='+', default='spam'), + ] + failures = ['a', '-x', '-y', 'a -x', 'a -y b'] + successes = [ + ('', NS(x=None, y='spam')), + ('-x a', NS(x=['a'], y='spam')), + ('-x a b', NS(x=['a', 'b'], y='spam')), + ('-y a', NS(x=None, y=['a'])), + ('-y a b', NS(x=None, y=['a', 'b'])), + ] + + +class TestOptionalsChoices(ParserTestCase): + """Tests specifying the choices for an Optional""" + + argument_signatures = [ + Sig('-f', choices='abc'), + Sig('-g', type=int, choices=range(5))] + failures = ['a', '-f d', '-fad', '-ga', '-g 6'] + successes = [ + ('', NS(f=None, g=None)), + ('-f a', NS(f='a', g=None)), + ('-f c', NS(f='c', g=None)), + ('-g 0', NS(f=None, g=0)), + ('-g 03', NS(f=None, g=3)), + ('-fb -g4', NS(f='b', g=4)), + ] + + +class TestOptionalsRequired(ParserTestCase): + """Tests the an optional action that is required""" + + argument_signatures = [ + Sig('-x', type=int, required=True), + ] + failures = ['a', ''] + successes = [ + ('-x 1', NS(x=1)), + ('-x42', NS(x=42)), + ] + + +class TestOptionalsActionStore(ParserTestCase): + """Tests the store action for an Optional""" + + argument_signatures = [Sig('-x', action='store')] + failures = ['a', 'a -x'] + successes = [ + ('', NS(x=None)), + ('-xfoo', NS(x='foo')), + ] + + +class TestOptionalsActionStoreConst(ParserTestCase): + """Tests the store_const action for an Optional""" + + argument_signatures = [Sig('-y', action='store_const', const=object)] + failures = ['a'] + successes = [ + ('', NS(y=None)), + ('-y', NS(y=object)), + ] + + +class TestOptionalsActionStoreFalse(ParserTestCase): + """Tests the store_false action for an Optional""" + + argument_signatures = [Sig('-z', action='store_false')] + failures = ['a', '-za', '-z a'] + successes = [ + ('', NS(z=True)), + ('-z', NS(z=False)), + ] + + +class TestOptionalsActionStoreTrue(ParserTestCase): + """Tests the store_true action for an Optional""" + + argument_signatures = [Sig('--apple', action='store_true')] + failures = ['a', '--apple=b', '--apple b'] + successes = [ + ('', NS(apple=False)), + ('--apple', NS(apple=True)), + ] + + +class TestOptionalsActionAppend(ParserTestCase): + """Tests the append action for an Optional""" + + argument_signatures = [Sig('--baz', action='append')] + failures = ['a', '--baz', 'a --baz', '--baz a b'] + successes = [ + ('', NS(baz=None)), + ('--baz a', NS(baz=['a'])), + ('--baz a --baz b', NS(baz=['a', 'b'])), + ] + + +class TestOptionalsActionAppendWithDefault(ParserTestCase): + """Tests the append action for an Optional""" + + argument_signatures = [Sig('--baz', action='append', default=['X'])] + failures = ['a', '--baz', 'a --baz', '--baz a b'] + successes = [ + ('', NS(baz=['X'])), + ('--baz a', NS(baz=['X', 'a'])), + ('--baz a --baz b', NS(baz=['X', 'a', 'b'])), + ] + + +class TestOptionalsActionAppendConst(ParserTestCase): + """Tests the append_const action for an Optional""" + + argument_signatures = [ + Sig('-b', action='append_const', const=Exception), + Sig('-c', action='append', dest='b'), + ] + failures = ['a', '-c', 'a -c', '-bx', '-b x'] + successes = [ + ('', NS(b=None)), + ('-b', NS(b=[Exception])), + ('-b -cx -b -cyz', NS(b=[Exception, 'x', Exception, 'yz'])), + ] + + +class TestOptionalsActionAppendConstWithDefault(ParserTestCase): + """Tests the append_const action for an Optional""" + + argument_signatures = [ + Sig('-b', action='append_const', const=Exception, default=['X']), + Sig('-c', action='append', dest='b'), + ] + failures = ['a', '-c', 'a -c', '-bx', '-b x'] + successes = [ + ('', NS(b=['X'])), + ('-b', NS(b=['X', Exception])), + ('-b -cx -b -cyz', NS(b=['X', Exception, 'x', Exception, 'yz'])), + ] + + +class TestOptionalsActionCount(ParserTestCase): + """Tests the count action for an Optional""" + + argument_signatures = [Sig('-x', action='count')] + failures = ['a', '-x a', '-x b', '-x a -x b'] + successes = [ + ('', NS(x=None)), + ('-x', NS(x=1)), + ] + + +# ================ +# Positional tests +# ================ + +class TestPositionalsNargsNone(ParserTestCase): + """Test a Positional that doesn't specify nargs""" + + argument_signatures = [Sig('foo')] + failures = ['', '-x', 'a b'] + successes = [ + ('a', NS(foo='a')), + ] + + +class TestPositionalsNargs1(ParserTestCase): + """Test a Positional that specifies an nargs of 1""" + + argument_signatures = [Sig('foo', nargs=1)] + failures = ['', '-x', 'a b'] + successes = [ + ('a', NS(foo=['a'])), + ] + + +class TestPositionalsNargs2(ParserTestCase): + """Test a Positional that specifies an nargs of 2""" + + argument_signatures = [Sig('foo', nargs=2)] + failures = ['', 'a', '-x', 'a b c'] + successes = [ + ('a b', NS(foo=['a', 'b'])), + ] + + +class TestPositionalsNargsZeroOrMore(ParserTestCase): + """Test a Positional that specifies unlimited nargs""" + + argument_signatures = [Sig('foo', nargs='*')] + failures = ['-x'] + successes = [ + ('', NS(foo=[])), + ('a', NS(foo=['a'])), + ('a b', NS(foo=['a', 'b'])), + ] + + +class TestPositionalsNargsZeroOrMoreDefault(ParserTestCase): + """Test a Positional that specifies unlimited nargs and a default""" + + argument_signatures = [Sig('foo', nargs='*', default='bar')] + failures = ['-x'] + successes = [ + ('', NS(foo='bar')), + ('a', NS(foo=['a'])), + ('a b', NS(foo=['a', 'b'])), + ] + + +class TestPositionalsNargsOneOrMore(ParserTestCase): + """Test a Positional that specifies one or more nargs""" + + argument_signatures = [Sig('foo', nargs='+')] + failures = ['', '-x'] + successes = [ + ('a', NS(foo=['a'])), + ('a b', NS(foo=['a', 'b'])), + ] + + +class TestPositionalsNargsOptional(ParserTestCase): + """Tests an Optional Positional""" + + argument_signatures = [Sig('foo', nargs='?')] + failures = ['-x', 'a b'] + successes = [ + ('', NS(foo=None)), + ('a', NS(foo='a')), + ] + + +class TestPositionalsNargsOptionalDefault(ParserTestCase): + """Tests an Optional Positional with a default value""" + + argument_signatures = [Sig('foo', nargs='?', default=42)] + failures = ['-x', 'a b'] + successes = [ + ('', NS(foo=42)), + ('a', NS(foo='a')), + ] + + +class TestPositionalsNargsOptionalConvertedDefault(ParserTestCase): + """Tests an Optional Positional with a default value + that needs to be converted to the appropriate type. + """ + + argument_signatures = [ + Sig('foo', nargs='?', type=int, default='42'), + ] + failures = ['-x', 'a b', '1 2'] + successes = [ + ('', NS(foo=42)), + ('1', NS(foo=1)), + ] + + +class TestPositionalsNargsNoneNone(ParserTestCase): + """Test two Positionals that don't specify nargs""" + + argument_signatures = [Sig('foo'), Sig('bar')] + failures = ['', '-x', 'a', 'a b c'] + successes = [ + ('a b', NS(foo='a', bar='b')), + ] + + +class TestPositionalsNargsNone1(ParserTestCase): + """Test a Positional with no nargs followed by one with 1""" + + argument_signatures = [Sig('foo'), Sig('bar', nargs=1)] + failures = ['', '--foo', 'a', 'a b c'] + successes = [ + ('a b', NS(foo='a', bar=['b'])), + ] + + +class TestPositionalsNargs2None(ParserTestCase): + """Test a Positional with 2 nargs followed by one with none""" + + argument_signatures = [Sig('foo', nargs=2), Sig('bar')] + failures = ['', '--foo', 'a', 'a b', 'a b c d'] + successes = [ + ('a b c', NS(foo=['a', 'b'], bar='c')), + ] + + +class TestPositionalsNargsNoneZeroOrMore(ParserTestCase): + """Test a Positional with no nargs followed by one with unlimited""" + + argument_signatures = [Sig('foo'), Sig('bar', nargs='*')] + failures = ['', '--foo'] + successes = [ + ('a', NS(foo='a', bar=[])), + ('a b', NS(foo='a', bar=['b'])), + ('a b c', NS(foo='a', bar=['b', 'c'])), + ] + + +class TestPositionalsNargsNoneOneOrMore(ParserTestCase): + """Test a Positional with no nargs followed by one with one or more""" + + argument_signatures = [Sig('foo'), Sig('bar', nargs='+')] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo='a', bar=['b'])), + ('a b c', NS(foo='a', bar=['b', 'c'])), + ] + + +class TestPositionalsNargsNoneOptional(ParserTestCase): + """Test a Positional with no nargs followed by one with an Optional""" + + argument_signatures = [Sig('foo'), Sig('bar', nargs='?')] + failures = ['', '--foo', 'a b c'] + successes = [ + ('a', NS(foo='a', bar=None)), + ('a b', NS(foo='a', bar='b')), + ] + + +class TestPositionalsNargsZeroOrMoreNone(ParserTestCase): + """Test a Positional with unlimited nargs followed by one with none""" + + argument_signatures = [Sig('foo', nargs='*'), Sig('bar')] + failures = ['', '--foo'] + successes = [ + ('a', NS(foo=[], bar='a')), + ('a b', NS(foo=['a'], bar='b')), + ('a b c', NS(foo=['a', 'b'], bar='c')), + ] + + +class TestPositionalsNargsOneOrMoreNone(ParserTestCase): + """Test a Positional with one or more nargs followed by one with none""" + + argument_signatures = [Sig('foo', nargs='+'), Sig('bar')] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo=['a'], bar='b')), + ('a b c', NS(foo=['a', 'b'], bar='c')), + ] + + +class TestPositionalsNargsOptionalNone(ParserTestCase): + """Test a Positional with an Optional nargs followed by one with none""" + + argument_signatures = [Sig('foo', nargs='?', default=42), Sig('bar')] + failures = ['', '--foo', 'a b c'] + successes = [ + ('a', NS(foo=42, bar='a')), + ('a b', NS(foo='a', bar='b')), + ] + + +class TestPositionalsNargs2ZeroOrMore(ParserTestCase): + """Test a Positional with 2 nargs followed by one with unlimited""" + + argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='*')] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo=['a', 'b'], bar=[])), + ('a b c', NS(foo=['a', 'b'], bar=['c'])), + ] + + +class TestPositionalsNargs2OneOrMore(ParserTestCase): + """Test a Positional with 2 nargs followed by one with one or more""" + + argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='+')] + failures = ['', '--foo', 'a', 'a b'] + successes = [ + ('a b c', NS(foo=['a', 'b'], bar=['c'])), + ] + + +class TestPositionalsNargs2Optional(ParserTestCase): + """Test a Positional with 2 nargs followed by one optional""" + + argument_signatures = [Sig('foo', nargs=2), Sig('bar', nargs='?')] + failures = ['', '--foo', 'a', 'a b c d'] + successes = [ + ('a b', NS(foo=['a', 'b'], bar=None)), + ('a b c', NS(foo=['a', 'b'], bar='c')), + ] + + +class TestPositionalsNargsZeroOrMore1(ParserTestCase): + """Test a Positional with unlimited nargs followed by one with 1""" + + argument_signatures = [Sig('foo', nargs='*'), Sig('bar', nargs=1)] + failures = ['', '--foo', ] + successes = [ + ('a', NS(foo=[], bar=['a'])), + ('a b', NS(foo=['a'], bar=['b'])), + ('a b c', NS(foo=['a', 'b'], bar=['c'])), + ] + + +class TestPositionalsNargsOneOrMore1(ParserTestCase): + """Test a Positional with one or more nargs followed by one with 1""" + + argument_signatures = [Sig('foo', nargs='+'), Sig('bar', nargs=1)] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo=['a'], bar=['b'])), + ('a b c', NS(foo=['a', 'b'], bar=['c'])), + ] + + +class TestPositionalsNargsOptional1(ParserTestCase): + """Test a Positional with an Optional nargs followed by one with 1""" + + argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs=1)] + failures = ['', '--foo', 'a b c'] + successes = [ + ('a', NS(foo=None, bar=['a'])), + ('a b', NS(foo='a', bar=['b'])), + ] + + +class TestPositionalsNargsNoneZeroOrMore1(ParserTestCase): + """Test three Positionals: no nargs, unlimited nargs and 1 nargs""" + + argument_signatures = [ + Sig('foo'), + Sig('bar', nargs='*'), + Sig('baz', nargs=1), + ] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo='a', bar=[], baz=['b'])), + ('a b c', NS(foo='a', bar=['b'], baz=['c'])), + ] + + +class TestPositionalsNargsNoneOneOrMore1(ParserTestCase): + """Test three Positionals: no nargs, one or more nargs and 1 nargs""" + + argument_signatures = [ + Sig('foo'), + Sig('bar', nargs='+'), + Sig('baz', nargs=1), + ] + failures = ['', '--foo', 'a', 'b'] + successes = [ + ('a b c', NS(foo='a', bar=['b'], baz=['c'])), + ('a b c d', NS(foo='a', bar=['b', 'c'], baz=['d'])), + ] + + +class TestPositionalsNargsNoneOptional1(ParserTestCase): + """Test three Positionals: no nargs, optional narg and 1 nargs""" + + argument_signatures = [ + Sig('foo'), + Sig('bar', nargs='?', default=0.625), + Sig('baz', nargs=1), + ] + failures = ['', '--foo', 'a'] + successes = [ + ('a b', NS(foo='a', bar=0.625, baz=['b'])), + ('a b c', NS(foo='a', bar='b', baz=['c'])), + ] + + +class TestPositionalsNargsOptionalOptional(ParserTestCase): + """Test two optional nargs""" + + argument_signatures = [ + Sig('foo', nargs='?'), + Sig('bar', nargs='?', default=42), + ] + failures = ['--foo', 'a b c'] + successes = [ + ('', NS(foo=None, bar=42)), + ('a', NS(foo='a', bar=42)), + ('a b', NS(foo='a', bar='b')), + ] + + +class TestPositionalsNargsOptionalZeroOrMore(ParserTestCase): + """Test an Optional narg followed by unlimited nargs""" + + argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='*')] + failures = ['--foo'] + successes = [ + ('', NS(foo=None, bar=[])), + ('a', NS(foo='a', bar=[])), + ('a b', NS(foo='a', bar=['b'])), + ('a b c', NS(foo='a', bar=['b', 'c'])), + ] + + +class TestPositionalsNargsOptionalOneOrMore(ParserTestCase): + """Test an Optional narg followed by one or more nargs""" + + argument_signatures = [Sig('foo', nargs='?'), Sig('bar', nargs='+')] + failures = ['', '--foo'] + successes = [ + ('a', NS(foo=None, bar=['a'])), + ('a b', NS(foo='a', bar=['b'])), + ('a b c', NS(foo='a', bar=['b', 'c'])), + ] + + +class TestPositionalsChoicesString(ParserTestCase): + """Test a set of single-character choices""" + + argument_signatures = [Sig('spam', choices=set('abcdefg'))] + failures = ['', '--foo', 'h', '42', 'ef'] + successes = [ + ('a', NS(spam='a')), + ('g', NS(spam='g')), + ] + + +class TestPositionalsChoicesInt(ParserTestCase): + """Test a set of integer choices""" + + argument_signatures = [Sig('spam', type=int, choices=range(20))] + failures = ['', '--foo', 'h', '42', 'ef'] + successes = [ + ('4', NS(spam=4)), + ('15', NS(spam=15)), + ] + + +class TestPositionalsActionAppend(ParserTestCase): + """Test the 'append' action""" + + argument_signatures = [ + Sig('spam', action='append'), + Sig('spam', action='append', nargs=2), + ] + failures = ['', '--foo', 'a', 'a b', 'a b c d'] + successes = [ + ('a b c', NS(spam=['a', ['b', 'c']])), + ] + +# ======================================== +# Combined optionals and positionals tests +# ======================================== + +class TestOptionalsNumericAndPositionals(ParserTestCase): + """Tests negative number args when numeric options are present""" + + argument_signatures = [ + Sig('x', nargs='?'), + Sig('-4', dest='y', action='store_true'), + ] + failures = ['-2', '-315'] + successes = [ + ('', NS(x=None, y=False)), + ('a', NS(x='a', y=False)), + ('-4', NS(x=None, y=True)), + ('-4 a', NS(x='a', y=True)), + ] + + +class TestOptionalsAlmostNumericAndPositionals(ParserTestCase): + """Tests negative number args when almost numeric options are present""" + + argument_signatures = [ + Sig('x', nargs='?'), + Sig('-k4', dest='y', action='store_true'), + ] + failures = ['-k3'] + successes = [ + ('', NS(x=None, y=False)), + ('-2', NS(x='-2', y=False)), + ('a', NS(x='a', y=False)), + ('-k4', NS(x=None, y=True)), + ('-k4 a', NS(x='a', y=True)), + ] + + +class TestEmptyAndSpaceContainingArguments(ParserTestCase): + + argument_signatures = [ + Sig('x', nargs='?'), + Sig('-y', '--yyy', dest='y'), + ] + failures = ['-y'] + successes = [ + ([''], NS(x='', y=None)), + (['a badger'], NS(x='a badger', y=None)), + (['-a badger'], NS(x='-a badger', y=None)), + (['-y', ''], NS(x=None, y='')), + (['-y', 'a badger'], NS(x=None, y='a badger')), + (['-y', '-a badger'], NS(x=None, y='-a badger')), + (['--yyy=a badger'], NS(x=None, y='a badger')), + (['--yyy=-a badger'], NS(x=None, y='-a badger')), + ] + + +class TestPrefixCharacterOnlyArguments(ParserTestCase): + + parser_signature = Sig(prefix_chars='-+') + argument_signatures = [ + Sig('-', dest='x', nargs='?', const='badger'), + Sig('+', dest='y', type=int, default=42), + Sig('-+-', dest='z', action='store_true'), + ] + failures = ['-y', '+ -'] + successes = [ + ('', NS(x=None, y=42, z=False)), + ('-', NS(x='badger', y=42, z=False)), + ('- X', NS(x='X', y=42, z=False)), + ('+ -3', NS(x=None, y=-3, z=False)), + ('-+-', NS(x=None, y=42, z=True)), + ('- ===', NS(x='===', y=42, z=False)), + ] + + +class TestNargsZeroOrMore(ParserTestCase): + """Tests specifying an args for an Optional that accepts zero or more""" + + argument_signatures = [Sig('-x', nargs='*'), Sig('y', nargs='*')] + failures = [] + successes = [ + ('', NS(x=None, y=[])), + ('-x', NS(x=[], y=[])), + ('-x a', NS(x=['a'], y=[])), + ('-x a -- b', NS(x=['a'], y=['b'])), + ('a', NS(x=None, y=['a'])), + ('a -x', NS(x=[], y=['a'])), + ('a -x b', NS(x=['b'], y=['a'])), + ] + + +class TestNargsRemainder(ParserTestCase): + """Tests specifying a positional with nargs=REMAINDER""" + + argument_signatures = [Sig('x'), Sig('y', nargs='...'), Sig('-z')] + failures = ['', '-z', '-z Z'] + successes = [ + ('X', NS(x='X', y=[], z=None)), + ('-z Z X', NS(x='X', y=[], z='Z')), + ('X A B -z Z', NS(x='X', y=['A', 'B', '-z', 'Z'], z=None)), + ('X Y --foo', NS(x='X', y=['Y', '--foo'], z=None)), + ] + + +class TestOptionLike(ParserTestCase): + """Tests options that may or may not be arguments""" + + argument_signatures = [ + Sig('-x', type=float), + Sig('-3', type=float, dest='y'), + Sig('z', nargs='*'), + ] + failures = ['-x', '-y2.5', '-xa', '-x -a', + '-x -3', '-x -3.5', '-3 -3.5', + '-x -2.5', '-x -2.5 a', '-3 -.5', + 'a x -1', '-x -1 a', '-3 -1 a'] + successes = [ + ('', NS(x=None, y=None, z=[])), + ('-x 2.5', NS(x=2.5, y=None, z=[])), + ('-x 2.5 a', NS(x=2.5, y=None, z=['a'])), + ('-3.5', NS(x=None, y=0.5, z=[])), + ('-3-.5', NS(x=None, y=-0.5, z=[])), + ('-3 .5', NS(x=None, y=0.5, z=[])), + ('a -3.5', NS(x=None, y=0.5, z=['a'])), + ('a', NS(x=None, y=None, z=['a'])), + ('a -x 1', NS(x=1.0, y=None, z=['a'])), + ('-x 1 a', NS(x=1.0, y=None, z=['a'])), + ('-3 1 a', NS(x=None, y=1.0, z=['a'])), + ] + + +class TestDefaultSuppress(ParserTestCase): + """Test actions with suppressed defaults""" + + argument_signatures = [ + Sig('foo', nargs='?', default=argparse.SUPPRESS), + Sig('bar', nargs='*', default=argparse.SUPPRESS), + Sig('--baz', action='store_true', default=argparse.SUPPRESS), + ] + failures = ['-x'] + successes = [ + ('', NS()), + ('a', NS(foo='a')), + ('a b', NS(foo='a', bar=['b'])), + ('--baz', NS(baz=True)), + ('a --baz', NS(foo='a', baz=True)), + ('--baz a b', NS(foo='a', bar=['b'], baz=True)), + ] + + +class TestParserDefaultSuppress(ParserTestCase): + """Test actions with a parser-level default of SUPPRESS""" + + parser_signature = Sig(argument_default=argparse.SUPPRESS) + argument_signatures = [ + Sig('foo', nargs='?'), + Sig('bar', nargs='*'), + Sig('--baz', action='store_true'), + ] + failures = ['-x'] + successes = [ + ('', NS()), + ('a', NS(foo='a')), + ('a b', NS(foo='a', bar=['b'])), + ('--baz', NS(baz=True)), + ('a --baz', NS(foo='a', baz=True)), + ('--baz a b', NS(foo='a', bar=['b'], baz=True)), + ] + + +class TestParserDefault42(ParserTestCase): + """Test actions with a parser-level default of 42""" + + parser_signature = Sig(argument_default=42, version='1.0') + argument_signatures = [ + Sig('foo', nargs='?'), + Sig('bar', nargs='*'), + Sig('--baz', action='store_true'), + ] + failures = ['-x'] + successes = [ + ('', NS(foo=42, bar=42, baz=42)), + ('a', NS(foo='a', bar=42, baz=42)), + ('a b', NS(foo='a', bar=['b'], baz=42)), + ('--baz', NS(foo=42, bar=42, baz=True)), + ('a --baz', NS(foo='a', bar=42, baz=True)), + ('--baz a b', NS(foo='a', bar=['b'], baz=True)), + ] + + +class TestArgumentsFromFile(TempDirMixin, ParserTestCase): + """Test reading arguments from a file""" + + def setUp(self): + super(TestArgumentsFromFile, self).setUp() + file_texts = [ + ('hello', 'hello world!\n'), + ('recursive', '-a\n' + 'A\n' + '@hello'), + ('invalid', '@no-such-path\n'), + ] + for path, text in file_texts: + file = open(path, 'w') + file.write(text) + file.close() + + parser_signature = Sig(fromfile_prefix_chars='@') + argument_signatures = [ + Sig('-a'), + Sig('x'), + Sig('y', nargs='+'), + ] + failures = ['', '-b', 'X', '@invalid', '@missing'] + successes = [ + ('X Y', NS(a=None, x='X', y=['Y'])), + ('X -a A Y Z', NS(a='A', x='X', y=['Y', 'Z'])), + ('@hello X', NS(a=None, x='hello world!', y=['X'])), + ('X @hello', NS(a=None, x='X', y=['hello world!'])), + ('-a B @recursive Y Z', NS(a='A', x='hello world!', y=['Y', 'Z'])), + ('X @recursive Z -a B', NS(a='B', x='X', y=['hello world!', 'Z'])), + ] + + +class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase): + """Test reading arguments from a file""" + + def setUp(self): + super(TestArgumentsFromFileConverter, self).setUp() + file_texts = [ + ('hello', 'hello world!\n'), + ] + for path, text in file_texts: + file = open(path, 'w') + file.write(text) + file.close() + + class FromFileConverterArgumentParser(ErrorRaisingArgumentParser): + + def convert_arg_line_to_args(self, arg_line): + for arg in arg_line.split(): + if not arg.strip(): + continue + yield arg + parser_class = FromFileConverterArgumentParser + parser_signature = Sig(fromfile_prefix_chars='@') + argument_signatures = [ + Sig('y', nargs='+'), + ] + failures = [] + successes = [ + ('@hello X', NS(y=['hello', 'world!', 'X'])), + ] + + +# ===================== +# Type conversion tests +# ===================== + +class TestFileTypeRepr(TestCase): + + def test_r(self): + type = argparse.FileType('r') + self.assertEqual("FileType('r')", repr(type)) + + def test_wb_1(self): + type = argparse.FileType('wb', 1) + self.assertEqual("FileType('wb', 1)", repr(type)) + + +class RFile(object): + seen = {} + + def __init__(self, name): + self.name = name + + __hash__ = None + + def __eq__(self, other): + if other in self.seen: + text = self.seen[other] + else: + text = self.seen[other] = other.read() + other.close() + if not isinstance(text, str): + text = text.decode('ascii') + return self.name == other.name == text + + +class TestFileTypeR(TempDirMixin, ParserTestCase): + """Test the FileType option/argument type for reading files""" + + def setUp(self): + super(TestFileTypeR, self).setUp() + for file_name in ['foo', 'bar']: + file = open(os.path.join(self.temp_dir, file_name), 'w') + file.write(file_name) + file.close() + self.create_readonly_file('readonly') + + argument_signatures = [ + Sig('-x', type=argparse.FileType()), + Sig('spam', type=argparse.FileType('r')), + ] + failures = ['-x', '-x bar', 'non-existent-file.txt'] + successes = [ + ('foo', NS(x=None, spam=RFile('foo'))), + ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), + ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), + ('-x - -', NS(x=sys.stdin, spam=sys.stdin)), + ('readonly', NS(x=None, spam=RFile('readonly'))), + ] + + +class TestFileTypeRB(TempDirMixin, ParserTestCase): + """Test the FileType option/argument type for reading files""" + + def setUp(self): + super(TestFileTypeRB, self).setUp() + for file_name in ['foo', 'bar']: + file = open(os.path.join(self.temp_dir, file_name), 'w') + file.write(file_name) + file.close() + + argument_signatures = [ + Sig('-x', type=argparse.FileType('rb')), + Sig('spam', type=argparse.FileType('rb')), + ] + failures = ['-x', '-x bar'] + successes = [ + ('foo', NS(x=None, spam=RFile('foo'))), + ('-x foo bar', NS(x=RFile('foo'), spam=RFile('bar'))), + ('bar -x foo', NS(x=RFile('foo'), spam=RFile('bar'))), + ('-x - -', NS(x=sys.stdin, spam=sys.stdin)), + ] + + +class WFile(object): + seen = set() + + def __init__(self, name): + self.name = name + + __hash__ = None + + def __eq__(self, other): + if other not in self.seen: + text = 'Check that file is writable.' + if 'b' in other.mode: + text = text.encode('ascii') + other.write(text) + other.close() + self.seen.add(other) + return self.name == other.name + + +class TestFileTypeW(TempDirMixin, ParserTestCase): + """Test the FileType option/argument type for writing files""" + + def setUp(self): + super(TestFileTypeW, self).setUp() + self.create_readonly_file('readonly') + + argument_signatures = [ + Sig('-x', type=argparse.FileType('w')), + Sig('spam', type=argparse.FileType('w')), + ] + failures = ['-x', '-x bar'] + failures = ['-x', '-x bar', 'readonly'] + successes = [ + ('foo', NS(x=None, spam=WFile('foo'))), + ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), + ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), + ('-x - -', NS(x=sys.stdout, spam=sys.stdout)), + ] + + +class TestFileTypeWB(TempDirMixin, ParserTestCase): + + argument_signatures = [ + Sig('-x', type=argparse.FileType('wb')), + Sig('spam', type=argparse.FileType('wb')), + ] + failures = ['-x', '-x bar'] + successes = [ + ('foo', NS(x=None, spam=WFile('foo'))), + ('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))), + ('bar -x foo', NS(x=WFile('foo'), spam=WFile('bar'))), + ('-x - -', NS(x=sys.stdout, spam=sys.stdout)), + ] + + +class TestTypeCallable(ParserTestCase): + """Test some callables as option/argument types""" + + argument_signatures = [ + Sig('--eggs', type=complex), + Sig('spam', type=float), + ] + failures = ['a', '42j', '--eggs a', '--eggs 2i'] + successes = [ + ('--eggs=42 42', NS(eggs=42, spam=42.0)), + ('--eggs 2j -- -1.5', NS(eggs=2j, spam=-1.5)), + ('1024.675', NS(eggs=None, spam=1024.675)), + ] + + +class TestTypeUserDefined(ParserTestCase): + """Test a user-defined option/argument type""" + + class MyType(TestCase): + + def __init__(self, value): + self.value = value + + __hash__ = None + + def __eq__(self, other): + return (type(self), self.value) == (type(other), other.value) + + argument_signatures = [ + Sig('-x', type=MyType), + Sig('spam', type=MyType), + ] + failures = [] + successes = [ + ('a -x b', NS(x=MyType('b'), spam=MyType('a'))), + ('-xf g', NS(x=MyType('f'), spam=MyType('g'))), + ] + + +class TestTypeClassicClass(ParserTestCase): + """Test a classic class type""" + + class C: + + def __init__(self, value): + self.value = value + + __hash__ = None + + def __eq__(self, other): + return (type(self), self.value) == (type(other), other.value) + + argument_signatures = [ + Sig('-x', type=C), + Sig('spam', type=C), + ] + failures = [] + successes = [ + ('a -x b', NS(x=C('b'), spam=C('a'))), + ('-xf g', NS(x=C('f'), spam=C('g'))), + ] + + +class TestTypeRegistration(TestCase): + """Test a user-defined type by registering it""" + + def test(self): + + def get_my_type(string): + return 'my_type{%s}' % string + + parser = argparse.ArgumentParser() + parser.register('type', 'my_type', get_my_type) + parser.add_argument('-x', type='my_type') + parser.add_argument('y', type='my_type') + + self.assertEqual(parser.parse_args('1'.split()), + NS(x=None, y='my_type{1}')) + self.assertEqual(parser.parse_args('-x 1 42'.split()), + NS(x='my_type{1}', y='my_type{42}')) + + +# ============ +# Action tests +# ============ + +class TestActionUserDefined(ParserTestCase): + """Test a user-defined option/argument action""" + + class OptionalAction(argparse.Action): + + def __call__(self, parser, namespace, value, option_string=None): + try: + # check destination and option string + assert self.dest == 'spam', 'dest: %s' % self.dest + assert option_string == '-s', 'flag: %s' % option_string + # when option is before argument, badger=2, and when + # option is after argument, badger= + expected_ns = NS(spam=0.25) + if value in [0.125, 0.625]: + expected_ns.badger = 2 + elif value in [2.0]: + expected_ns.badger = 84 + else: + raise AssertionError('value: %s' % value) + assert expected_ns == namespace, ('expected %s, got %s' % + (expected_ns, namespace)) + except AssertionError: + e = sys.exc_info()[1] + raise ArgumentParserError('opt_action failed: %s' % e) + setattr(namespace, 'spam', value) + + class PositionalAction(argparse.Action): + + def __call__(self, parser, namespace, value, option_string=None): + try: + assert option_string is None, ('option_string: %s' % + option_string) + # check destination + assert self.dest == 'badger', 'dest: %s' % self.dest + # when argument is before option, spam=0.25, and when + # option is after argument, spam= + expected_ns = NS(badger=2) + if value in [42, 84]: + expected_ns.spam = 0.25 + elif value in [1]: + expected_ns.spam = 0.625 + elif value in [2]: + expected_ns.spam = 0.125 + else: + raise AssertionError('value: %s' % value) + assert expected_ns == namespace, ('expected %s, got %s' % + (expected_ns, namespace)) + except AssertionError: + e = sys.exc_info()[1] + raise ArgumentParserError('arg_action failed: %s' % e) + setattr(namespace, 'badger', value) + + argument_signatures = [ + Sig('-s', dest='spam', action=OptionalAction, + type=float, default=0.25), + Sig('badger', action=PositionalAction, + type=int, nargs='?', default=2), + ] + failures = [] + successes = [ + ('-s0.125', NS(spam=0.125, badger=2)), + ('42', NS(spam=0.25, badger=42)), + ('-s 0.625 1', NS(spam=0.625, badger=1)), + ('84 -s2', NS(spam=2.0, badger=84)), + ] + + +class TestActionRegistration(TestCase): + """Test a user-defined action supplied by registering it""" + + class MyAction(argparse.Action): + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, 'foo[%s]' % values) + + def test(self): + + parser = argparse.ArgumentParser() + parser.register('action', 'my_action', self.MyAction) + parser.add_argument('badger', action='my_action') + + self.assertEqual(parser.parse_args(['1']), NS(badger='foo[1]')) + self.assertEqual(parser.parse_args(['42']), NS(badger='foo[42]')) + + +# ================ +# Subparsers tests +# ================ + +class TestAddSubparsers(TestCase): + """Test the add_subparsers method""" + + def assertArgumentParserError(self, *args, **kwargs): + self.assertRaises(ArgumentParserError, *args, **kwargs) + + def _get_parser(self, subparser_help=False, prefix_chars=None): + # create a parser with a subparsers argument + if prefix_chars: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description', prefix_chars=prefix_chars) + parser.add_argument( + prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') + else: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description') + parser.add_argument( + '--foo', action='store_true', help='foo help') + parser.add_argument( + 'bar', type=float, help='bar help') + + # check that only one subparsers argument can be added + subparsers = parser.add_subparsers(help='command help') + self.assertArgumentParserError(parser.add_subparsers) + + # add first sub-parser + parser1_kwargs = dict(description='1 description') + if subparser_help: + parser1_kwargs['help'] = '1 help' + parser1 = subparsers.add_parser('1', **parser1_kwargs) + parser1.add_argument('-w', type=int, help='w help') + parser1.add_argument('x', choices='abc', help='x help') + + # add second sub-parser + parser2_kwargs = dict(description='2 description') + if subparser_help: + parser2_kwargs['help'] = '2 help' + parser2 = subparsers.add_parser('2', **parser2_kwargs) + parser2.add_argument('-y', choices='123', help='y help') + parser2.add_argument('z', type=complex, nargs='*', help='z help') + + # return the main parser + return parser + + def setUp(self): + super(TestAddSubparsers, self).setUp() + self.parser = self._get_parser() + self.command_help_parser = self._get_parser(subparser_help=True) + + def test_parse_args_failures(self): + # check some failure cases: + for args_str in ['', 'a', 'a a', '0.5 a', '0.5 1', + '0.5 1 -y', '0.5 2 -w']: + args = args_str.split() + self.assertArgumentParserError(self.parser.parse_args, args) + + def test_parse_args(self): + # check some non-failure cases: + self.assertEqual( + self.parser.parse_args('0.5 1 b -w 7'.split()), + NS(foo=False, bar=0.5, w=7, x='b'), + ) + self.assertEqual( + self.parser.parse_args('0.25 --foo 2 -y 2 3j -- -1j'.split()), + NS(foo=True, bar=0.25, y='2', z=[3j, -1j]), + ) + self.assertEqual( + self.parser.parse_args('--foo 0.125 1 c'.split()), + NS(foo=True, bar=0.125, w=None, x='c'), + ) + + def test_parse_known_args(self): + self.assertEqual( + self.parser.parse_known_args('0.5 1 b -w 7'.split()), + (NS(foo=False, bar=0.5, w=7, x='b'), []), + ) + self.assertEqual( + self.parser.parse_known_args('0.5 -p 1 b -w 7'.split()), + (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), + ) + self.assertEqual( + self.parser.parse_known_args('0.5 1 b -w 7 -p'.split()), + (NS(foo=False, bar=0.5, w=7, x='b'), ['-p']), + ) + self.assertEqual( + self.parser.parse_known_args('0.5 1 b -q -rs -w 7'.split()), + (NS(foo=False, bar=0.5, w=7, x='b'), ['-q', '-rs']), + ) + self.assertEqual( + self.parser.parse_known_args('0.5 -W 1 b -X Y -w 7 Z'.split()), + (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']), + ) + + def test_dest(self): + parser = ErrorRaisingArgumentParser() + parser.add_argument('--foo', action='store_true') + subparsers = parser.add_subparsers(dest='bar') + parser1 = subparsers.add_parser('1') + parser1.add_argument('baz') + self.assertEqual(NS(foo=False, bar='1', baz='2'), + parser.parse_args('1 2'.split())) + + def test_help(self): + self.assertEqual(self.parser.format_usage(), + 'usage: PROG [-h] [--foo] bar {1,2} ...\n') + self.assertEqual(self.parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [--foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + -h, --help show this help message and exit + --foo foo help + ''')) + + def test_help_extra_prefix_chars(self): + # Make sure - is still used for help if it is a non-first prefix char + parser = self._get_parser(prefix_chars='+:-') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + -h, --help show this help message and exit + ++foo foo help + ''')) + + + def test_help_alternate_prefix_chars(self): + parser = self._get_parser(prefix_chars='+:/') + self.assertEqual(parser.format_usage(), + 'usage: PROG [+h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [+h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + +h, ++help show this help message and exit + ++foo foo help + ''')) + + def test_parser_command_help(self): + self.assertEqual(self.command_help_parser.format_usage(), + 'usage: PROG [-h] [--foo] bar {1,2} ...\n') + self.assertEqual(self.command_help_parser.format_help(), + textwrap.dedent('''\ + usage: PROG [-h] [--foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + 1 1 help + 2 2 help + + optional arguments: + -h, --help show this help message and exit + --foo foo help + ''')) + + def test_subparser_title_help(self): + parser = ErrorRaisingArgumentParser(prog='PROG', + description='main description') + parser.add_argument('--foo', action='store_true', help='foo help') + parser.add_argument('bar', help='bar help') + subparsers = parser.add_subparsers(title='subcommands', + description='command help', + help='additional text') + parser1 = subparsers.add_parser('1') + parser2 = subparsers.add_parser('2') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [--foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [--foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + + optional arguments: + -h, --help show this help message and exit + --foo foo help + + subcommands: + command help + + {1,2} additional text + ''')) + + def _test_subparser_help(self, args_str, expected_help): + try: + self.parser.parse_args(args_str.split()) + except ArgumentParserError: + err = sys.exc_info()[1] + if err.stdout != expected_help: + print(repr(expected_help)) + print(repr(err.stdout)) + self.assertEqual(err.stdout, expected_help) + + def test_subparser1_help(self): + self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\ + usage: PROG bar 1 [-h] [-w W] {a,b,c} + + 1 description + + positional arguments: + {a,b,c} x help + + optional arguments: + -h, --help show this help message and exit + -w W w help + ''')) + + def test_subparser2_help(self): + self._test_subparser_help('5.0 2 -h', textwrap.dedent('''\ + usage: PROG bar 2 [-h] [-y {1,2,3}] [z [z ...]] + + 2 description + + positional arguments: + z z help + + optional arguments: + -h, --help show this help message and exit + -y {1,2,3} y help + ''')) + +# ============ +# Groups tests +# ============ + +class TestPositionalsGroups(TestCase): + """Tests that order of group positionals matches construction order""" + + def test_nongroup_first(self): + parser = ErrorRaisingArgumentParser() + parser.add_argument('foo') + group = parser.add_argument_group('g') + group.add_argument('bar') + parser.add_argument('baz') + expected = NS(foo='1', bar='2', baz='3') + result = parser.parse_args('1 2 3'.split()) + self.assertEqual(expected, result) + + def test_group_first(self): + parser = ErrorRaisingArgumentParser() + group = parser.add_argument_group('xxx') + group.add_argument('foo') + parser.add_argument('bar') + parser.add_argument('baz') + expected = NS(foo='1', bar='2', baz='3') + result = parser.parse_args('1 2 3'.split()) + self.assertEqual(expected, result) + + def test_interleaved_groups(self): + parser = ErrorRaisingArgumentParser() + group = parser.add_argument_group('xxx') + parser.add_argument('foo') + group.add_argument('bar') + parser.add_argument('baz') + group = parser.add_argument_group('yyy') + group.add_argument('frell') + expected = NS(foo='1', bar='2', baz='3', frell='4') + result = parser.parse_args('1 2 3 4'.split()) + self.assertEqual(expected, result) + +# =================== +# Parent parser tests +# =================== + +class TestParentParsers(TestCase): + """Tests that parsers can be created with parent parsers""" + + def assertArgumentParserError(self, *args, **kwargs): + self.assertRaises(ArgumentParserError, *args, **kwargs) + + def setUp(self): + super(TestParentParsers, self).setUp() + self.wxyz_parent = ErrorRaisingArgumentParser(add_help=False) + self.wxyz_parent.add_argument('--w') + x_group = self.wxyz_parent.add_argument_group('x') + x_group.add_argument('-y') + self.wxyz_parent.add_argument('z') + + self.abcd_parent = ErrorRaisingArgumentParser(add_help=False) + self.abcd_parent.add_argument('a') + self.abcd_parent.add_argument('-b') + c_group = self.abcd_parent.add_argument_group('c') + c_group.add_argument('--d') + + self.w_parent = ErrorRaisingArgumentParser(add_help=False) + self.w_parent.add_argument('--w') + + self.z_parent = ErrorRaisingArgumentParser(add_help=False) + self.z_parent.add_argument('z') + + # parents with mutually exclusive groups + self.ab_mutex_parent = ErrorRaisingArgumentParser(add_help=False) + group = self.ab_mutex_parent.add_mutually_exclusive_group() + group.add_argument('-a', action='store_true') + group.add_argument('-b', action='store_true') + + self.main_program = os.path.basename(sys.argv[0]) + + def test_single_parent(self): + parser = ErrorRaisingArgumentParser(parents=[self.wxyz_parent]) + self.assertEqual(parser.parse_args('-y 1 2 --w 3'.split()), + NS(w='3', y='1', z='2')) + + def test_single_parent_mutex(self): + self._test_mutex_ab(self.ab_mutex_parent.parse_args) + parser = ErrorRaisingArgumentParser(parents=[self.ab_mutex_parent]) + self._test_mutex_ab(parser.parse_args) + + def test_single_granparent_mutex(self): + parents = [self.ab_mutex_parent] + parser = ErrorRaisingArgumentParser(add_help=False, parents=parents) + parser = ErrorRaisingArgumentParser(parents=[parser]) + self._test_mutex_ab(parser.parse_args) + + def _test_mutex_ab(self, parse_args): + self.assertEqual(parse_args([]), NS(a=False, b=False)) + self.assertEqual(parse_args(['-a']), NS(a=True, b=False)) + self.assertEqual(parse_args(['-b']), NS(a=False, b=True)) + self.assertArgumentParserError(parse_args, ['-a', '-b']) + self.assertArgumentParserError(parse_args, ['-b', '-a']) + self.assertArgumentParserError(parse_args, ['-c']) + self.assertArgumentParserError(parse_args, ['-a', '-c']) + self.assertArgumentParserError(parse_args, ['-b', '-c']) + + def test_multiple_parents(self): + parents = [self.abcd_parent, self.wxyz_parent] + parser = ErrorRaisingArgumentParser(parents=parents) + self.assertEqual(parser.parse_args('--d 1 --w 2 3 4'.split()), + NS(a='3', b=None, d='1', w='2', y=None, z='4')) + + def test_multiple_parents_mutex(self): + parents = [self.ab_mutex_parent, self.wxyz_parent] + parser = ErrorRaisingArgumentParser(parents=parents) + self.assertEqual(parser.parse_args('-a --w 2 3'.split()), + NS(a=True, b=False, w='2', y=None, z='3')) + self.assertArgumentParserError( + parser.parse_args, '-a --w 2 3 -b'.split()) + self.assertArgumentParserError( + parser.parse_args, '-a -b --w 2 3'.split()) + + def test_conflicting_parents(self): + self.assertRaises( + argparse.ArgumentError, + argparse.ArgumentParser, + parents=[self.w_parent, self.wxyz_parent]) + + def test_conflicting_parents_mutex(self): + self.assertRaises( + argparse.ArgumentError, + argparse.ArgumentParser, + parents=[self.abcd_parent, self.ab_mutex_parent]) + + def test_same_argument_name_parents(self): + parents = [self.wxyz_parent, self.z_parent] + parser = ErrorRaisingArgumentParser(parents=parents) + self.assertEqual(parser.parse_args('1 2'.split()), + NS(w=None, y=None, z='2')) + + def test_subparser_parents(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers() + abcde_parser = subparsers.add_parser('bar', parents=[self.abcd_parent]) + abcde_parser.add_argument('e') + self.assertEqual(parser.parse_args('bar -b 1 --d 2 3 4'.split()), + NS(a='3', b='1', d='2', e='4')) + + def test_subparser_parents_mutex(self): + parser = ErrorRaisingArgumentParser() + subparsers = parser.add_subparsers() + parents = [self.ab_mutex_parent] + abc_parser = subparsers.add_parser('foo', parents=parents) + c_group = abc_parser.add_argument_group('c_group') + c_group.add_argument('c') + parents = [self.wxyz_parent, self.ab_mutex_parent] + wxyzabe_parser = subparsers.add_parser('bar', parents=parents) + wxyzabe_parser.add_argument('e') + self.assertEqual(parser.parse_args('foo -a 4'.split()), + NS(a=True, b=False, c='4')) + self.assertEqual(parser.parse_args('bar -b --w 2 3 4'.split()), + NS(a=False, b=True, w='2', y=None, z='3', e='4')) + self.assertArgumentParserError( + parser.parse_args, 'foo -a -b 4'.split()) + self.assertArgumentParserError( + parser.parse_args, 'bar -b -a 4'.split()) + + def test_parent_help(self): + parents = [self.abcd_parent, self.wxyz_parent] + parser = ErrorRaisingArgumentParser(parents=parents) + parser_help = parser.format_help() + self.assertEqual(parser_help, textwrap.dedent('''\ + usage: {} [-h] [-b B] [--d D] [--w W] [-y Y] a z + + positional arguments: + a + z + + optional arguments: + -h, --help show this help message and exit + -b B + --w W + + c: + --d D + + x: + -y Y + '''.format(self.main_program))) + + def test_groups_parents(self): + parent = ErrorRaisingArgumentParser(add_help=False) + g = parent.add_argument_group(title='g', description='gd') + g.add_argument('-w') + g.add_argument('-x') + m = parent.add_mutually_exclusive_group() + m.add_argument('-y') + m.add_argument('-z') + parser = ErrorRaisingArgumentParser(parents=[parent]) + + self.assertRaises(ArgumentParserError, parser.parse_args, + ['-y', 'Y', '-z', 'Z']) + + parser_help = parser.format_help() + self.assertEqual(parser_help, textwrap.dedent('''\ + usage: {} [-h] [-w W] [-x X] [-y Y | -z Z] + + optional arguments: + -h, --help show this help message and exit + -y Y + -z Z + + g: + gd + + -w W + -x X + '''.format(self.main_program))) + +# ============================== +# Mutually exclusive group tests +# ============================== + +class TestMutuallyExclusiveGroupErrors(TestCase): + + def test_invalid_add_argument_group(self): + parser = ErrorRaisingArgumentParser() + raises = self.assertRaises + raises(TypeError, parser.add_mutually_exclusive_group, title='foo') + + def test_invalid_add_argument(self): + parser = ErrorRaisingArgumentParser() + group = parser.add_mutually_exclusive_group() + add_argument = group.add_argument + raises = self.assertRaises + raises(ValueError, add_argument, '--foo', required=True) + raises(ValueError, add_argument, 'bar') + raises(ValueError, add_argument, 'bar', nargs='+') + raises(ValueError, add_argument, 'bar', nargs=1) + raises(ValueError, add_argument, 'bar', nargs=argparse.PARSER) + + def test_help(self): + parser = ErrorRaisingArgumentParser(prog='PROG') + group1 = parser.add_mutually_exclusive_group() + group1.add_argument('--foo', action='store_true') + group1.add_argument('--bar', action='store_false') + group2 = parser.add_mutually_exclusive_group() + group2.add_argument('--soup', action='store_true') + group2.add_argument('--nuts', action='store_false') + expected = '''\ + usage: PROG [-h] [--foo | --bar] [--soup | --nuts] + + optional arguments: + -h, --help show this help message and exit + --foo + --bar + --soup + --nuts + ''' + self.assertEqual(parser.format_help(), textwrap.dedent(expected)) + +class MEMixin(object): + + def test_failures_when_not_required(self): + parse_args = self.get_parser(required=False).parse_args + error = ArgumentParserError + for args_string in self.failures: + self.assertRaises(error, parse_args, args_string.split()) + + def test_failures_when_required(self): + parse_args = self.get_parser(required=True).parse_args + error = ArgumentParserError + for args_string in self.failures + ['']: + self.assertRaises(error, parse_args, args_string.split()) + + def test_successes_when_not_required(self): + parse_args = self.get_parser(required=False).parse_args + successes = self.successes + self.successes_when_not_required + for args_string, expected_ns in successes: + actual_ns = parse_args(args_string.split()) + self.assertEqual(actual_ns, expected_ns) + + def test_successes_when_required(self): + parse_args = self.get_parser(required=True).parse_args + for args_string, expected_ns in self.successes: + actual_ns = parse_args(args_string.split()) + self.assertEqual(actual_ns, expected_ns) + + def test_usage_when_not_required(self): + format_usage = self.get_parser(required=False).format_usage + expected_usage = self.usage_when_not_required + self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) + + def test_usage_when_required(self): + format_usage = self.get_parser(required=True).format_usage + expected_usage = self.usage_when_required + self.assertEqual(format_usage(), textwrap.dedent(expected_usage)) + + def test_help_when_not_required(self): + format_help = self.get_parser(required=False).format_help + help = self.usage_when_not_required + self.help + self.assertEqual(format_help(), textwrap.dedent(help)) + + def test_help_when_required(self): + format_help = self.get_parser(required=True).format_help + help = self.usage_when_required + self.help + self.assertEqual(format_help(), textwrap.dedent(help)) + + +class TestMutuallyExclusiveSimple(MEMixin, TestCase): + + def get_parser(self, required=None): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('--bar', help='bar help') + group.add_argument('--baz', nargs='?', const='Z', help='baz help') + return parser + + failures = ['--bar X --baz Y', '--bar X --baz'] + successes = [ + ('--bar X', NS(bar='X', baz=None)), + ('--bar X --bar Z', NS(bar='Z', baz=None)), + ('--baz Y', NS(bar=None, baz='Y')), + ('--baz', NS(bar=None, baz='Z')), + ] + successes_when_not_required = [ + ('', NS(bar=None, baz=None)), + ] + + usage_when_not_required = '''\ + usage: PROG [-h] [--bar BAR | --baz [BAZ]] + ''' + usage_when_required = '''\ + usage: PROG [-h] (--bar BAR | --baz [BAZ]) + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + --bar BAR bar help + --baz [BAZ] baz help + ''' + + +class TestMutuallyExclusiveLong(MEMixin, TestCase): + + def get_parser(self, required=None): + parser = ErrorRaisingArgumentParser(prog='PROG') + parser.add_argument('--abcde', help='abcde help') + parser.add_argument('--fghij', help='fghij help') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('--klmno', help='klmno help') + group.add_argument('--pqrst', help='pqrst help') + return parser + + failures = ['--klmno X --pqrst Y'] + successes = [ + ('--klmno X', NS(abcde=None, fghij=None, klmno='X', pqrst=None)), + ('--abcde Y --klmno X', + NS(abcde='Y', fghij=None, klmno='X', pqrst=None)), + ('--pqrst X', NS(abcde=None, fghij=None, klmno=None, pqrst='X')), + ('--pqrst X --fghij Y', + NS(abcde=None, fghij='Y', klmno=None, pqrst='X')), + ] + successes_when_not_required = [ + ('', NS(abcde=None, fghij=None, klmno=None, pqrst=None)), + ] + + usage_when_not_required = '''\ + usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] + [--klmno KLMNO | --pqrst PQRST] + ''' + usage_when_required = '''\ + usage: PROG [-h] [--abcde ABCDE] [--fghij FGHIJ] + (--klmno KLMNO | --pqrst PQRST) + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + --abcde ABCDE abcde help + --fghij FGHIJ fghij help + --klmno KLMNO klmno help + --pqrst PQRST pqrst help + ''' + + +class TestMutuallyExclusiveFirstSuppressed(MEMixin, TestCase): + + def get_parser(self, required): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('-x', help=argparse.SUPPRESS) + group.add_argument('-y', action='store_false', help='y help') + return parser + + failures = ['-x X -y'] + successes = [ + ('-x X', NS(x='X', y=True)), + ('-x X -x Y', NS(x='Y', y=True)), + ('-y', NS(x=None, y=False)), + ] + successes_when_not_required = [ + ('', NS(x=None, y=True)), + ] + + usage_when_not_required = '''\ + usage: PROG [-h] [-y] + ''' + usage_when_required = '''\ + usage: PROG [-h] -y + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + -y y help + ''' + + +class TestMutuallyExclusiveManySuppressed(MEMixin, TestCase): + + def get_parser(self, required): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=required) + add = group.add_argument + add('--spam', action='store_true', help=argparse.SUPPRESS) + add('--badger', action='store_false', help=argparse.SUPPRESS) + add('--bladder', help=argparse.SUPPRESS) + return parser + + failures = [ + '--spam --badger', + '--badger --bladder B', + '--bladder B --spam', + ] + successes = [ + ('--spam', NS(spam=True, badger=True, bladder=None)), + ('--badger', NS(spam=False, badger=False, bladder=None)), + ('--bladder B', NS(spam=False, badger=True, bladder='B')), + ('--spam --spam', NS(spam=True, badger=True, bladder=None)), + ] + successes_when_not_required = [ + ('', NS(spam=False, badger=True, bladder=None)), + ] + + usage_when_required = usage_when_not_required = '''\ + usage: PROG [-h] + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + ''' + + +class TestMutuallyExclusiveOptionalAndPositional(MEMixin, TestCase): + + def get_parser(self, required): + parser = ErrorRaisingArgumentParser(prog='PROG') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('--foo', action='store_true', help='FOO') + group.add_argument('--spam', help='SPAM') + group.add_argument('badger', nargs='*', default='X', help='BADGER') + return parser + + failures = [ + '--foo --spam S', + '--spam S X', + 'X --foo', + 'X Y Z --spam S', + '--foo X Y', + ] + successes = [ + ('--foo', NS(foo=True, spam=None, badger='X')), + ('--spam S', NS(foo=False, spam='S', badger='X')), + ('X', NS(foo=False, spam=None, badger=['X'])), + ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])), + ] + successes_when_not_required = [ + ('', NS(foo=False, spam=None, badger='X')), + ] + + usage_when_not_required = '''\ + usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]] + ''' + usage_when_required = '''\ + usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...]) + ''' + help = '''\ + + positional arguments: + badger BADGER + + optional arguments: + -h, --help show this help message and exit + --foo FOO + --spam SPAM SPAM + ''' + + +class TestMutuallyExclusiveOptionalsMixed(MEMixin, TestCase): + + def get_parser(self, required): + parser = ErrorRaisingArgumentParser(prog='PROG') + parser.add_argument('-x', action='store_true', help='x help') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('-a', action='store_true', help='a help') + group.add_argument('-b', action='store_true', help='b help') + parser.add_argument('-y', action='store_true', help='y help') + group.add_argument('-c', action='store_true', help='c help') + return parser + + failures = ['-a -b', '-b -c', '-a -c', '-a -b -c'] + successes = [ + ('-a', NS(a=True, b=False, c=False, x=False, y=False)), + ('-b', NS(a=False, b=True, c=False, x=False, y=False)), + ('-c', NS(a=False, b=False, c=True, x=False, y=False)), + ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)), + ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)), + ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)), + ] + successes_when_not_required = [ + ('', NS(a=False, b=False, c=False, x=False, y=False)), + ('-x', NS(a=False, b=False, c=False, x=True, y=False)), + ('-y', NS(a=False, b=False, c=False, x=False, y=True)), + ] + + usage_when_required = usage_when_not_required = '''\ + usage: PROG [-h] [-x] [-a] [-b] [-y] [-c] + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + -x x help + -a a help + -b b help + -y y help + -c c help + ''' + + +class TestMutuallyExclusiveInGroup(MEMixin, TestCase): + + def get_parser(self, required=None): + parser = ErrorRaisingArgumentParser(prog='PROG') + titled_group = parser.add_argument_group( + title='Titled group', description='Group description') + mutex_group = \ + titled_group.add_mutually_exclusive_group(required=required) + mutex_group.add_argument('--bar', help='bar help') + mutex_group.add_argument('--baz', help='baz help') + return parser + + failures = ['--bar X --baz Y', '--baz X --bar Y'] + successes = [ + ('--bar X', NS(bar='X', baz=None)), + ('--baz Y', NS(bar=None, baz='Y')), + ] + successes_when_not_required = [ + ('', NS(bar=None, baz=None)), + ] + + usage_when_not_required = '''\ + usage: PROG [-h] [--bar BAR | --baz BAZ] + ''' + usage_when_required = '''\ + usage: PROG [-h] (--bar BAR | --baz BAZ) + ''' + help = '''\ + + optional arguments: + -h, --help show this help message and exit + + Titled group: + Group description + + --bar BAR bar help + --baz BAZ baz help + ''' + + +class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase): + + def get_parser(self, required): + parser = ErrorRaisingArgumentParser(prog='PROG') + parser.add_argument('x', help='x help') + parser.add_argument('-y', action='store_true', help='y help') + group = parser.add_mutually_exclusive_group(required=required) + group.add_argument('a', nargs='?', help='a help') + group.add_argument('-b', action='store_true', help='b help') + group.add_argument('-c', action='store_true', help='c help') + return parser + + failures = ['X A -b', '-b -c', '-c X A'] + successes = [ + ('X A', NS(a='A', b=False, c=False, x='X', y=False)), + ('X -b', NS(a=None, b=True, c=False, x='X', y=False)), + ('X -c', NS(a=None, b=False, c=True, x='X', y=False)), + ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)), + ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)), + ] + successes_when_not_required = [ + ('X', NS(a=None, b=False, c=False, x='X', y=False)), + ('X -y', NS(a=None, b=False, c=False, x='X', y=True)), + ] + + usage_when_required = usage_when_not_required = '''\ + usage: PROG [-h] [-y] [-b] [-c] x [a] + ''' + help = '''\ + + positional arguments: + x x help + a a help + + optional arguments: + -h, --help show this help message and exit + -y y help + -b b help + -c c help + ''' + +# ================================================= +# Mutually exclusive group in parent parser tests +# ================================================= + +class MEPBase(object): + + def get_parser(self, required=None): + parent = super(MEPBase, self).get_parser(required=required) + parser = ErrorRaisingArgumentParser( + prog=parent.prog, add_help=False, parents=[parent]) + return parser + + +class TestMutuallyExclusiveGroupErrorsParent( + MEPBase, TestMutuallyExclusiveGroupErrors): + pass + + +class TestMutuallyExclusiveSimpleParent( + MEPBase, TestMutuallyExclusiveSimple): + pass + + +class TestMutuallyExclusiveLongParent( + MEPBase, TestMutuallyExclusiveLong): + pass + + +class TestMutuallyExclusiveFirstSuppressedParent( + MEPBase, TestMutuallyExclusiveFirstSuppressed): + pass + + +class TestMutuallyExclusiveManySuppressedParent( + MEPBase, TestMutuallyExclusiveManySuppressed): + pass + + +class TestMutuallyExclusiveOptionalAndPositionalParent( + MEPBase, TestMutuallyExclusiveOptionalAndPositional): + pass + + +class TestMutuallyExclusiveOptionalsMixedParent( + MEPBase, TestMutuallyExclusiveOptionalsMixed): + pass + + +class TestMutuallyExclusiveOptionalsAndPositionalsMixedParent( + MEPBase, TestMutuallyExclusiveOptionalsAndPositionalsMixed): + pass + +# ================= +# Set default tests +# ================= + +class TestSetDefaults(TestCase): + + def test_set_defaults_no_args(self): + parser = ErrorRaisingArgumentParser() + parser.set_defaults(x='foo') + parser.set_defaults(y='bar', z=1) + self.assertEqual(NS(x='foo', y='bar', z=1), + parser.parse_args([])) + self.assertEqual(NS(x='foo', y='bar', z=1), + parser.parse_args([], NS())) + self.assertEqual(NS(x='baz', y='bar', z=1), + parser.parse_args([], NS(x='baz'))) + self.assertEqual(NS(x='baz', y='bar', z=2), + parser.parse_args([], NS(x='baz', z=2))) + + def test_set_defaults_with_args(self): + parser = ErrorRaisingArgumentParser() + parser.set_defaults(x='foo', y='bar') + parser.add_argument('-x', default='xfoox') + self.assertEqual(NS(x='xfoox', y='bar'), + parser.parse_args([])) + self.assertEqual(NS(x='xfoox', y='bar'), + parser.parse_args([], NS())) + self.assertEqual(NS(x='baz', y='bar'), + parser.parse_args([], NS(x='baz'))) + self.assertEqual(NS(x='1', y='bar'), + parser.parse_args('-x 1'.split())) + self.assertEqual(NS(x='1', y='bar'), + parser.parse_args('-x 1'.split(), NS())) + self.assertEqual(NS(x='1', y='bar'), + parser.parse_args('-x 1'.split(), NS(x='baz'))) + + def test_set_defaults_subparsers(self): + parser = ErrorRaisingArgumentParser() + parser.set_defaults(x='foo') + subparsers = parser.add_subparsers() + parser_a = subparsers.add_parser('a') + parser_a.set_defaults(y='bar') + self.assertEqual(NS(x='foo', y='bar'), + parser.parse_args('a'.split())) + + def test_set_defaults_parents(self): + parent = ErrorRaisingArgumentParser(add_help=False) + parent.set_defaults(x='foo') + parser = ErrorRaisingArgumentParser(parents=[parent]) + self.assertEqual(NS(x='foo'), parser.parse_args([])) + + def test_set_defaults_same_as_add_argument(self): + parser = ErrorRaisingArgumentParser() + parser.set_defaults(w='W', x='X', y='Y', z='Z') + parser.add_argument('-w') + parser.add_argument('-x', default='XX') + parser.add_argument('y', nargs='?') + parser.add_argument('z', nargs='?', default='ZZ') + + # defaults set previously + self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), + parser.parse_args([])) + + # reset defaults + parser.set_defaults(w='WW', x='X', y='YY', z='Z') + self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), + parser.parse_args([])) + + def test_set_defaults_same_as_add_argument_group(self): + parser = ErrorRaisingArgumentParser() + parser.set_defaults(w='W', x='X', y='Y', z='Z') + group = parser.add_argument_group('foo') + group.add_argument('-w') + group.add_argument('-x', default='XX') + group.add_argument('y', nargs='?') + group.add_argument('z', nargs='?', default='ZZ') + + + # defaults set previously + self.assertEqual(NS(w='W', x='XX', y='Y', z='ZZ'), + parser.parse_args([])) + + # reset defaults + parser.set_defaults(w='WW', x='X', y='YY', z='Z') + self.assertEqual(NS(w='WW', x='X', y='YY', z='Z'), + parser.parse_args([])) + +# ================= +# Get default tests +# ================= + +class TestGetDefault(TestCase): + + def test_get_default(self): + parser = ErrorRaisingArgumentParser() + self.assertEqual(None, parser.get_default("foo")) + self.assertEqual(None, parser.get_default("bar")) + + parser.add_argument("--foo") + self.assertEqual(None, parser.get_default("foo")) + self.assertEqual(None, parser.get_default("bar")) + + parser.add_argument("--bar", type=int, default=42) + self.assertEqual(None, parser.get_default("foo")) + self.assertEqual(42, parser.get_default("bar")) + + parser.set_defaults(foo="badger") + self.assertEqual("badger", parser.get_default("foo")) + self.assertEqual(42, parser.get_default("bar")) + +# ========================== +# Namespace 'contains' tests +# ========================== + +class TestNamespaceContainsSimple(TestCase): + + def test_empty(self): + ns = argparse.Namespace() + self.assertEqual('' in ns, False) + self.assertEqual('' not in ns, True) + self.assertEqual('x' in ns, False) + + def test_non_empty(self): + ns = argparse.Namespace(x=1, y=2) + self.assertEqual('x' in ns, True) + self.assertEqual('x' not in ns, False) + self.assertEqual('y' in ns, True) + self.assertEqual('' in ns, False) + self.assertEqual('xx' in ns, False) + self.assertEqual('z' in ns, False) + +# ===================== +# Help formatting tests +# ===================== + +class TestHelpFormattingMetaclass(type): + + def __init__(cls, name, bases, bodydict): + if name == 'HelpTestCase': + return + + class AddTests(object): + + def __init__(self, test_class, func_suffix, std_name): + self.func_suffix = func_suffix + self.std_name = std_name + + for test_func in [self.test_format, + self.test_print, + self.test_print_file]: + test_name = '%s_%s' % (test_func.__name__, func_suffix) + + def test_wrapper(self, test_func=test_func): + test_func(self) + try: + test_wrapper.__name__ = test_name + except TypeError: + pass + setattr(test_class, test_name, test_wrapper) + + def _get_parser(self, tester): + parser = argparse.ArgumentParser( + *tester.parser_signature.args, + **tester.parser_signature.kwargs) + for argument_sig in getattr(tester, 'argument_signatures', []): + parser.add_argument(*argument_sig.args, + **argument_sig.kwargs) + group_sigs = getattr(tester, 'argument_group_signatures', []) + for group_sig, argument_sigs in group_sigs: + group = parser.add_argument_group(*group_sig.args, + **group_sig.kwargs) + for argument_sig in argument_sigs: + group.add_argument(*argument_sig.args, + **argument_sig.kwargs) + subparsers_sigs = getattr(tester, 'subparsers_signatures', []) + if subparsers_sigs: + subparsers = parser.add_subparsers() + for subparser_sig in subparsers_sigs: + subparsers.add_parser(*subparser_sig.args, + **subparser_sig.kwargs) + return parser + + def _test(self, tester, parser_text): + expected_text = getattr(tester, self.func_suffix) + expected_text = textwrap.dedent(expected_text) + if expected_text != parser_text: + print(repr(expected_text)) + print(repr(parser_text)) + for char1, char2 in zip(expected_text, parser_text): + if char1 != char2: + print('first diff: %r %r' % (char1, char2)) + break + tester.assertEqual(expected_text, parser_text) + + def test_format(self, tester): + parser = self._get_parser(tester) + format = getattr(parser, 'format_%s' % self.func_suffix) + self._test(tester, format()) + + def test_print(self, tester): + parser = self._get_parser(tester) + print_ = getattr(parser, 'print_%s' % self.func_suffix) + old_stream = getattr(sys, self.std_name) + setattr(sys, self.std_name, StdIOBuffer()) + try: + print_() + parser_text = getattr(sys, self.std_name).getvalue() + finally: + setattr(sys, self.std_name, old_stream) + self._test(tester, parser_text) + + def test_print_file(self, tester): + parser = self._get_parser(tester) + print_ = getattr(parser, 'print_%s' % self.func_suffix) + sfile = StdIOBuffer() + print_(sfile) + parser_text = sfile.getvalue() + self._test(tester, parser_text) + + # add tests for {format,print}_{usage,help,version} + for func_suffix, std_name in [('usage', 'stdout'), + ('help', 'stdout'), + ('version', 'stderr')]: + AddTests(cls, func_suffix, std_name) + +bases = TestCase, +HelpTestCase = TestHelpFormattingMetaclass('HelpTestCase', bases, {}) + + +class TestHelpBiggerOptionals(HelpTestCase): + """Make sure that argument help aligns when options are longer""" + + parser_signature = Sig(prog='PROG', description='DESCRIPTION', + epilog='EPILOG', version='0.1') + argument_signatures = [ + Sig('-x', action='store_true', help='X HELP'), + Sig('--y', help='Y HELP'), + Sig('foo', help='FOO HELP'), + Sig('bar', help='BAR HELP'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-v] [-x] [--y Y] foo bar + ''' + help = usage + '''\ + + DESCRIPTION + + positional arguments: + foo FOO HELP + bar BAR HELP + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + -x X HELP + --y Y Y HELP + + EPILOG + ''' + version = '''\ + 0.1 + ''' + + +class TestHelpBiggerOptionalGroups(HelpTestCase): + """Make sure that argument help aligns when options are longer""" + + parser_signature = Sig(prog='PROG', description='DESCRIPTION', + epilog='EPILOG', version='0.1') + argument_signatures = [ + Sig('-x', action='store_true', help='X HELP'), + Sig('--y', help='Y HELP'), + Sig('foo', help='FOO HELP'), + Sig('bar', help='BAR HELP'), + ] + argument_group_signatures = [ + (Sig('GROUP TITLE', description='GROUP DESCRIPTION'), [ + Sig('baz', help='BAZ HELP'), + Sig('-z', nargs='+', help='Z HELP')]), + ] + usage = '''\ + usage: PROG [-h] [-v] [-x] [--y Y] [-z Z [Z ...]] foo bar baz + ''' + help = usage + '''\ + + DESCRIPTION + + positional arguments: + foo FOO HELP + bar BAR HELP + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + -x X HELP + --y Y Y HELP + + GROUP TITLE: + GROUP DESCRIPTION + + baz BAZ HELP + -z Z [Z ...] Z HELP + + EPILOG + ''' + version = '''\ + 0.1 + ''' + + +class TestHelpBiggerPositionals(HelpTestCase): + """Make sure that help aligns when arguments are longer""" + + parser_signature = Sig(usage='USAGE', description='DESCRIPTION') + argument_signatures = [ + Sig('-x', action='store_true', help='X HELP'), + Sig('--y', help='Y HELP'), + Sig('ekiekiekifekang', help='EKI HELP'), + Sig('bar', help='BAR HELP'), + ] + argument_group_signatures = [] + usage = '''\ + usage: USAGE + ''' + help = usage + '''\ + + DESCRIPTION + + positional arguments: + ekiekiekifekang EKI HELP + bar BAR HELP + + optional arguments: + -h, --help show this help message and exit + -x X HELP + --y Y Y HELP + ''' + + version = '' + + +class TestHelpReformatting(HelpTestCase): + """Make sure that text after short names starts on the first line""" + + parser_signature = Sig( + prog='PROG', + description=' oddly formatted\n' + 'description\n' + '\n' + 'that is so long that it should go onto multiple ' + 'lines when wrapped') + argument_signatures = [ + Sig('-x', metavar='XX', help='oddly\n' + ' formatted -x help'), + Sig('y', metavar='yyy', help='normal y help'), + ] + argument_group_signatures = [ + (Sig('title', description='\n' + ' oddly formatted group\n' + '\n' + 'description'), + [Sig('-a', action='store_true', + help=' oddly \n' + 'formatted -a help \n' + ' again, so long that it should be wrapped over ' + 'multiple lines')]), + ] + usage = '''\ + usage: PROG [-h] [-x XX] [-a] yyy + ''' + help = usage + '''\ + + oddly formatted description that is so long that it should go onto \ +multiple + lines when wrapped + + positional arguments: + yyy normal y help + + optional arguments: + -h, --help show this help message and exit + -x XX oddly formatted -x help + + title: + oddly formatted group description + + -a oddly formatted -a help again, so long that it should \ +be wrapped + over multiple lines + ''' + version = '' + + +class TestHelpWrappingShortNames(HelpTestCase): + """Make sure that text after short names starts on the first line""" + + parser_signature = Sig(prog='PROG', description= 'D\nD' * 30) + argument_signatures = [ + Sig('-x', metavar='XX', help='XHH HX' * 20), + Sig('y', metavar='yyy', help='YH YH' * 20), + ] + argument_group_signatures = [ + (Sig('ALPHAS'), [ + Sig('-a', action='store_true', help='AHHH HHA' * 10)]), + ] + usage = '''\ + usage: PROG [-h] [-x XX] [-a] yyy + ''' + help = usage + '''\ + + D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ +DD DD DD + DD DD DD DD D + + positional arguments: + yyy YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ +YHYH YHYH + YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH + + optional arguments: + -h, --help show this help message and exit + -x XX XHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH \ +HXXHH HXXHH + HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HXXHH HX + + ALPHAS: + -a AHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH HHAAHHH \ +HHAAHHH + HHAAHHH HHAAHHH HHA + ''' + version = '' + + +class TestHelpWrappingLongNames(HelpTestCase): + """Make sure that text after long names starts on the next line""" + + parser_signature = Sig(usage='USAGE', description= 'D D' * 30, + version='V V'*30) + argument_signatures = [ + Sig('-x', metavar='X' * 25, help='XH XH' * 20), + Sig('y', metavar='y' * 25, help='YH YH' * 20), + ] + argument_group_signatures = [ + (Sig('ALPHAS'), [ + Sig('-a', metavar='A' * 25, help='AH AH' * 20), + Sig('z', metavar='z' * 25, help='ZH ZH' * 20)]), + ] + usage = '''\ + usage: USAGE + ''' + help = usage + '''\ + + D DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD \ +DD DD DD + DD DD DD DD D + + positional arguments: + yyyyyyyyyyyyyyyyyyyyyyyyy + YH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH \ +YHYH YHYH + YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YHYH YH + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + -x XXXXXXXXXXXXXXXXXXXXXXXXX + XH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH \ +XHXH XHXH + XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XHXH XH + + ALPHAS: + -a AAAAAAAAAAAAAAAAAAAAAAAAA + AH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH \ +AHAH AHAH + AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AHAH AH + zzzzzzzzzzzzzzzzzzzzzzzzz + ZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH \ +ZHZH ZHZH + ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZHZH ZH + ''' + version = '''\ + V VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV VV \ +VV VV VV + VV VV VV VV V + ''' + + +class TestHelpUsage(HelpTestCase): + """Test basic usage messages""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-w', nargs='+', help='w'), + Sig('-x', nargs='*', help='x'), + Sig('a', help='a'), + Sig('b', help='b', nargs=2), + Sig('c', help='c', nargs='?'), + ] + argument_group_signatures = [ + (Sig('group'), [ + Sig('-y', nargs='?', help='y'), + Sig('-z', nargs=3, help='z'), + Sig('d', help='d', nargs='*'), + Sig('e', help='e', nargs='+'), + ]) + ] + usage = '''\ + usage: PROG [-h] [-w W [W ...]] [-x [X [X ...]]] [-y [Y]] [-z Z Z Z] + a b b [c] [d [d ...]] e [e ...] + ''' + help = usage + '''\ + + positional arguments: + a a + b b + c c + + optional arguments: + -h, --help show this help message and exit + -w W [W ...] w + -x [X [X ...]] x + + group: + -y [Y] y + -z Z Z Z z + d d + e e + ''' + version = '' + + +class TestHelpOnlyUserGroups(HelpTestCase): + """Test basic usage messages""" + + parser_signature = Sig(prog='PROG', add_help=False) + argument_signatures = [] + argument_group_signatures = [ + (Sig('xxxx'), [ + Sig('-x', help='x'), + Sig('a', help='a'), + ]), + (Sig('yyyy'), [ + Sig('b', help='b'), + Sig('-y', help='y'), + ]), + ] + usage = '''\ + usage: PROG [-x X] [-y Y] a b + ''' + help = usage + '''\ + + xxxx: + -x X x + a a + + yyyy: + b b + -y Y y + ''' + version = '' + + +class TestHelpUsageLongProg(HelpTestCase): + """Test usage messages where the prog is long""" + + parser_signature = Sig(prog='P' * 60) + argument_signatures = [ + Sig('-w', metavar='W'), + Sig('-x', metavar='X'), + Sig('a'), + Sig('b'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP + [-h] [-w W] [-x X] a b + ''' + help = usage + '''\ + + positional arguments: + a + b + + optional arguments: + -h, --help show this help message and exit + -w W + -x X + ''' + version = '' + + +class TestHelpUsageLongProgOptionsWrap(HelpTestCase): + """Test usage messages where the prog is long and the optionals wrap""" + + parser_signature = Sig(prog='P' * 60) + argument_signatures = [ + Sig('-w', metavar='W' * 25), + Sig('-x', metavar='X' * 25), + Sig('-y', metavar='Y' * 25), + Sig('-z', metavar='Z' * 25), + Sig('a'), + Sig('b'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP + [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ +[-x XXXXXXXXXXXXXXXXXXXXXXXXX] + [-y YYYYYYYYYYYYYYYYYYYYYYYYY] [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] + a b + ''' + help = usage + '''\ + + positional arguments: + a + b + + optional arguments: + -h, --help show this help message and exit + -w WWWWWWWWWWWWWWWWWWWWWWWWW + -x XXXXXXXXXXXXXXXXXXXXXXXXX + -y YYYYYYYYYYYYYYYYYYYYYYYYY + -z ZZZZZZZZZZZZZZZZZZZZZZZZZ + ''' + version = '' + + +class TestHelpUsageLongProgPositionalsWrap(HelpTestCase): + """Test usage messages where the prog is long and the positionals wrap""" + + parser_signature = Sig(prog='P' * 60, add_help=False) + argument_signatures = [ + Sig('a' * 25), + Sig('b' * 25), + Sig('c' * 25), + ] + argument_group_signatures = [] + usage = '''\ + usage: PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP + aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + help = usage + '''\ + + positional arguments: + aaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + version = '' + + +class TestHelpUsageOptionalsWrap(HelpTestCase): + """Test usage messages where the optionals wrap""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-w', metavar='W' * 25), + Sig('-x', metavar='X' * 25), + Sig('-y', metavar='Y' * 25), + Sig('-z', metavar='Z' * 25), + Sig('a'), + Sig('b'), + Sig('c'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-w WWWWWWWWWWWWWWWWWWWWWWWWW] \ +[-x XXXXXXXXXXXXXXXXXXXXXXXXX] + [-y YYYYYYYYYYYYYYYYYYYYYYYYY] \ +[-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] + a b c + ''' + help = usage + '''\ + + positional arguments: + a + b + c + + optional arguments: + -h, --help show this help message and exit + -w WWWWWWWWWWWWWWWWWWWWWWWWW + -x XXXXXXXXXXXXXXXXXXXXXXXXX + -y YYYYYYYYYYYYYYYYYYYYYYYYY + -z ZZZZZZZZZZZZZZZZZZZZZZZZZ + ''' + version = '' + + +class TestHelpUsagePositionalsWrap(HelpTestCase): + """Test usage messages where the positionals wrap""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-x'), + Sig('-y'), + Sig('-z'), + Sig('a' * 25), + Sig('b' * 25), + Sig('c' * 25), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-x X] [-y Y] [-z Z] + aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + help = usage + '''\ + + positional arguments: + aaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + + optional arguments: + -h, --help show this help message and exit + -x X + -y Y + -z Z + ''' + version = '' + + +class TestHelpUsageOptionalsPositionalsWrap(HelpTestCase): + """Test usage messages where the optionals and positionals wrap""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-x', metavar='X' * 25), + Sig('-y', metavar='Y' * 25), + Sig('-z', metavar='Z' * 25), + Sig('a' * 25), + Sig('b' * 25), + Sig('c' * 25), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ +[-y YYYYYYYYYYYYYYYYYYYYYYYYY] + [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] + aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + help = usage + '''\ + + positional arguments: + aaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + + optional arguments: + -h, --help show this help message and exit + -x XXXXXXXXXXXXXXXXXXXXXXXXX + -y YYYYYYYYYYYYYYYYYYYYYYYYY + -z ZZZZZZZZZZZZZZZZZZZZZZZZZ + ''' + version = '' + + +class TestHelpUsageOptionalsOnlyWrap(HelpTestCase): + """Test usage messages where there are only optionals and they wrap""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-x', metavar='X' * 25), + Sig('-y', metavar='Y' * 25), + Sig('-z', metavar='Z' * 25), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-x XXXXXXXXXXXXXXXXXXXXXXXXX] \ +[-y YYYYYYYYYYYYYYYYYYYYYYYYY] + [-z ZZZZZZZZZZZZZZZZZZZZZZZZZ] + ''' + help = usage + '''\ + + optional arguments: + -h, --help show this help message and exit + -x XXXXXXXXXXXXXXXXXXXXXXXXX + -y YYYYYYYYYYYYYYYYYYYYYYYYY + -z ZZZZZZZZZZZZZZZZZZZZZZZZZ + ''' + version = '' + + +class TestHelpUsagePositionalsOnlyWrap(HelpTestCase): + """Test usage messages where there are only positionals and they wrap""" + + parser_signature = Sig(prog='PROG', add_help=False) + argument_signatures = [ + Sig('a' * 25), + Sig('b' * 25), + Sig('c' * 25), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG aaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + help = usage + '''\ + + positional arguments: + aaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccc + ''' + version = '' + + +class TestHelpVariableExpansion(HelpTestCase): + """Test that variables are expanded properly in help messages""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-x', type=int, + help='x %(prog)s %(default)s %(type)s %%'), + Sig('-y', action='store_const', default=42, const='XXX', + help='y %(prog)s %(default)s %(const)s'), + Sig('--foo', choices='abc', + help='foo %(prog)s %(default)s %(choices)s'), + Sig('--bar', default='baz', choices=[1, 2], metavar='BBB', + help='bar %(prog)s %(default)s %(dest)s'), + Sig('spam', help='spam %(prog)s %(default)s'), + Sig('badger', default=0.5, help='badger %(prog)s %(default)s'), + ] + argument_group_signatures = [ + (Sig('group'), [ + Sig('-a', help='a %(prog)s %(default)s'), + Sig('-b', default=-1, help='b %(prog)s %(default)s'), + ]) + ] + usage = ('''\ + usage: PROG [-h] [-x X] [-y] [--foo {a,b,c}] [--bar BBB] [-a A] [-b B] + spam badger + ''') + help = usage + '''\ + + positional arguments: + spam spam PROG None + badger badger PROG 0.5 + + optional arguments: + -h, --help show this help message and exit + -x X x PROG None int % + -y y PROG 42 XXX + --foo {a,b,c} foo PROG None a, b, c + --bar BBB bar PROG baz bar + + group: + -a A a PROG None + -b B b PROG -1 + ''' + version = '' + + +class TestHelpVariableExpansionUsageSupplied(HelpTestCase): + """Test that variables are expanded properly when usage= is present""" + + parser_signature = Sig(prog='PROG', usage='%(prog)s FOO') + argument_signatures = [] + argument_group_signatures = [] + usage = ('''\ + usage: PROG FOO + ''') + help = usage + '''\ + + optional arguments: + -h, --help show this help message and exit + ''' + version = '' + + +class TestHelpVariableExpansionNoArguments(HelpTestCase): + """Test that variables are expanded properly with no arguments""" + + parser_signature = Sig(prog='PROG', add_help=False) + argument_signatures = [] + argument_group_signatures = [] + usage = ('''\ + usage: PROG + ''') + help = usage + version = '' + + +class TestHelpSuppressUsage(HelpTestCase): + """Test that items can be suppressed in usage messages""" + + parser_signature = Sig(prog='PROG', usage=argparse.SUPPRESS) + argument_signatures = [ + Sig('--foo', help='foo help'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [] + help = '''\ + positional arguments: + spam spam help + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help + ''' + usage = '' + version = '' + + +class TestHelpSuppressOptional(HelpTestCase): + """Test that optional arguments can be suppressed in help messages""" + + parser_signature = Sig(prog='PROG', add_help=False) + argument_signatures = [ + Sig('--foo', help=argparse.SUPPRESS), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG spam + ''' + help = usage + '''\ + + positional arguments: + spam spam help + ''' + version = '' + + +class TestHelpSuppressOptionalGroup(HelpTestCase): + """Test that optional groups can be suppressed in help messages""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('--foo', help='foo help'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [ + (Sig('group'), [Sig('--bar', help=argparse.SUPPRESS)]), + ] + usage = '''\ + usage: PROG [-h] [--foo FOO] spam + ''' + help = usage + '''\ + + positional arguments: + spam spam help + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help + ''' + version = '' + + +class TestHelpSuppressPositional(HelpTestCase): + """Test that positional arguments can be suppressed in help messages""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('--foo', help='foo help'), + Sig('spam', help=argparse.SUPPRESS), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [--foo FOO] + ''' + help = usage + '''\ + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help + ''' + version = '' + + +class TestHelpRequiredOptional(HelpTestCase): + """Test that required options don't look optional""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('--foo', required=True, help='foo help'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] --foo FOO + ''' + help = usage + '''\ + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help + ''' + version = '' + + +class TestHelpAlternatePrefixChars(HelpTestCase): + """Test that options display with different prefix characters""" + + parser_signature = Sig(prog='PROG', prefix_chars='^;', add_help=False) + argument_signatures = [ + Sig('^^foo', action='store_true', help='foo help'), + Sig(';b', ';;bar', help='bar help'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [^^foo] [;b BAR] + ''' + help = usage + '''\ + + optional arguments: + ^^foo foo help + ;b BAR, ;;bar BAR bar help + ''' + version = '' + + +class TestHelpNoHelpOptional(HelpTestCase): + """Test that the --help argument can be suppressed help messages""" + + parser_signature = Sig(prog='PROG', add_help=False) + argument_signatures = [ + Sig('--foo', help='foo help'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [--foo FOO] spam + ''' + help = usage + '''\ + + positional arguments: + spam spam help + + optional arguments: + --foo FOO foo help + ''' + version = '' + + +class TestHelpVersionOptional(HelpTestCase): + """Test that the --version argument can be suppressed help messages""" + + parser_signature = Sig(prog='PROG', version='1.0') + argument_signatures = [ + Sig('--foo', help='foo help'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-v] [--foo FOO] spam + ''' + help = usage + '''\ + + positional arguments: + spam spam help + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + --foo FOO foo help + ''' + version = '''\ + 1.0 + ''' + + +class TestHelpNone(HelpTestCase): + """Test that no errors occur if no help is specified""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('--foo'), + Sig('spam'), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [--foo FOO] spam + ''' + help = usage + '''\ + + positional arguments: + spam + + optional arguments: + -h, --help show this help message and exit + --foo FOO + ''' + version = '' + + +class TestHelpTupleMetavar(HelpTestCase): + """Test specifying metavar as a tuple""" + + parser_signature = Sig(prog='PROG') + argument_signatures = [ + Sig('-w', help='w', nargs='+', metavar=('W1', 'W2')), + Sig('-x', help='x', nargs='*', metavar=('X1', 'X2')), + Sig('-y', help='y', nargs=3, metavar=('Y1', 'Y2', 'Y3')), + Sig('-z', help='z', nargs='?', metavar=('Z1', )), + ] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-w W1 [W2 ...]] [-x [X1 [X2 ...]]] [-y Y1 Y2 Y3] \ +[-z [Z1]] + ''' + help = usage + '''\ + + optional arguments: + -h, --help show this help message and exit + -w W1 [W2 ...] w + -x [X1 [X2 ...]] x + -y Y1 Y2 Y3 y + -z [Z1] z + ''' + version = '' + + +class TestHelpRawText(HelpTestCase): + """Test the RawTextHelpFormatter""" + + parser_signature = Sig( + prog='PROG', formatter_class=argparse.RawTextHelpFormatter, + description='Keep the formatting\n' + ' exactly as it is written\n' + '\n' + 'here\n') + + argument_signatures = [ + Sig('--foo', help=' foo help should also\n' + 'appear as given here'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [ + (Sig('title', description=' This text\n' + ' should be indented\n' + ' exactly like it is here\n'), + [Sig('--bar', help='bar help')]), + ] + usage = '''\ + usage: PROG [-h] [--foo FOO] [--bar BAR] spam + ''' + help = usage + '''\ + + Keep the formatting + exactly as it is written + + here + + positional arguments: + spam spam help + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help should also + appear as given here + + title: + This text + should be indented + exactly like it is here + + --bar BAR bar help + ''' + version = '' + + +class TestHelpRawDescription(HelpTestCase): + """Test the RawTextHelpFormatter""" + + parser_signature = Sig( + prog='PROG', formatter_class=argparse.RawDescriptionHelpFormatter, + description='Keep the formatting\n' + ' exactly as it is written\n' + '\n' + 'here\n') + + argument_signatures = [ + Sig('--foo', help=' foo help should not\n' + ' retain this odd formatting'), + Sig('spam', help='spam help'), + ] + argument_group_signatures = [ + (Sig('title', description=' This text\n' + ' should be indented\n' + ' exactly like it is here\n'), + [Sig('--bar', help='bar help')]), + ] + usage = '''\ + usage: PROG [-h] [--foo FOO] [--bar BAR] spam + ''' + help = usage + '''\ + + Keep the formatting + exactly as it is written + + here + + positional arguments: + spam spam help + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help should not retain this odd formatting + + title: + This text + should be indented + exactly like it is here + + --bar BAR bar help + ''' + version = '' + + +class TestHelpArgumentDefaults(HelpTestCase): + """Test the ArgumentDefaultsHelpFormatter""" + + parser_signature = Sig( + prog='PROG', formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description='description') + + argument_signatures = [ + Sig('--foo', help='foo help - oh and by the way, %(default)s'), + Sig('--bar', action='store_true', help='bar help'), + Sig('spam', help='spam help'), + Sig('badger', nargs='?', default='wooden', help='badger help'), + ] + argument_group_signatures = [ + (Sig('title', description='description'), + [Sig('--baz', type=int, default=42, help='baz help')]), + ] + usage = '''\ + usage: PROG [-h] [--foo FOO] [--bar] [--baz BAZ] spam [badger] + ''' + help = usage + '''\ + + description + + positional arguments: + spam spam help + badger badger help (default: wooden) + + optional arguments: + -h, --help show this help message and exit + --foo FOO foo help - oh and by the way, None + --bar bar help (default: False) + + title: + description + + --baz BAZ baz help (default: 42) + ''' + version = '' + +class TestHelpVersionAction(HelpTestCase): + """Test the default help for the version action""" + + parser_signature = Sig(prog='PROG', description='description') + argument_signatures = [Sig('-V', '--version', action='version', version='3.6')] + argument_group_signatures = [] + usage = '''\ + usage: PROG [-h] [-V] + ''' + help = usage + '''\ + + description + + optional arguments: + -h, --help show this help message and exit + -V, --version show program's version number and exit + ''' + version = '' + +class TestHelpSubparsersOrdering(HelpTestCase): + """Test ordering of subcommands in help matches the code""" + parser_signature = Sig(prog='PROG', + description='display some subcommands', + version='0.1') + + subparsers_signatures = [Sig(name=name) + for name in ('a', 'b', 'c', 'd', 'e')] + + usage = '''\ + usage: PROG [-h] [-v] {a,b,c,d,e} ... + ''' + + help = usage + '''\ + + display some subcommands + + positional arguments: + {a,b,c,d,e} + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + ''' + + version = '''\ + 0.1 + ''' + +class TestHelpSubparsersWithHelpOrdering(HelpTestCase): + """Test ordering of subcommands in help matches the code""" + parser_signature = Sig(prog='PROG', + description='display some subcommands', + version='0.1') + + subcommand_data = (('a', 'a subcommand help'), + ('b', 'b subcommand help'), + ('c', 'c subcommand help'), + ('d', 'd subcommand help'), + ('e', 'e subcommand help'), + ) + + subparsers_signatures = [Sig(name=name, help=help) + for name, help in subcommand_data] + + usage = '''\ + usage: PROG [-h] [-v] {a,b,c,d,e} ... + ''' + + help = usage + '''\ + + display some subcommands + + positional arguments: + {a,b,c,d,e} + a a subcommand help + b b subcommand help + c c subcommand help + d d subcommand help + e e subcommand help + + optional arguments: + -h, --help show this help message and exit + -v, --version show program's version number and exit + ''' + + version = '''\ + 0.1 + ''' + + +# ===================================== +# Optional/Positional constructor tests +# ===================================== + +class TestInvalidArgumentConstructors(TestCase): + """Test a bunch of invalid Argument constructors""" + + def assertTypeError(self, *args, **kwargs): + parser = argparse.ArgumentParser() + self.assertRaises(TypeError, parser.add_argument, + *args, **kwargs) + + def assertValueError(self, *args, **kwargs): + parser = argparse.ArgumentParser() + self.assertRaises(ValueError, parser.add_argument, + *args, **kwargs) + + def test_invalid_keyword_arguments(self): + self.assertTypeError('-x', bar=None) + self.assertTypeError('-y', callback='foo') + self.assertTypeError('-y', callback_args=()) + self.assertTypeError('-y', callback_kwargs={}) + + def test_missing_destination(self): + self.assertTypeError() + for action in ['append', 'store']: + self.assertTypeError(action=action) + + def test_invalid_option_strings(self): + self.assertValueError('--') + self.assertValueError('---') + + def test_invalid_type(self): + self.assertValueError('--foo', type='int') + self.assertValueError('--foo', type=(int, float)) + + def test_invalid_action(self): + self.assertValueError('-x', action='foo') + self.assertValueError('foo', action='baz') + self.assertValueError('--foo', action=('store', 'append')) + parser = argparse.ArgumentParser() + try: + parser.add_argument("--foo", action="store-true") + except ValueError: + e = sys.exc_info()[1] + expected = 'unknown action' + msg = 'expected %r, found %r' % (expected, e) + self.assertTrue(expected in str(e), msg) + + def test_multiple_dest(self): + parser = argparse.ArgumentParser() + parser.add_argument(dest='foo') + try: + parser.add_argument('bar', dest='baz') + except ValueError: + e = sys.exc_info()[1] + expected = 'dest supplied twice for positional argument' + msg = 'expected %r, found %r' % (expected, e) + self.assertTrue(expected in str(e), msg) + + def test_no_argument_actions(self): + for action in ['store_const', 'store_true', 'store_false', + 'append_const', 'count']: + for attrs in [dict(type=int), dict(nargs='+'), + dict(choices='ab')]: + self.assertTypeError('-x', action=action, **attrs) + + def test_no_argument_no_const_actions(self): + # options with zero arguments + for action in ['store_true', 'store_false', 'count']: + + # const is always disallowed + self.assertTypeError('-x', const='foo', action=action) + + # nargs is always disallowed + self.assertTypeError('-x', nargs='*', action=action) + + def test_more_than_one_argument_actions(self): + for action in ['store', 'append']: + + # nargs=0 is disallowed + self.assertValueError('-x', nargs=0, action=action) + self.assertValueError('spam', nargs=0, action=action) + + # const is disallowed with non-optional arguments + for nargs in [1, '*', '+']: + self.assertValueError('-x', const='foo', + nargs=nargs, action=action) + self.assertValueError('spam', const='foo', + nargs=nargs, action=action) + + def test_required_const_actions(self): + for action in ['store_const', 'append_const']: + + # nargs is always disallowed + self.assertTypeError('-x', nargs='+', action=action) + + def test_parsers_action_missing_params(self): + self.assertTypeError('command', action='parsers') + self.assertTypeError('command', action='parsers', prog='PROG') + self.assertTypeError('command', action='parsers', + parser_class=argparse.ArgumentParser) + + def test_required_positional(self): + self.assertTypeError('foo', required=True) + + def test_user_defined_action(self): + + class Success(Exception): + pass + + class Action(object): + + def __init__(self, + option_strings, + dest, + const, + default, + required=False): + if dest == 'spam': + if const is Success: + if default is Success: + raise Success() + + def __call__(self, *args, **kwargs): + pass + + parser = argparse.ArgumentParser() + self.assertRaises(Success, parser.add_argument, '--spam', + action=Action, default=Success, const=Success) + self.assertRaises(Success, parser.add_argument, 'spam', + action=Action, default=Success, const=Success) + +# ================================ +# Actions returned by add_argument +# ================================ + +class TestActionsReturned(TestCase): + + def test_dest(self): + parser = argparse.ArgumentParser() + action = parser.add_argument('--foo') + self.assertEqual(action.dest, 'foo') + action = parser.add_argument('-b', '--bar') + self.assertEqual(action.dest, 'bar') + action = parser.add_argument('-x', '-y') + self.assertEqual(action.dest, 'x') + + def test_misc(self): + parser = argparse.ArgumentParser() + action = parser.add_argument('--foo', nargs='?', const=42, + default=84, type=int, choices=[1, 2], + help='FOO', metavar='BAR', dest='baz') + self.assertEqual(action.nargs, '?') + self.assertEqual(action.const, 42) + self.assertEqual(action.default, 84) + self.assertEqual(action.type, int) + self.assertEqual(action.choices, [1, 2]) + self.assertEqual(action.help, 'FOO') + self.assertEqual(action.metavar, 'BAR') + self.assertEqual(action.dest, 'baz') + + +# ================================ +# Argument conflict handling tests +# ================================ + +class TestConflictHandling(TestCase): + + def test_bad_type(self): + self.assertRaises(ValueError, argparse.ArgumentParser, + conflict_handler='foo') + + def test_conflict_error(self): + parser = argparse.ArgumentParser() + parser.add_argument('-x') + self.assertRaises(argparse.ArgumentError, + parser.add_argument, '-x') + parser.add_argument('--spam') + self.assertRaises(argparse.ArgumentError, + parser.add_argument, '--spam') + + def test_resolve_error(self): + get_parser = argparse.ArgumentParser + parser = get_parser(prog='PROG', conflict_handler='resolve') + + parser.add_argument('-x', help='OLD X') + parser.add_argument('-x', help='NEW X') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [-x X] + + optional arguments: + -h, --help show this help message and exit + -x X NEW X + ''')) + + parser.add_argument('--spam', metavar='OLD_SPAM') + parser.add_argument('--spam', metavar='NEW_SPAM') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [-x X] [--spam NEW_SPAM] + + optional arguments: + -h, --help show this help message and exit + -x X NEW X + --spam NEW_SPAM + ''')) + + +# ============================= +# Help and Version option tests +# ============================= + +class TestOptionalsHelpVersionActions(TestCase): + """Test the help and version actions""" + + def _get_error(self, func, *args, **kwargs): + try: + func(*args, **kwargs) + except ArgumentParserError: + return sys.exc_info()[1] + else: + self.assertRaises(ArgumentParserError, func, *args, **kwargs) + + def assertPrintHelpExit(self, parser, args_str): + self.assertEqual( + parser.format_help(), + self._get_error(parser.parse_args, args_str.split()).stdout) + + def assertPrintVersionExit(self, parser, args_str): + self.assertEqual( + parser.format_version(), + self._get_error(parser.parse_args, args_str.split()).stderr) + + def assertArgumentParserError(self, parser, *args): + self.assertRaises(ArgumentParserError, parser.parse_args, args) + + def test_version(self): + parser = ErrorRaisingArgumentParser(version='1.0') + self.assertPrintHelpExit(parser, '-h') + self.assertPrintHelpExit(parser, '--help') + self.assertPrintVersionExit(parser, '-v') + self.assertPrintVersionExit(parser, '--version') + + def test_version_format(self): + parser = ErrorRaisingArgumentParser(prog='PPP', version='%(prog)s 3.5') + msg = self._get_error(parser.parse_args, ['-v']).stderr + self.assertEqual('PPP 3.5\n', msg) + + def test_version_no_help(self): + parser = ErrorRaisingArgumentParser(add_help=False, version='1.0') + self.assertArgumentParserError(parser, '-h') + self.assertArgumentParserError(parser, '--help') + self.assertPrintVersionExit(parser, '-v') + self.assertPrintVersionExit(parser, '--version') + + def test_version_action(self): + parser = ErrorRaisingArgumentParser(prog='XXX') + parser.add_argument('-V', action='version', version='%(prog)s 3.7') + msg = self._get_error(parser.parse_args, ['-V']).stderr + self.assertEqual('XXX 3.7\n', msg) + + def test_no_help(self): + parser = ErrorRaisingArgumentParser(add_help=False) + self.assertArgumentParserError(parser, '-h') + self.assertArgumentParserError(parser, '--help') + self.assertArgumentParserError(parser, '-v') + self.assertArgumentParserError(parser, '--version') + + def test_alternate_help_version(self): + parser = ErrorRaisingArgumentParser() + parser.add_argument('-x', action='help') + parser.add_argument('-y', action='version') + self.assertPrintHelpExit(parser, '-x') + self.assertPrintVersionExit(parser, '-y') + self.assertArgumentParserError(parser, '-v') + self.assertArgumentParserError(parser, '--version') + + def test_help_version_extra_arguments(self): + parser = ErrorRaisingArgumentParser(version='1.0') + parser.add_argument('-x', action='store_true') + parser.add_argument('y') + + # try all combinations of valid prefixes and suffixes + valid_prefixes = ['', '-x', 'foo', '-x bar', 'baz -x'] + valid_suffixes = valid_prefixes + ['--bad-option', 'foo bar baz'] + for prefix in valid_prefixes: + for suffix in valid_suffixes: + format = '%s %%s %s' % (prefix, suffix) + self.assertPrintHelpExit(parser, format % '-h') + self.assertPrintHelpExit(parser, format % '--help') + self.assertPrintVersionExit(parser, format % '-v') + self.assertPrintVersionExit(parser, format % '--version') + + +# ====================== +# str() and repr() tests +# ====================== + +class TestStrings(TestCase): + """Test str() and repr() on Optionals and Positionals""" + + def assertStringEqual(self, obj, result_string): + for func in [str, repr]: + self.assertEqual(func(obj), result_string) + + def test_optional(self): + option = argparse.Action( + option_strings=['--foo', '-a', '-b'], + dest='b', + type='int', + nargs='+', + default=42, + choices=[1, 2, 3], + help='HELP', + metavar='METAVAR') + string = ( + "Action(option_strings=['--foo', '-a', '-b'], dest='b', " + "nargs='+', const=None, default=42, type='int', " + "choices=[1, 2, 3], help='HELP', metavar='METAVAR')") + self.assertStringEqual(option, string) + + def test_argument(self): + argument = argparse.Action( + option_strings=[], + dest='x', + type=float, + nargs='?', + default=2.5, + choices=[0.5, 1.5, 2.5], + help='H HH H', + metavar='MV MV MV') + string = ( + "Action(option_strings=[], dest='x', nargs='?', " + "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], " + "help='H HH H', metavar='MV MV MV')" % float) + self.assertStringEqual(argument, string) + + def test_namespace(self): + ns = argparse.Namespace(foo=42, bar='spam') + string = "Namespace(bar='spam', foo=42)" + self.assertStringEqual(ns, string) + + def test_parser(self): + parser = argparse.ArgumentParser(prog='PROG') + string = ( + "ArgumentParser(prog='PROG', usage=None, description=None, " + "version=None, formatter_class=%r, conflict_handler='error', " + "add_help=True)" % argparse.HelpFormatter) + self.assertStringEqual(parser, string) + +# =============== +# Namespace tests +# =============== + +class TestNamespace(TestCase): + + def test_constructor(self): + ns = argparse.Namespace() + self.assertRaises(AttributeError, getattr, ns, 'x') + + ns = argparse.Namespace(a=42, b='spam') + self.assertEqual(ns.a, 42) + self.assertEqual(ns.b, 'spam') + + def test_equality(self): + ns1 = argparse.Namespace(a=1, b=2) + ns2 = argparse.Namespace(b=2, a=1) + ns3 = argparse.Namespace(a=1) + ns4 = argparse.Namespace(b=2) + + self.assertEqual(ns1, ns2) + self.assertNotEqual(ns1, ns3) + self.assertNotEqual(ns1, ns4) + self.assertNotEqual(ns2, ns3) + self.assertNotEqual(ns2, ns4) + self.assertTrue(ns1 != ns3) + self.assertTrue(ns1 != ns4) + self.assertTrue(ns2 != ns3) + self.assertTrue(ns2 != ns4) + + +# =================== +# File encoding tests +# =================== + +class TestEncoding(TestCase): + + def _test_module_encoding(self, path): + path, _ = os.path.splitext(path) + path += ".py" + with codecs.open(path, 'r', 'utf8') as f: + f.read() + + def test_argparse_module_encoding(self): + self._test_module_encoding(argparse.__file__) + + def test_test_argparse_module_encoding(self): + self._test_module_encoding(__file__) + +# =================== +# ArgumentError tests +# =================== + +class TestArgumentError(TestCase): + + def test_argument_error(self): + msg = "my error here" + error = argparse.ArgumentError(None, msg) + self.assertEqual(str(error), msg) + +# ======================= +# ArgumentTypeError tests +# ======================= + +class TestArgumentTypeError(TestCase): + + def test_argument_type_error(self): + + def spam(string): + raise argparse.ArgumentTypeError('spam!') + + parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) + parser.add_argument('x', type=spam) + try: + parser.parse_args(['XXX']) + except ArgumentParserError: + expected = 'usage: PROG x\nPROG: error: argument x: spam!\n' + msg = sys.exc_info()[1].stderr + self.assertEqual(expected, msg) + else: + self.fail() + +# ====================== +# parse_known_args tests +# ====================== + +class TestParseKnownArgs(TestCase): + + def test_optionals(self): + parser = argparse.ArgumentParser() + parser.add_argument('--foo') + args, extras = parser.parse_known_args('--foo F --bar --baz'.split()) + self.assertEqual(NS(foo='F'), args) + self.assertEqual(['--bar', '--baz'], extras) + + def test_mixed(self): + parser = argparse.ArgumentParser() + parser.add_argument('-v', nargs='?', const=1, type=int) + parser.add_argument('--spam', action='store_false') + parser.add_argument('badger') + + argv = ["B", "C", "--foo", "-v", "3", "4"] + args, extras = parser.parse_known_args(argv) + self.assertEqual(NS(v=3, spam=True, badger="B"), args) + self.assertEqual(["C", "--foo", "4"], extras) + +# ========================== +# add_argument metavar tests +# ========================== + +class TestAddArgumentMetavar(TestCase): + + EXPECTED_MESSAGE = "length of metavar tuple does not match nargs" + + def do_test_no_exception(self, nargs, metavar): + parser = argparse.ArgumentParser() + parser.add_argument("--foo", nargs=nargs, metavar=metavar) + + def do_test_exception(self, nargs, metavar): + parser = argparse.ArgumentParser() + with self.assertRaises(ValueError) as cm: + parser.add_argument("--foo", nargs=nargs, metavar=metavar) + self.assertEqual(cm.exception.args[0], self.EXPECTED_MESSAGE) + + # Unit tests for different values of metavar when nargs=None + + def test_nargs_None_metavar_string(self): + self.do_test_no_exception(nargs=None, metavar="1") + + def test_nargs_None_metavar_length0(self): + self.do_test_exception(nargs=None, metavar=tuple()) + + def test_nargs_None_metavar_length1(self): + self.do_test_no_exception(nargs=None, metavar=("1")) + + def test_nargs_None_metavar_length2(self): + self.do_test_exception(nargs=None, metavar=("1", "2")) + + def test_nargs_None_metavar_length3(self): + self.do_test_exception(nargs=None, metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=? + + def test_nargs_optional_metavar_string(self): + self.do_test_no_exception(nargs="?", metavar="1") + + def test_nargs_optional_metavar_length0(self): + self.do_test_exception(nargs="?", metavar=tuple()) + + def test_nargs_optional_metavar_length1(self): + self.do_test_no_exception(nargs="?", metavar=("1")) + + def test_nargs_optional_metavar_length2(self): + self.do_test_exception(nargs="?", metavar=("1", "2")) + + def test_nargs_optional_metavar_length3(self): + self.do_test_exception(nargs="?", metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=* + + def test_nargs_zeroormore_metavar_string(self): + self.do_test_no_exception(nargs="*", metavar="1") + + def test_nargs_zeroormore_metavar_length0(self): + self.do_test_exception(nargs="*", metavar=tuple()) + + def test_nargs_zeroormore_metavar_length1(self): + self.do_test_no_exception(nargs="*", metavar=("1")) + + def test_nargs_zeroormore_metavar_length2(self): + self.do_test_no_exception(nargs="*", metavar=("1", "2")) + + def test_nargs_zeroormore_metavar_length3(self): + self.do_test_exception(nargs="*", metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=+ + + def test_nargs_oneormore_metavar_string(self): + self.do_test_no_exception(nargs="+", metavar="1") + + def test_nargs_oneormore_metavar_length0(self): + self.do_test_exception(nargs="+", metavar=tuple()) + + def test_nargs_oneormore_metavar_length1(self): + self.do_test_no_exception(nargs="+", metavar=("1")) + + def test_nargs_oneormore_metavar_length2(self): + self.do_test_no_exception(nargs="+", metavar=("1", "2")) + + def test_nargs_oneormore_metavar_length3(self): + self.do_test_exception(nargs="+", metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=... + + def test_nargs_remainder_metavar_string(self): + self.do_test_no_exception(nargs="...", metavar="1") + + def test_nargs_remainder_metavar_length0(self): + self.do_test_no_exception(nargs="...", metavar=tuple()) + + def test_nargs_remainder_metavar_length1(self): + self.do_test_no_exception(nargs="...", metavar=("1")) + + def test_nargs_remainder_metavar_length2(self): + self.do_test_no_exception(nargs="...", metavar=("1", "2")) + + def test_nargs_remainder_metavar_length3(self): + self.do_test_no_exception(nargs="...", metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=A... + + def test_nargs_parser_metavar_string(self): + self.do_test_no_exception(nargs="A...", metavar="1") + + def test_nargs_parser_metavar_length0(self): + self.do_test_exception(nargs="A...", metavar=tuple()) + + def test_nargs_parser_metavar_length1(self): + self.do_test_no_exception(nargs="A...", metavar=("1")) + + def test_nargs_parser_metavar_length2(self): + self.do_test_exception(nargs="A...", metavar=("1", "2")) + + def test_nargs_parser_metavar_length3(self): + self.do_test_exception(nargs="A...", metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=1 + + def test_nargs_1_metavar_string(self): + self.do_test_no_exception(nargs=1, metavar="1") + + def test_nargs_1_metavar_length0(self): + self.do_test_exception(nargs=1, metavar=tuple()) + + def test_nargs_1_metavar_length1(self): + self.do_test_no_exception(nargs=1, metavar=("1")) + + def test_nargs_1_metavar_length2(self): + self.do_test_exception(nargs=1, metavar=("1", "2")) + + def test_nargs_1_metavar_length3(self): + self.do_test_exception(nargs=1, metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=2 + + def test_nargs_2_metavar_string(self): + self.do_test_no_exception(nargs=2, metavar="1") + + def test_nargs_2_metavar_length0(self): + self.do_test_exception(nargs=2, metavar=tuple()) + + def test_nargs_2_metavar_length1(self): + self.do_test_no_exception(nargs=2, metavar=("1")) + + def test_nargs_2_metavar_length2(self): + self.do_test_no_exception(nargs=2, metavar=("1", "2")) + + def test_nargs_2_metavar_length3(self): + self.do_test_exception(nargs=2, metavar=("1", "2", "3")) + + # Unit tests for different values of metavar when nargs=3 + + def test_nargs_3_metavar_string(self): + self.do_test_no_exception(nargs=3, metavar="1") + + def test_nargs_3_metavar_length0(self): + self.do_test_exception(nargs=3, metavar=tuple()) + + def test_nargs_3_metavar_length1(self): + self.do_test_no_exception(nargs=3, metavar=("1")) + + def test_nargs_3_metavar_length2(self): + self.do_test_exception(nargs=3, metavar=("1", "2")) + + def test_nargs_3_metavar_length3(self): + self.do_test_no_exception(nargs=3, metavar=("1", "2", "3")) + +# ============================ +# from argparse import * tests +# ============================ + +class TestImportStar(TestCase): + + def test(self): + for name in argparse.__all__: + self.assertTrue(hasattr(argparse, name)) + + def test_all_exports_everything_but_modules(self): + items = [ + name + for name, value in vars(argparse).items() + if not name.startswith("_") + if not inspect.ismodule(value) + ] + self.assertEqual(sorted(items), sorted(argparse.__all__)) + +def test_main(): + # silence warnings about version argument - these are expected + with test_support.check_warnings( + ('The "version" argument to ArgumentParser is deprecated.', + DeprecationWarning), + ('The (format|print)_version method is deprecated', + DeprecationWarning)): + test_support.run_unittest(__name__) + # Remove global references to avoid looking like we have refleaks. + RFile.seen = {} + WFile.seen = set() + + + +if __name__ == '__main__': + test_main() -- cgit v1.2.3