From 74898db996d892c072e9dab1829a639beb276b9b Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Thu, 12 Oct 2023 22:07:29 -0700 Subject: [PATCH] Add DUCKARGS_COMMENT and DUCKARGS_PRINT environment variables --- README.rst | 29 ++++++++++++++-- duckargs/__init__.py | 34 ++++++++++++++----- tests/test_data/choices/expected_python.txt | 1 + tests/test_data/env_all/args.txt | 1 + tests/test_data/env_all/expected_python.txt | 20 +++++++++++ tests/test_data/env_comment/args.txt | 1 + .../test_data/env_comment/expected_python.txt | 30 ++++++++++++++++ tests/test_data/env_print/args.txt | 1 + tests/test_data/env_print/expected_python.txt | 23 +++++++++++++ .../test_data/flags_only/expected_python.txt | 1 + tests/test_data/hex/expected_python.txt | 1 + tests/test_data/many_opts/expected_python.txt | 1 + .../negative_hex/expected_python.txt | 1 + tests/test_data/negative_int/args.txt | 1 + .../negative_int/expected_python.txt | 22 ++++++++++++ .../normalize_names/expected_python.txt | 1 + .../options_only/expected_python.txt | 1 + .../positional_only/expected_python.txt | 1 + .../positional_values/expected_python.txt | 1 + .../readme_example/expected_python.txt | 1 + tests/test_duckargs.py | 18 ++++++++++ 21 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 tests/test_data/env_all/args.txt create mode 100644 tests/test_data/env_all/expected_python.txt create mode 100644 tests/test_data/env_comment/args.txt create mode 100644 tests/test_data/env_comment/expected_python.txt create mode 100644 tests/test_data/env_print/args.txt create mode 100644 tests/test_data/env_print/expected_python.txt create mode 100644 tests/test_data/negative_int/args.txt create mode 100644 tests/test_data/negative_int/expected_python.txt diff --git a/README.rst b/README.rst index b65f9c5..215c456 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,9 @@ +.. contents:: **Table of Contents** + .. |duck| unicode:: 0x1F986 duckargs |duck| -=============== +--------------- .. |tests_badge| image:: https://github.com/eriknyquist/duckargs/actions/workflows/tests.yml/badge.svg .. |cov_badge| image:: https://github.com/eriknyquist/duckargs/actions/workflows/coverage.yml/badge.svg @@ -42,12 +44,13 @@ The output of the above command looks like this: .. code:: python + # Generated by duckargs, invoked with the following arguments: # positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c import argparse def main(): - parser = argparse.ArgumentParser(description='', + parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('positional_arg1', help='a string') @@ -102,6 +105,26 @@ Either of which will generate a line like this: parser.add_argument('-f', '--filename', default='file', type=argparse.FileType(), help='a filename') +Environment variables +===================== + +Some things can be configured by setting environment variables. + +``DUCKARGS_PRINT`` +################## + +By default, ``duckargs`` generates a program that prints all provided arguments/options, +so that you can see all the corresponding attribute names on the object returned by ``argparse``. +If you want to disable this and generate programs with the print statements, set +``DUCKARGS_PRINT=0`` in your environment variables. + +``DUCKARGS_COMMENT`` +#################### + +By default, ``duckargs`` generates a program that prints a comment header at the top, +showing the arguments that ``duckargs`` was invoked with. If you want to disable this and +generate programs without the comment header, set ``DUCKARGS_COMMENT=0`` in your environment +variables. Use duckargs in python code =========================== @@ -127,7 +150,7 @@ an option ``-q --quiet`` with a required argument. To avoid this, it is recommended to declare your positional arguments first (as in: ``python -m duckargs positional_arg -q --quiet``) Contributions -------------- +============= Contributions are welcome, please open a pull request at ``_. You will need to install packages required for development by doing ``pip install -r dev_requirements.txt``. diff --git a/duckargs/__init__.py b/duckargs/__init__.py index 259b2ae..e7871d8 100644 --- a/duckargs/__init__.py +++ b/duckargs/__init__.py @@ -4,18 +4,14 @@ import os import re -PYTHON_TEMPLATE = """# {0} - -import argparse +PYTHON_TEMPLATE = """{0}import argparse def main(): parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', formatter_class=argparse.ArgumentDefaultsHelpFormatter) {1} - args = parser.parse_args() - -{2} + args = parser.parse_args(){2} if __name__ == "__main__": main() @@ -290,6 +286,28 @@ def generate_python_code(argv=sys.argv): """ processed_args = process_args(argv) optlines = " " + "\n ".join([o.generate_code() for o in processed_args]) - printlines = " " + "\n ".join([f"print(args.{o.var_name})" for o in processed_args]) + + printlines = "" + env_print = os.environ.get('DUCKARGS_PRINT', 1) + try: + env_print_int = int(env_print) + except ValueError: + raise RuntimeError("DUCKARGS_PRINT must be an integer") + + if env_print_int > 0: + printlines += "\n\n " + "\n ".join([f"print(args.{o.var_name})" for o in processed_args]) + + comment = "" + env_comment = os.environ.get("DUCKARGS_COMMENT", 1) + try: + env_comment_int = int(env_comment) + except ValueError: + raise RuntimeError("DUCKARGS_COMMENT must be an integer") + + if env_comment_int > 0: + comment = (f"# Generated by duckargs, invoked with the following arguments:\n# " + + ' '.join(argv[1:]) + "\n\n") + CmdlineOpt.positional_count = 0 - return PYTHON_TEMPLATE.format(' '.join(argv[1:]), optlines, printlines) + + return PYTHON_TEMPLATE.format(comment, optlines, printlines) diff --git a/tests/test_data/choices/expected_python.txt b/tests/test_data/choices/expected_python.txt index 0e0de3a..f589116 100644 --- a/tests/test_data/choices/expected_python.txt +++ b/tests/test_data/choices/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # pos1 pos2 -f -g -q --qefqaf op,ep,orp import argparse diff --git a/tests/test_data/env_all/args.txt b/tests/test_data/env_all/args.txt new file mode 100644 index 0000000..11196d0 --- /dev/null +++ b/tests/test_data/env_all/args.txt @@ -0,0 +1 @@ +duckargs positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c diff --git a/tests/test_data/env_all/expected_python.txt b/tests/test_data/env_all/expected_python.txt new file mode 100644 index 0000000..891bd56 --- /dev/null +++ b/tests/test_data/env_all/expected_python.txt @@ -0,0 +1,20 @@ +import argparse + +def main(): + parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional_arg1', help='a string') + parser.add_argument('positional_arg2', help='a string') + parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') + parser.add_argument('-f', default=3.3, type=float, help='a float value') + parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-a', action='store_true', help='a flag') + parser.add_argument('-b', action='store_true', help='b flag') + parser.add_argument('-c', action='store_true', help='c flag') + args = parser.parse_args() + +if __name__ == "__main__": + main() + diff --git a/tests/test_data/env_comment/args.txt b/tests/test_data/env_comment/args.txt new file mode 100644 index 0000000..11196d0 --- /dev/null +++ b/tests/test_data/env_comment/args.txt @@ -0,0 +1 @@ +duckargs positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c diff --git a/tests/test_data/env_comment/expected_python.txt b/tests/test_data/env_comment/expected_python.txt new file mode 100644 index 0000000..fd53f2b --- /dev/null +++ b/tests/test_data/env_comment/expected_python.txt @@ -0,0 +1,30 @@ +import argparse + +def main(): + parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional_arg1', help='a string') + parser.add_argument('positional_arg2', help='a string') + parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') + parser.add_argument('-f', default=3.3, type=float, help='a float value') + parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-a', action='store_true', help='a flag') + parser.add_argument('-b', action='store_true', help='b flag') + parser.add_argument('-c', action='store_true', help='c flag') + args = parser.parse_args() + + print(args.positional_arg1) + print(args.positional_arg2) + print(args.int_val) + print(args.f) + print(args.file) + print(args.otherfile) + print(args.a) + print(args.b) + print(args.c) + +if __name__ == "__main__": + main() + diff --git a/tests/test_data/env_print/args.txt b/tests/test_data/env_print/args.txt new file mode 100644 index 0000000..11196d0 --- /dev/null +++ b/tests/test_data/env_print/args.txt @@ -0,0 +1 @@ +duckargs positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c diff --git a/tests/test_data/env_print/expected_python.txt b/tests/test_data/env_print/expected_python.txt new file mode 100644 index 0000000..94be95b --- /dev/null +++ b/tests/test_data/env_print/expected_python.txt @@ -0,0 +1,23 @@ +# Generated by duckargs, invoked with the following arguments: +# positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c + +import argparse + +def main(): + parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional_arg1', help='a string') + parser.add_argument('positional_arg2', help='a string') + parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') + parser.add_argument('-f', default=3.3, type=float, help='a float value') + parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') + parser.add_argument('-a', action='store_true', help='a flag') + parser.add_argument('-b', action='store_true', help='b flag') + parser.add_argument('-c', action='store_true', help='c flag') + args = parser.parse_args() + +if __name__ == "__main__": + main() + diff --git a/tests/test_data/flags_only/expected_python.txt b/tests/test_data/flags_only/expected_python.txt index 95efb35..f029930 100644 --- a/tests/test_data/flags_only/expected_python.txt +++ b/tests/test_data/flags_only/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # -a -b -c -d -e -f -g -h import argparse diff --git a/tests/test_data/hex/expected_python.txt b/tests/test_data/hex/expected_python.txt index 8d3ec76..0c12ef8 100644 --- a/tests/test_data/hex/expected_python.txt +++ b/tests/test_data/hex/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # -f --fff 0xabc -q 0x --test ox2 import argparse diff --git a/tests/test_data/many_opts/expected_python.txt b/tests/test_data/many_opts/expected_python.txt index 0f71ba0..1a75795 100644 --- a/tests/test_data/many_opts/expected_python.txt +++ b/tests/test_data/many_opts/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # pos1 pos2 pos3 pos4 pos5 pos6 pos7 pos8 pos9 pos10 -a --aye 5 -w --yyy -k 0.0 -l jkjkj -o --out -d --ede -v --vvv jfijfdsifj -s --ssss -z --zzzz -c --ccc jiji -b --bbb 8 -i --ii d -q --qqqqsdgvs -y -r import argparse diff --git a/tests/test_data/negative_hex/expected_python.txt b/tests/test_data/negative_hex/expected_python.txt index 16dbfe2..3ca7130 100644 --- a/tests/test_data/negative_hex/expected_python.txt +++ b/tests/test_data/negative_hex/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # -0x44 -f --fell -0x235 -x -0x8 import argparse diff --git a/tests/test_data/negative_int/args.txt b/tests/test_data/negative_int/args.txt new file mode 100644 index 0000000..d4937bd --- /dev/null +++ b/tests/test_data/negative_int/args.txt @@ -0,0 +1 @@ +duckargs -4 -f --ff -2 --blah -2343 -6 diff --git a/tests/test_data/negative_int/expected_python.txt b/tests/test_data/negative_int/expected_python.txt new file mode 100644 index 0000000..828e379 --- /dev/null +++ b/tests/test_data/negative_int/expected_python.txt @@ -0,0 +1,22 @@ +# Generated by duckargs, invoked with the following arguments: +# -4 -f --ff -2 --blah -2343 -6 + +import argparse + +def main(): + parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional_arg0', type=int, help='an int value') + parser.add_argument('-f', '--ff', default=-2, type=int, help='an int value') + parser.add_argument('--blah', default=-2343, type=int, help='an int value') + parser.add_argument('positional_arg1', type=int, help='an int value') + args = parser.parse_args() + + print(args.positional_arg0) + print(args.ff) + print(args.blah) + print(args.positional_arg1) + +if __name__ == "__main__": + main() diff --git a/tests/test_data/normalize_names/expected_python.txt b/tests/test_data/normalize_names/expected_python.txt index f419255..c9d1494 100644 --- a/tests/test_data/normalize_names/expected_python.txt +++ b/tests/test_data/normalize_names/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # -a --test_1 5464 -b --test__-2 8 -j --j==j -g --lp++l pos-1 pos_2 pos_-_3 import argparse diff --git a/tests/test_data/options_only/expected_python.txt b/tests/test_data/options_only/expected_python.txt index 2563a6b..c3ee11b 100644 --- a/tests/test_data/options_only/expected_python.txt +++ b/tests/test_data/options_only/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # -a 4 -b --bbb 5.5 -t --tttt test -j srgsrh -k wergwshg import argparse diff --git a/tests/test_data/positional_only/expected_python.txt b/tests/test_data/positional_only/expected_python.txt index 3596d4a..0870b92 100644 --- a/tests/test_data/positional_only/expected_python.txt +++ b/tests/test_data/positional_only/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # pos1 pos2 pos3 pos4 pos5 pos6 import argparse diff --git a/tests/test_data/positional_values/expected_python.txt b/tests/test_data/positional_values/expected_python.txt index 8abe8a0..94b2303 100644 --- a/tests/test_data/positional_values/expected_python.txt +++ b/tests/test_data/positional_values/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # 0x 0x123 2.3 hello -r --ra FILE import argparse diff --git a/tests/test_data/readme_example/expected_python.txt b/tests/test_data/readme_example/expected_python.txt index e6a854f..b6aeeb3 100644 --- a/tests/test_data/readme_example/expected_python.txt +++ b/tests/test_data/readme_example/expected_python.txt @@ -1,3 +1,4 @@ +# Generated by duckargs, invoked with the following arguments: # positional_arg1 positional_arg2 -i --int-val 4 -f 3.3 -f --file FILE -F --otherfile FILE -a -b -c import argparse diff --git a/tests/test_duckargs.py b/tests/test_duckargs.py index 4ac89e5..5ea9033 100644 --- a/tests/test_duckargs.py +++ b/tests/test_duckargs.py @@ -8,6 +8,11 @@ TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "test_data") class TestDuckargs(unittest.TestCase): + def setUp(self): + # Set default env. var values + os.environ["DUCKARGS_PRINT"] = "1" + os.environ["DUCKARGS_COMMENT"] = "1" + def _run_test(self, test_dir_name): test_dir_path = os.path.join(TEST_DATA_DIR, test_dir_name) expected_python_path = os.path.join(test_dir_path, "expected_python.txt") @@ -55,6 +60,19 @@ def test_negative_int(self): def test_negative_hex(self): self._run_test("negative_hex") + def test_env_print(self): + os.environ["DUCKARGS_PRINT"] = "0" + self._run_test("env_print") + + def test_env_comment(self): + os.environ["DUCKARGS_COMMENT"] = "0" + self._run_test("env_comment") + + def test_env_all(self): + os.environ["DUCKARGS_COMMENT"] = "0" + os.environ["DUCKARGS_PRINT"] = "0" + self._run_test("env_all") + def test_duplicate_names(self): self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '-a']) self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '-b', '-a'])