I'm trying to write tests for a piece of data analysis software, which takes some command-line arguments. One problem is that some of the cl arguments can start with a minus sign. For example, I have the file spam.py
import sys,argparse
def parse_input(input_args):
# Read in command line inputs
my_parser = argparse.ArgumentParser(description='Test positional and optional CL arguments')
# Required arguments (Positional)
my_parser.add_argument('prefix', help='The prefix to be used on the output files')
my_parser.add_argument('ra', help='The RA of the star in hms format', metavar='ra[hms]')
my_parser.add_argument('dec', help='The Dec of the star in dms format', metavar='dec[dms]')
my_parser.add_argument('mass', help='The mass of the primary in solar masses', type=float, metavar='mass[M_sun]')
# Optional arguments
my_parser.add_argument('--age', help='The age of the star in Gyr', type=float, required=False, default=5,
metavar='AGE[Gyr]')
# Run parser
args = my_parser.parse_args(input_args)
return None
if __name__=="__main__":
print(parse_input(sys.argv[1:]))
If I execute this from the command line in any of the following formats: python spam.py test_spam 13h50m06.28s -- -40d50m08.9s 1.3 (positional arguments only), python spam.py test_spam ra='13h50m06.28s' dec='-40d50m08.9s' 1.3 (positional only again, following this answer), or python spam.py test_spam ra='13h50m06.28s' dec='-40d50m08.9s' 1.3 --age 0.5 (now with an optional argument), then I get the expected output. In the case of my test code, it just prints "None", but in the full code it would proceed with further analysis.
Now, I've also written two test functions in the file test_spam.py
import pytest,sys
from spam import parse_input
def test_position_only(monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, 'argv', [sys.argv[0], "spam_test",
"13h50m06.28s", "--", "-40d50m08.9s", 1.3])
print(sys.argv)
assert parse_input(sys.argv[1:]) is None
def test_with_optional(monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, 'argv', [sys.argv[0], "test_bar",
"13h50m06.28s", "--", "-40d50m08.9s", "1.3",
"--age","0.5"])
print(sys.argv)
print(sys.argv[1:])
assert parse_input(sys.argv[1:]) is None
When I run pytest on this, test_positional_only works just fine, though I have to add that buffer "--" string. Writing "ra","13h50m06.28s" or "ra=13h50m06.28s" or "ra='13h50m06.28s'" all fail with the error "TypeError: 'float' object is not subscriptable".
In contrast, test_with_optional always fails. I get the following error message, indicating that the parser doesn't recognize the additional arguments.
=================================== FAILURES ===================================
______________________________ test_with_optional ______________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x109f96cb0>
def test_with_optional(monkeypatch):
with monkeypatch.context() as m:
m.setattr(sys, 'argv', [sys.argv[0], "test_bar",
"13h50m06.28s", "--", "-40d50m08.9s", "1.3",
"--age","0.5"])
print(sys.argv)
print(sys.argv[1:])
> assert parse_input(sys.argv[1:]) is None
test_spam.py:20:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
spam.py:20: in parse_input
args = my_parser.parse_args(input_args)
../../anaconda3/envs/molusc2/lib/python3.10/argparse.py:1836: in parse_args
self.error(msg % ' '.join(argv))
../../anaconda3/envs/molusc2/lib/python3.10/argparse.py:2594: in error
self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = ArgumentParser(prog='pytest', usage=None, description='Test positional and optional CL arguments', formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
status = 2, message = 'pytest: error: unrecognized arguments: --age 0.5\n'
def exit(self, status=0, message=None):
if message:
self._print_message(message, _sys.stderr)
> _sys.exit(status)
E SystemExit: 2
../../anaconda3/envs/molusc2/lib/python3.10/argparse.py:2581: SystemExit
----------------------------- Captured stdout call -----------------------------
['/Users/douglste/anaconda3/envs/molusc2/bin/pytest', 'test_bar', '13h50m06.28s', '--', '-40d50m08.9s', '1.3', '--age', '0.5']
['test_bar', '13h50m06.28s', '--', '-40d50m08.9s', '1.3', '--age', '0.5']
----------------------------- Captured stderr call -----------------------------
usage: pytest [-h] [--age AGE[Gyr]] prefix ra[hms] dec[dms] mass[M_sun]
pytest: error: unrecognized arguments: --age 0.5
=========================== short test summary info ============================
FAILED test_spam.py::test_with_optional - SystemExit: 2
========================= 1 failed, 1 passed in 0.14s ==========================
Ultimately, I want to be able to test whether some optional arguments are being read in properly. That appears to require correctly setting the ra and dec positional arguments. I'm editing someone else's code, so I would like to maintain backwards compatibility and avoid changing the positional arguments. Also, I am running in Bash. Is there another way I can can set these arguments with monkeypatch given the current parser structure?