From 971ce4c7e991556a7ba06ba1ea6e062be4e55e1b Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Mon, 1 Jan 2024 23:01:13 +0100 Subject: [PATCH 1/6] docs: cleanup README, rearrange the rest --- .readthedocs.yml | 2 +- CHANGES.rst => CHANGELOG.rst | 0 README.rst | 255 ++++++++------------------- docs/CONTRIBUTING.rst | 72 ++++++++ docs/Makefile | 2 +- docs/changes.rst | 1 + docs/{source => }/conf.py | 2 +- AUTHORS.rst => docs/contributors.rst | 4 + docs/{source => }/cookbook.rst | 0 docs/{source => }/index.rst | 12 +- docs/{source => }/projects.rst | 0 docs/{source => }/quickstart.rst | 0 docs/{source => }/reference.rst | 0 docs/{source => }/similar.rst | 0 docs/source/changes.rst | 1 - docs/source/contributors.rst | 1 - docs/{source => }/subparsers.rst | 0 docs/{source => }/the_story.rst | 0 docs/{source => }/tutorial.rst | 0 tox.ini | 2 +- 20 files changed, 157 insertions(+), 197 deletions(-) rename CHANGES.rst => CHANGELOG.rst (100%) create mode 100644 docs/CONTRIBUTING.rst create mode 100644 docs/changes.rst rename docs/{source => }/conf.py (94%) rename AUTHORS.rst => docs/contributors.rst (97%) rename docs/{source => }/cookbook.rst (100%) rename docs/{source => }/index.rst (88%) rename docs/{source => }/projects.rst (100%) rename docs/{source => }/quickstart.rst (100%) rename docs/{source => }/reference.rst (100%) rename docs/{source => }/similar.rst (100%) delete mode 100644 docs/source/changes.rst delete mode 100644 docs/source/contributors.rst rename docs/{source => }/subparsers.rst (100%) rename docs/{source => }/the_story.rst (100%) rename docs/{source => }/tutorial.rst (100%) diff --git a/.readthedocs.yml b/.readthedocs.yml index a0eb399..933eff2 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,7 +13,7 @@ build: # Build documentation in the docs/ directory with Sphinx sphinx: - configuration: docs/source/conf.py + configuration: docs/conf.py # NOTE: PDF generation was intentionally disabled due to failed builds. # Should you require enabling this, please make sure that it's tested. diff --git a/CHANGES.rst b/CHANGELOG.rst similarity index 100% rename from CHANGES.rst rename to CHANGELOG.rst diff --git a/README.rst b/README.rst index d0ce5e4..d3ab12d 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -Argh: The Natural CLI -===================== +Argh: The Effortless CLI +======================== .. image:: https://github.com/neithere/argh/actions/workflows/lint-and-test.yml/badge.svg :target: https://github.com/neithere/argh/actions/workflows/lint-and-test.yml @@ -25,21 +25,71 @@ Argh: The Natural CLI .. image:: https://readthedocs.org/projects/argh/badge/?version=latest :target: http://argh.readthedocs.org/en/latest/ +**The power of Argparse with plain Python functions!** + Building a command-line interface? Found yourself uttering "argh!" while struggling with the API of `argparse`? Don't like the complexity but need the power? -.. epigraph:: +`Argh` builds on the power of `argparse` (which comes with Python) and makes it +really easy to use. It eliminates the complex API and lets you "dispatch" +ordinary Python functions as CLI commands. + +Installation +------------ - Everything should be made as simple as possible, but no simpler. +:: - -- Albert Einstein (probably) + $ pip install argh -`Argh` is a smart wrapper for `argparse`. `Argparse` is a very powerful tool; -`Argh` just makes it easy to use. +Example +------- -In a nutshell -------------- +.. code-block:: python + + import argh + + def verify_paths(paths: list[str], *, verbose: bool = False): + """ + Verify that all given paths exist. + """ + for path in paths: + if verbose: + print(f"Checking {path}...") + assert os.path.exists(path) + + argh.dispatch_command(verify_paths) + +Now you can run the script like this: + +.. code-block:: bash + + $ python app.py foo.txt bar/quux.txt + + $ python app.py foo.txt bar/quux.txt --verbose + Checking foo.txt... + Checking bar/quux.txt... + + $ python app.py -h + usage: app.py [-h] [-v] [paths ...] + + Verify that all given paths exist. + + positional arguments: + paths - + + options: + -h, --help show this help message and exit + -v, --verbose False + +Please check the documentation for examples of multiple commands, modularity, +help generation, advanced type annotations inspection, decorators and more: + +* `Quick Start `_ +* `Tutorial `_ + +Why Argh? +--------- `Argh`-powered applications are *simple* but *flexible*: @@ -80,198 +130,33 @@ In a nutshell :Compact: No dependencies apart from Python's standard library. -Sounds good? Dive in! :) - -* `Quick Start `_ -* `Tutorial `_ - -Relation to argparse --------------------- - -`Argh` is fully compatible with `argparse`. You can mix `Argh`-agnostic and -`Argh`-aware code. Just keep in mind that the dispatcher does some extra work -that a custom dispatcher may not do. - -Installation ------------- - -:: - - $ pip install argh - -Examples --------- - -Hello World -........... - -A very simple application with one command: - -.. code-block:: python - - import argh - - def main() -> str: - return "Hello world" - - argh.dispatch_command(main) - -Run it: - -.. code-block:: bash - - $ ./app.py - Hello world - -Type Annotations -................ - -Type annotations are used to infer argument types: - -.. code-block:: python - - def summarise(numbers: list[int]) -> int: - return sum(numbers) - - argh.dispatch_command(summarise) - -Run it (note that ``nargs="+"`` + ``type=int`` were inferred from the -annotation): - -.. code-block:: bash - - $ ./app.py 1 2 3 - 6 - -Multiple Commands -................. - -An app with multiple commands: - -.. code-block:: python - - import argh - - from my_commands import hello, echo - - argh.dispatch_commands([hello, echo]) - -Run it: - -.. code-block:: bash - - $ ./app.py echo Hey - Hey - -Modularity -.......... - -A potentially modular application with more control over the process: - -.. code-block:: python - - import argh - - # declaring: - - def echo(text): - "Returns given word as is." - return text - - def greet(name: str, *, greeting: str = "Hello") -> str: - "Greets the user with given name. The greeting is customizable." - return f"{greeting}, {name}!" - - # assembling: - - parser = argh.ArghParser() - parser.add_commands([echo, greet]) - - # dispatching: - - if __name__ == "__main__": - parser.dispatch() - -.. code-block:: bash - - $ ./app.py greet Andy - Hello, Andy - - $ ./app.py greet Andy -g Arrrgh - Arrrgh, Andy - -Here's the auto-generated help for this application (note how the docstrings -are reused):: - - $ ./app.py --help - - usage: app.py {echo,greet} ... - - positional arguments: - echo Returns given word as is. - greet Greets the user with given name. The greeting is customizable. - -...and for a specific command (an ordinary function signature is converted -to CLI arguments):: - - $ ./app.py --help greet - - usage: app.py greet [-g GREETING] name - - Greets the user with given name. The greeting is customizable. - - positional arguments: - name - - optional arguments: - -g GREETING, --greeting GREETING 'Hello' - -(The help messages have been simplified a bit for brevity.) - -Decorators -.......... - -`Argh` easily maps plain Python functions to CLI. Sometimes this is not -enough; in these cases the powerful API of `argparse` is also available: - -.. code-block:: python - - @arg("words", default="hello world", nargs="+", help="The message") - def echo(words: list[str]) -> str: - return " ".join(words) - -Please note that decorators will soon be fully replaced with annotations. - Links ----- -* `Project home page`_ (GitHub) -* `Documentation`_ (Read the Docs) -* `Package distribution`_ (PyPI) -* Questions, requests, bug reports, etc.: - - * `Issue tracker`_ (GitHub) - * Direct e-mail (neithere at gmail com) +See also the `project page on GitHub`_, `documentation`_ and `PyPI page`_. -.. _project home page: http://github.com/neithere/argh/ +.. _project page on GitHub: http://github.com/neithere/argh/ .. _documentation: http://argh.readthedocs.org -.. _package distribution: http://pypi.python.org/pypi/argh -.. _issue tracker: http://github.com/neithere/argh/issues/ +.. _PyPI page: http://pypi.python.org/pypi/argh Author ------ Developed by Andrey Mikhaylenko since 2010. -See file `AUTHORS.rst` for a list of contributors to this library. +See `contributors `_ +for a list of contributors to this library. -Support -------- +Contribute +---------- The fastest way to improve this project is to submit tested and documented patches or detailed bug reports. -You can also `donate via Liberapay`_. This may speed up development or simply +Donate +------ + +You can `donate via Liberapay`_. This may speed up development or simply make the original author happy :) .. _donate via Liberapay: https://liberapay.com/neithere/donate diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst new file mode 100644 index 0000000..4e603d9 --- /dev/null +++ b/docs/CONTRIBUTING.rst @@ -0,0 +1,72 @@ +Contributing to Argh +==================== + +Argh would not be so good without the FOSS community. + +Your contributions matter! + +This document describes how to contribute to Argh. + +Issues, Bug reports, Feature requests +------------------------------------- + +If you have a question, a bug report or a feature request, please +`create an issue `. + +Please include, if possible: + +* a minimal reproducible example; +* a description of the expected and observed behaviour; +* a description of your environment (OS, Python version, etc.). + +Code changes +------------ + +Starting work on a new release +.............................. + +* Assign tasks to the release milestone. +* Create a release branch (e.g. `release/v0.30.4`) from `master`. +* Bump version in `pyproject.toml`. +* Create section in `CHANGES.rst` for the new version. +* Create a pull request from the release branch to `master`. + +Contributing to the release +........................... + +* Create a feature branch from the release branch. +* Make changes, commit, push, create a pull request (target branch = release). +* Make sure the pipeline is green. +* Ask for review. +* Merge the pull request. + + * Default strategy: squash. Fast-forward or rebase if: + + - there are multiple commits from different authors; + - there are commits which are important to keep separate. + +Finalising the release +...................... + +* Make sure the pipeline is green. +* Make sure all tasks in the release milestone are "ready for release". +* Update `CHANGES.rst`, then proof-read on RTD: + + * make sure all merged PRs are mentioned; + * add current date in section title. + +* Create a GitHub release: https://github.com/neithere/argh/releases/new + + * based on the release branch; + * new tag in the format `v0.30.4`; + * tick "Set as the latest release" checkbox; + * click the "Generate release notes" button; + * add link to RTD changelog. + +* Monitor the release pipeline: https://github.com/neithere/argh/actions + + * if it failed, fix and re-create the release with the same tag. + +* Merge the release branch into `master`. + + * don't squash! diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf..ed88099 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -5,7 +5,7 @@ # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build -SOURCEDIR = source +SOURCEDIR = . BUILDDIR = build # Put it first so that "make" without argument is like "make help". diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 0000000..565b052 --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1 @@ +.. include:: ../CHANGELOG.rst diff --git a/docs/source/conf.py b/docs/conf.py similarity index 94% rename from docs/source/conf.py rename to docs/conf.py index 0d8b552..c54bf35 100644 --- a/docs/source/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ current_year = date.today().year -config = SphinxConfig("../../pyproject.toml") +config = SphinxConfig("../pyproject.toml") # -- Project information diff --git a/AUTHORS.rst b/docs/contributors.rst similarity index 97% rename from AUTHORS.rst rename to docs/contributors.rst index 66c12cb..3a0a655 100644 --- a/AUTHORS.rst +++ b/docs/contributors.rst @@ -15,6 +15,8 @@ Contributors Also, please don't hesitate to file issues_ and create pull requests and *become a contributor*! ❤️ + Here's how to contribute: :doc:`CONTRIBUTING`_. + .. _issues: https://github.com/neithere/argh/issues Historical List of Contributors @@ -87,5 +89,7 @@ platform. Thanks to Github_ team for the place where Argh has been developed for over a decade. +See also :doc:`similar`. + .. _bitbucket: https://bitbucket.org .. _github: https://github.com diff --git a/docs/source/cookbook.rst b/docs/cookbook.rst similarity index 100% rename from docs/source/cookbook.rst rename to docs/cookbook.rst diff --git a/docs/source/index.rst b/docs/index.rst similarity index 88% rename from docs/source/index.rst rename to docs/index.rst index 4aa4b67..e4047b1 100644 --- a/docs/source/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -.. include:: ../../README.rst +.. include:: ../README.rst Dependencies ------------ @@ -12,11 +12,6 @@ of Argh (the numeric puns were semi-intentional): * Python 2.6 — Argh 0.26.x. * Python 2.7 — Argh 0.27.x. -Why this one? -------------- - -See :doc:`similar`. - Details ------- @@ -25,6 +20,10 @@ Details quickstart tutorial + +.. toctree:: + :maxdepth: 1 + reference cookbook the_story @@ -32,6 +31,7 @@ Details projects subparsers contributors + CONTRIBUTING changes Indices and tables diff --git a/docs/source/projects.rst b/docs/projects.rst similarity index 100% rename from docs/source/projects.rst rename to docs/projects.rst diff --git a/docs/source/quickstart.rst b/docs/quickstart.rst similarity index 100% rename from docs/source/quickstart.rst rename to docs/quickstart.rst diff --git a/docs/source/reference.rst b/docs/reference.rst similarity index 100% rename from docs/source/reference.rst rename to docs/reference.rst diff --git a/docs/source/similar.rst b/docs/similar.rst similarity index 100% rename from docs/source/similar.rst rename to docs/similar.rst diff --git a/docs/source/changes.rst b/docs/source/changes.rst deleted file mode 100644 index d76c92b..0000000 --- a/docs/source/changes.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../CHANGES.rst diff --git a/docs/source/contributors.rst b/docs/source/contributors.rst deleted file mode 100644 index 7739272..0000000 --- a/docs/source/contributors.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../AUTHORS.rst diff --git a/docs/source/subparsers.rst b/docs/subparsers.rst similarity index 100% rename from docs/source/subparsers.rst rename to docs/subparsers.rst diff --git a/docs/source/the_story.rst b/docs/the_story.rst similarity index 100% rename from docs/source/the_story.rst rename to docs/the_story.rst diff --git a/docs/source/tutorial.rst b/docs/tutorial.rst similarity index 100% rename from docs/source/tutorial.rst rename to docs/tutorial.rst diff --git a/tox.ini b/tox.ini index 55bb690..c3eb094 100644 --- a/tox.ini +++ b/tox.ini @@ -44,7 +44,7 @@ changedir = docs/ allowlist_externals = rm commands = rm -rf docs/build - sphinx-build source build --color -W --keep-going -n -b html {posargs} + sphinx-build . build --color -W --keep-going -n -b html {posargs} [testenv:lint] description = verify with linters From edb7263357a37a0eb1c6f7289c902f6fd707168f Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Mon, 1 Jan 2024 23:36:04 +0100 Subject: [PATCH 2/6] chore: bump version --- CHANGELOG.rst | 7 +++++++ pyproject.toml | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4fd744b..330f520 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Changelog ========= +Version 0.31.1 (TODO: DATE) +--------------------------- + +Enhancements: + +- cleaned up the README, rearranged other documentation. + Version 0.31.0 (2023-12-30) --------------------------- diff --git a/pyproject.toml b/pyproject.toml index 159e27b..00db708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "flit_core.buildapi" [project] name = "argh" -version = "0.31.0" -description = "An unobtrusive argparse wrapper with natural syntax" +version = "0.31.1" +description = "Plain Python functions as CLI commands without boilerplate" readme = "README.rst" requires-python = ">=3.8" license = { file = "COPYING.LESSER" } From fdbb642e3ef5120db3b4cab13b92b764d80c1252 Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Mon, 1 Jan 2024 23:40:59 +0100 Subject: [PATCH 3/6] docs: fix typos --- docs/CONTRIBUTING.rst | 2 +- docs/contributors.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst index 4e603d9..062406d 100644 --- a/docs/CONTRIBUTING.rst +++ b/docs/CONTRIBUTING.rst @@ -11,7 +11,7 @@ Issues, Bug reports, Feature requests ------------------------------------- If you have a question, a bug report or a feature request, please -`create an issue `. +`create an issue `_. Please include, if possible: diff --git a/docs/contributors.rst b/docs/contributors.rst index 3a0a655..d70f58c 100644 --- a/docs/contributors.rst +++ b/docs/contributors.rst @@ -15,7 +15,7 @@ Contributors Also, please don't hesitate to file issues_ and create pull requests and *become a contributor*! ❤️ - Here's how to contribute: :doc:`CONTRIBUTING`_. + Here's how to contribute: :doc:`CONTRIBUTING`. .. _issues: https://github.com/neithere/argh/issues From bf573bbc1f859688736a6dbb47ce5dcffe70bcc2 Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Mon, 1 Jan 2024 23:41:52 +0100 Subject: [PATCH 4/6] chore: improve bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8f28dd4..ebf3645 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,14 +11,14 @@ assignees: neithere A clear and concise description of what the bug is. ### To Reproduce -Example script: -``` +Python script: +```python import argh SOME CODE ``` -Example command line interaction: +Command line input/output: ``` $ my-script.py ... SOME OUTPUT From e79048d24c4870f84f198a46884c7047f4a4cc8c Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Tue, 2 Jan 2024 19:55:22 +0100 Subject: [PATCH 5/6] docs: don't ref enum name (won't work in Sphinx) --- src/argh/assembling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/argh/assembling.py b/src/argh/assembling.py index 2dc554f..a7860d7 100644 --- a/src/argh/assembling.py +++ b/src/argh/assembling.py @@ -382,7 +382,7 @@ def set_default_command( The policy to use when mapping function arguments onto CLI arguments. See :class:`.NameMappingPolicy`. If not defined explicitly, - :meth:`.NameMappingPolicy.BY_NAME_IF_KWONLY` is used. + `BY_NAME_IF_KWONLY` is used. .. versionadded:: 0.30 From 8f270d01c6ce8c8571a5c5055de2f78273eba094 Mon Sep 17 00:00:00 2001 From: Andy Mikhaylenko Date: Fri, 19 Jan 2024 17:39:48 +0100 Subject: [PATCH 6/6] fix: broken support for type alias List (fixes #216) --- CHANGELOG.rst | 6 +++++- src/argh/assembling.py | 2 +- tests/test_typing_hints.py | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 330f520..f35dd60 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,9 +1,13 @@ Changelog ========= -Version 0.31.1 (TODO: DATE) +Version 0.31.1 (2024-01-19) --------------------------- +Bugs fixed: + +- broken support for type alias `List` (issue #216). + Enhancements: - cleaned up the README, rearranged other documentation. diff --git a/src/argh/assembling.py b/src/argh/assembling.py index a7860d7..02aefe5 100644 --- a/src/argh/assembling.py +++ b/src/argh/assembling.py @@ -756,7 +756,7 @@ def typing_hint_to_arg_spec_params( } # `list` - if type_def == list: + if type_def in (list, List): return {"nargs": ZERO_OR_MORE} # `Literal["a", "b"]` diff --git a/tests/test_typing_hints.py b/tests/test_typing_hints.py index 1b02da7..2432824 100644 --- a/tests/test_typing_hints.py +++ b/tests/test_typing_hints.py @@ -34,6 +34,7 @@ def test_list(): guess = TypingHintArgSpecGuesser.typing_hint_to_arg_spec_params assert guess(list) == {"nargs": "*"} + assert guess(List) == {"nargs": "*"} assert guess(Optional[list]) == {"nargs": "*", "required": False} assert guess(List[str]) == {"nargs": "*", "type": str}