{"id":29192320,"url":"https://github.com/linuskmr/clipyarser","last_synced_at":"2025-07-02T01:06:13.186Z","repository":{"id":58464305,"uuid":"532036602","full_name":"linuskmr/clipyarser","owner":"linuskmr","description":"A simple, declarative and easy-testable command line argument parser","archived":false,"fork":false,"pushed_at":"2022-12-20T12:15:08.000Z","size":35,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-24T17:16:22.514Z","etag":null,"topics":["argparse","argumentparser","cli","parser"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/clipyarser/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/linuskmr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-02T18:24:41.000Z","updated_at":"2022-09-26T16:00:06.000Z","dependencies_parsed_at":"2023-01-30T00:31:05.859Z","dependency_job_id":null,"html_url":"https://github.com/linuskmr/clipyarser","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/linuskmr/clipyarser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuskmr%2Fclipyarser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuskmr%2Fclipyarser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuskmr%2Fclipyarser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuskmr%2Fclipyarser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linuskmr","download_url":"https://codeload.github.com/linuskmr/clipyarser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuskmr%2Fclipyarser/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261865546,"owners_count":23221974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argparse","argumentparser","cli","parser"],"created_at":"2025-07-02T01:06:11.956Z","updated_at":"2025-07-02T01:06:13.121Z","avatar_url":"https://github.com/linuskmr.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clipyarser\n\n`clipyarser` is a simple, declarative and easy-testable command line argument parser.\nIt's inspired by [Typer](https://github.com/tiangolo/typer).\n\nSimply decorate your _normal_ python functions with `@clipyarser.main` or `@clipyarser.subcommand` and\nyou have a fully working cli program.\n`clipyarser` parses the arguments from the console and calls the matching function with the right arguments.\n\n\n## Features\n\n- No dependencies: Argument parsing is done with Python's [argparse](https://docs.python.org/3/library/argparse.html) module\n- Parsing and validation based on *type hints*\n- Global arguments via `@clipyarser.main`\n- [Subcommands](#subcommands) with `@clipyarser.subcommand`\n- [Default arguments](#default-argumentsoptions)\n- [Easily testing](#testing), because functions `yield` instead of `print()` lines\n\n\n\n## TL;DR — Show me the code\n\nExample: Calculator with `add` and `sub` *subcommands*, and a *global* `verbose` switch.\n\n```python3\nfrom clipyarser import Clipyarser\n\nclipyarser = Clipyarser()\n\nverbose_output = None\n\n@clipyarser.subcommand\ndef add(lhs: int, rhs: int):\n\t\"\"\"Compute lhs + rhs\"\"\"\n\tif verbose_output:\n\t\tyield f\"Computing {lhs} + {rhs}\"\n\tyield lhs + rhs\n\n@clipyarser.subcommand\ndef sub(lhs: int, rhs: int = 0):\n\t\"\"\"Compute lhs - rhs\"\"\"\n\tif verbose_output:\n\t\tyield f\"Computing {lhs} - {rhs}\"\n\tyield lhs + rhs\n\n@clipyarser.main\ndef main(verbose: bool = False):\n\t\"\"\"Useless calculator\"\"\"\n\t# main gets always called\n\t# We use it for arguments used in *all* subcommands\n\tglobal verbose_output\n\tverbose_output = verbose\n\nif __name__ == '__main__':\n\tclipyarser.run()\n```\n\nHelp message with doc comment for `main`:\n\n```\n$ python3 cli.py --help\nusage: cli.py [-h] [--verbose VERBOSE] {add,sub} ...\n\nUseless calculator\n\npositional arguments:\n  {add,sub}\n\noptional arguments:\n  -h, --help         show this help message and exit\n  --verbose VERBOSE\n```\n\nHelp message for `add`:\n\n```\n$ python3 cli.py add --help\nusage: cli.py add [-h] lhs rhs\n\nCompute lhs + rhs\n\npositional arguments:\n  lhs\n  rhs\n\noptional arguments:\n  -h, --help  show this help message and exit\n```\n\nInvoke add:\n\n```\n$ python3 cli.py add 3 4\n7\n```\n\nInvoke sub with default argument and use global `verbose` argument:\n\n```\n$ python3 cli.py --verbose True sub 5\nComputing 5 - 0\n5\n```\n\nError handling:\n\n```\n$ python3 cli.py add\nusage: cli.py add [-h] lhs rhs\ncli.py add: error: the following arguments are required: lhs, rhs\n```\n\n\n\n## Tutorial\n\nHere we will create a small command line application with `clipyarser`.\n\n### Setup\n\nCreate a new directory and an empty `main.py` file.\n\nIn `main.py` import `clipyarser`:\n\n```python\nfrom clipyarser import CLI\n```\n\n### Create a CLI parser\n\nTo create a `clipyarser` parser, simply write this into the `main.py` file.\n\n```python\nfrom clipyarser import Clipyarser\n\nclipyarser = Clipyarser()\n```\n\nNow we create a simple greeting function.\n\n```python\n@clipyarser.main\ndef main(name: str) -\u003e str:\n    return f'Hello {name}'\n```\n\nThis function simply returns a string which says \"hello\" to a person.\nThe name of the person is specified in the arguments of the function.\n\nNote that we use **type hints** in the function arguments.\nThis is necessary for `clipyarser`, because it parses the arguments from the console according to the specified types.\nSo when adding an argument `age` with type `int`, `clipyarser` does the parsing and validation of an `int` for you.\n\nNow, we have to execute `clipyarser` when the program is run.\n\n```python\nif __name__ == '__main__':\n    clipyarser.run()\n```\n\nAnd that is the first running example of `clipyarser`.\n\nNow lets test it.\n\n```shell\n$ python3 main.py\nusage: example.py [-h] name {} ...\nexample.py: error: the following arguments are required: name\n```\n\nOk, we have to specify the name of the person we want to greet.\nThis makes sense, because we take `name` as argument in our `main()` function.\n\n```shell\n$ python3 main.py Linus\nHello Linus\n```\n\nOk nice. That worked.\n\n### Default Arguments/Options\n\nSometimes it is convenient that a function has default arguments, which can be overridden when calling the function.\nThis is also possible with `clipyarser`. Let's try to take our greeting function from above and extend it with an\noptional argument.\n\n```python\n@clipyarser.main\ndef main(name: str, greeting: str = 'Hello') -\u003e str:\n  return f'{greeting} {name}'\n```\n\nSo a user can customize the greeting. By default, the greeting is 'Hello', but it can be overridden.\nNow let's see how this looks in the console.\n\n```shell\n$ python3 main.py Linus\nHello Linus\n$ python3 main.py Linus --greeting Hi\nHi Linus\n```\n\nPretty intiutive, right?\n\n### Subcommands\n\nIn a bigger application, you may don't want all logic in a `main()` function.\nTherefore, `clipyarser` allows you to add subcommands.\n\n```python3\nfrom clipyarser import Clipyarser\n\nclipyarser = Clipyarser()\n\n@clipyarser.subcommand\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n```\n\nThis subcommand is a function which adds to numbers together.\n\n```shell\n$ python3 main.py add 2 4\n6\n```\n\nBut what has happened to the main function? For simplicity, we have deleted it.\nLet's try to add one again.\n\n```shell\n@clipyarser.main\ndef main(verbose: bool = False):\n  return 'verbose is {verbose}'\n```\n\nNow, when calling our application, the `main()` function *always* runs.\nThis might be handy when you have some logic or arguments that are independent of am individual subcommand, like a \nmore verbose output.\n\n```shell\n$ python3 main.py add 3 2\nverbose is False\n5\n$ python3 main.py --verbose true add 3 2\nverbose is True\n5\n```\n\n### Printing multiple lines\n\nUntil now, when we want to print something to the console, we just returned it.\nThis might seem ok, but sometimes you want to print multiple lines or want to print something during a calculation.\nBut simple `print()`ing is not a good idea. We will se soon [why](#testing).\n\nTo print multiple lines, use the `yield` statement.\n\nBack to our greeting example from the beginning.\n\n```python3\nfrom time import sleep\nfrom clipyarser import Clipyarser\n\nclipyarser = Clipyarser()\n\n\n@clipyarser.main\ndef main(name: str):\n    yield 'Hello'\n    sleep(2)\n    yield name\n    yield 42\n```\n\n```shell\n$ python3 main.py Linus\nHello\nLinus\n42\n```\n\nBecause yield turns `main()` into a *generator function*, the output 'Hello' is printed *immediately*, but `name` takes \ntwo seconds to print.\n\nMaybe one could think of another solution: Just add all things to be printed to a list and return this list at the \nend of the function.\nThis is bad, because the whole output would take two seconds to print, in particular the 'Hello' line.\nThis makes your `clipyarser` not very responsive to the end user.\n\nOk. But why is just printing a bad idea? Testing.\n\n### Testing\n\nThe advantage of CLI is simple testing.\nFunctions like `main()` and `add()` are *normal python functions*, so you can call them like normal functions.\nThey take *normal* arguments. They return *normal* things, e.g. a generator instead of printing.\nThis means that you can also test these functions like normal functions.\n\n```python3\nimport unittest\nfrom clipyarser import Clipyarser\n\nclipyarser = Clipyarser()\n\n\nclass TestMyCli(unittest.TestCase):\n    def test_greet(self):\n        actual = list(main('Linus'))\n        expected = ['Hello', 'Linus', 42]\n        self.assertEqual(actual, expected)\n```\n\nSince `main()` is a generator function, we can convert its output to a list and check if it is what we expect.\nIf using `print()`, this would not be as easy.\n\nOk, this is the end of the tutorial. Have fun using `clipyarser`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuskmr%2Fclipyarser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinuskmr%2Fclipyarser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuskmr%2Fclipyarser/lists"}