3

I defined a custom regex type for an argument that needs to follow an exact format. I used code from another post (regex custom type) that has been super useful. My problem is that I'm writing unit tests where I expect the regex to fail and trying to assert that the argparse.ArgumentError is raised ( assertRaises(argparse.ArgumentError, parser.parse_args(inargs.split())) ). The problem is that argparse appears to be catching the ArgumentError and throwing a generic error, preventing me from verifying the cause of the failure. Am I missing something?

Here is the traceback:

Error
Traceback (most recent call last):
  File "/Users/markebbert/PyCharmProjects/newproject/unittests.py", line 203, in test_set_operation_parameter
    self.assertRaises(argparse.ArgumentError, parser.parse_args(inargs.split()))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1688, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 1727, in parse_known_args
self.error(str(err))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 2347, in error
    self.exit(2, _('%s: error: %s\n') % (self.prog, message))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py", line 2335, in exit
    _sys.exit(status)
SystemExit: 2

Here is my defined custom type and the parser code:

class RegexValidator(object):
    """
    Performs regular expression match on value.
    If match fails an ArgumentError is raised
    """

    def __init__(self, pattern, statement=None):
        self.pattern = re.compile(pattern)
        self.statement = statement
        if not self.statement:
            self.statement = "must match pattern %s" % self.pattern

    def __call__(self, string):
        match = self.pattern.search(string)
        if not match:
            raise argparse.ArgumentError(None, self.statement)
        return string


operatorRV = RegexValidator(
    "^((\w+)=)?[iIuUcC]\[(\w+(\[\w+(,\w+)*\])?)(:\w+(\[\w+(,\w+)*\])?)*\]$",
    "Set operations must conform to...")

parser = argparse.ArgumentParser(
    description='Compare variants across individuals',
    formatter_class=argparse.ArgumentDefaultsHelpFormatter)

group.add_argument('-s', '--set-operation', dest='operation', nargs='+',
                   type=operatorRV,
                   help="blah.")

Here is the unit test:

    # Fail for ending colon
    inargs = "-s out=i[one[id1]:]"
    self.assertRaises(argparse.ArgumentError, parser.parse_args(inargs.split()))
3
  • Argparse raises a SystemExit, because that is what a command line tool is supposed to do; return a system exit code on error. Commented Mar 28, 2013 at 20:50
  • Can you elaborate? I would not consider argparse a command line tool since it is a python module. As with any module, I would expect to be able to "catch" argparse's errors and handle them myself. Commented Mar 28, 2013 at 21:13
  • It is a library to build command line tools. I've added an answer. Commented Mar 28, 2013 at 21:15

1 Answer 1

3

Argparse is a command line parser, and errors always end up calling argparse.exit(), which in turn calls sys.exit() with an error code. This is by design.

For a unit test, you'd have to monkeypatch (using mocking perhaps) the .error() or the .exit() methods of the parser. .error() is called with an error message and prints the usage message, then calls .exit() with an exit code and the error message.

Their current implementations:

# ===============
# Exiting methods
# ===============
def exit(self, status=0, message=None):
    if message:
        self._print_message(message, _sys.stderr)
    _sys.exit(status)

def error(self, message):
    """error(message: string)

    Prints a usage message incorporating the message to stderr and
    exits.

    If you override this in a subclass, it should not return -- it
    should either exit or raise an exception.
    """
    self.print_usage(_sys.stderr)
    self.exit(2, _('%s: error: %s\n') % (self.prog, message))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.