├── Makefile ├── Pipfile ├── .github └── workflows │ └── unittest.yaml ├── args.py ├── tests.py ├── UNLICENSE.txt ├── README.md ├── .gitignore └── Pipfile.lock /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | test: 4 | pipenv run python -m unittest discover -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | 3 | url = "https://pypi.python.org/simple" 4 | verify_ssl = true 5 | name = "pypi" 6 | 7 | 8 | [packages] 9 | 10 | black = "*" 11 | 12 | 13 | [dev-packages] 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/unittest.yaml: -------------------------------------------------------------------------------- 1 | name: Run unittests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.7", "3.8", "3.9", "3.10"] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Test with unittest 25 | run: | 26 | python -m unittest discover 27 | -------------------------------------------------------------------------------- /args.py: -------------------------------------------------------------------------------- 1 | """ 2 | UNLICENSED 3 | This is free and unencumbered software released into the public domain. 4 | 5 | https://github.com/sesh/args 6 | """ 7 | 8 | import sys 9 | 10 | 11 | def parse_args(args): 12 | result = { 13 | a.split("=")[0]: int(a.split("=")[1]) 14 | if "=" in a and a.split("=")[1].isnumeric() 15 | else a.split("=")[1] 16 | if "=" in a 17 | else True 18 | for a in args 19 | if "--" in a 20 | } 21 | result["[]"] = [a for a in args if not a.startswith('--')] 22 | return result 23 | 24 | 25 | if __name__ == "__main__": 26 | import json 27 | print(json.dumps(parse_args(sys.argv[1:]), indent=2)) 28 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from args import parse_args 4 | 5 | 6 | # python args.py --verbose --count=2 run 7 | # { 8 | # "--verbose": True, 9 | # "--count": 2, 10 | # "[]": ["run"], 11 | # } 12 | 13 | class ArgsTestCase(TestCase): 14 | 15 | def test_option_with_equals(self): 16 | args = parse_args(['--name=bob']) 17 | self.assertEqual(args['--name'], 'bob') 18 | 19 | args = parse_args(['--first=jane', '--last=austin']) 20 | self.assertEqual(args['--first'], 'jane') 21 | self.assertEqual(args['--last'], 'austin') 22 | 23 | def test_option_with_integer_value(self): 24 | args = parse_args(['--count=2']) 25 | self.assertEqual(args['--count'], 2) 26 | 27 | def test_flag(self): 28 | args = parse_args(['--verbose']) 29 | self.assertTrue(args['--verbose']) 30 | 31 | args = parse_args(['--verbose', '--name=bob']) 32 | self.assertTrue(args['--verbose']) 33 | self.assertEqual(args['--name'], 'bob') 34 | 35 | def test_command(self): 36 | args = parse_args(['bob']) 37 | self.assertEqual(args['[]'], ["bob"]) 38 | -------------------------------------------------------------------------------- /UNLICENSE.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. 4 | 5 | In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | For more information, please refer to https://unlicense.org -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `args` is a zero-dependency, really back to basics, solution to command line arguments in Python. 2 | 3 | ## Usage 4 | 5 | ``` 6 | import sys 7 | from args import parse_args 8 | 9 | args = parse_args(sys.argv[1:]) 10 | ``` 11 | 12 | Arguments can be in one of three formats: 13 | 14 | - `--option`, like `--verbose`, `--debug` 15 | - `--value=3` to pass a value to an option 16 | - `cmd` or any other `value` passed positionally 17 | 18 | The output is a Python dictionary with keys for flags and options, and a `"[]"` key for all other arguments. 19 | 20 | Single dash options (`-X`) and duplicate options (`-H "UserAgent: XYZ" -H "Content-type: application/json"`) are explicitly **not supported**. Use argparse or click if you need support for parsing these styles of argument. 21 | 22 | 23 | ## Examples 24 | 25 | ``` 26 | > pipenv run python args.py test --verbose --skip=test.py 27 | { 28 | "--verbose": true, 29 | "--skip": "test.py", 30 | "[]": [ 31 | "test" 32 | ] 33 | } 34 | ``` 35 | 36 | ``` 37 | > pipenv run python args.py run --port=1491:1491 --volume=~/config.cfg:/etc/sonic.cfg 38 | { 39 | "--port": "1491:1491", 40 | "--volume": "~/config.cfg:/etc/sonic.cfg", 41 | "[]": [ 42 | "run" 43 | ] 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/7eef17f37c63ce3cbddbdd154ff836f370d0ad70/Python.gitignore 2 | 3 | .vscode 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # celery beat schedule file 97 | celerybeat-schedule 98 | 99 | # SageMath parsed files 100 | *.sage.py 101 | 102 | # Environments 103 | .env 104 | .venv 105 | env/ 106 | venv/ 107 | ENV/ 108 | env.bak/ 109 | venv.bak/ 110 | 111 | # Spyder project settings 112 | .spyderproject 113 | .spyproject 114 | 115 | # Rope project settings 116 | .ropeproject 117 | 118 | # mkdocs documentation 119 | /site 120 | 121 | # mypy 122 | .mypy_cache/ 123 | .dmypy.json 124 | dmypy.json 125 | 126 | # Pyre type checker 127 | .pyre/ 128 | 129 | 130 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "dfc10fff42e3ba8a498208323198291e5e3d3e33cc2fc42131dc6d0675ec9a68" 5 | }, 6 | "host-environment-markers": { 7 | "implementation_name": "cpython", 8 | "implementation_version": "3.6.4", 9 | "os_name": "posix", 10 | "platform_machine": "x86_64", 11 | "platform_python_implementation": "CPython", 12 | "platform_release": "18.2.0", 13 | "platform_system": "Darwin", 14 | "platform_version": "Darwin Kernel Version 18.2.0: Thu Dec 20 20:46:53 PST 2018; root:xnu-4903.241.1~1/RELEASE_X86_64", 15 | "python_full_version": "3.6.4", 16 | "python_version": "3.6", 17 | "sys_platform": "darwin" 18 | }, 19 | "pipfile-spec": 6, 20 | "requires": {}, 21 | "sources": [ 22 | { 23 | "name": "pypi", 24 | "url": "https://pypi.python.org/simple", 25 | "verify_ssl": true 26 | } 27 | ] 28 | }, 29 | "default": { 30 | "appdirs": { 31 | "hashes": [ 32 | "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e", 33 | "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92" 34 | ], 35 | "version": "==1.4.3" 36 | }, 37 | "attrs": { 38 | "hashes": [ 39 | "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", 40 | "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" 41 | ], 42 | "version": "==19.1.0" 43 | }, 44 | "black": { 45 | "hashes": [ 46 | "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", 47 | "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c" 48 | ], 49 | "version": "==19.3b0" 50 | }, 51 | "click": { 52 | "hashes": [ 53 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 54 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 55 | ], 56 | "version": "==7.0" 57 | }, 58 | "toml": { 59 | "hashes": [ 60 | "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3", 61 | "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", 62 | "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c" 63 | ], 64 | "version": "==0.10.0" 65 | } 66 | }, 67 | "develop": {} 68 | } 69 | --------------------------------------------------------------------------------