Skip to content

Commit

Permalink
Merge pull request #201 from neithere/release/v0.31.0
Browse files Browse the repository at this point in the history
Release v0.31.0
  • Loading branch information
neithere committed Dec 30, 2023
2 parents 082fb32 + fddce1a commit e40014c
Show file tree
Hide file tree
Showing 25 changed files with 1,148 additions and 505 deletions.
2 changes: 1 addition & 1 deletion AUTHORS.rst
@@ -1,5 +1,5 @@
Contributors
~~~~~~~~~~~~
============

.. note::

Expand Down
69 changes: 67 additions & 2 deletions CHANGES.rst
@@ -1,6 +1,71 @@
~~~~~~~~~
Changelog
~~~~~~~~~
=========

Version 0.31.0 (2023-12-30)
---------------------------

Breaking changes:

- The typing hints introspection feature is automatically enabled for any
command (function) which does **not** have any arguments specified via `@arg`
decorator.

This means that, for example, the following function used to fail and now
it will pass::

def main(count: int):
assert isinstance(count, int)

This may lead to unexpected behaviour in some rare cases.

- A small change in the legacy argument mapping policy `BY_NAME_IF_HAS_DEFAULT`
concerning the order of variadic positional vs. keyword-only arguments.

The following function now results in ``main alpha [args ...] beta`` instead of
``main alpha beta [args ...]``::

def main(alpha, *args, beta): ...

This does **not** concern the default name mapping policy. Even for the
legacy one it's an edge case which is extremely unlikely to appear in any
real-life application.

- Removed the previously deprecated decorator `@expects_obj`.

Enhancements:

- Added experimental support for basic typing hints (issue #203)

The following hints are currently supported:

- ``str``, ``int``, ``float``, ``bool`` (goes to ``type``);
- ``list`` (affects ``nargs``), ``list[T]`` (first subtype goes into ``type``);
- ``Literal[T1, T2, ...]`` (interpreted as ``choices``);
- ``Optional[T]`` AKA ``T | None`` (currently interpreted as
``required=False`` for optional and ``nargs="?"`` for positional
arguments; likely to change in the future as use cases accumulate).

The exact interpretation of the type hints is subject to change in the
upcoming versions of Argh.

- Added `always_flush` argument to `dispatch()` (issue #145)

- High-level functions `argh.dispatch_command()` and `argh.dispatch_commands()`
now accept a new parameter `old_name_mapping_policy`. The behaviour hasn't
changed because the parameter is `True` by default. It will change to
`False` in Argh v.0.33 or v.1.0.

Deprecated:

- the `namespace` argument in `argh.dispatch()` and `argh.parse_and_resolve()`.
Rationale: continued API cleanup. It's already possible to mutate the
namespace object between parsing and calling the endpoint; it's unlikely that
anyone would need to specify a custom namespace class or pre-populate it
before parsing. Please file an issue if you have a valid use case.

Other changes:

- Refactoring.

Version 0.30.5 (2023-12-25)
---------------------------
Expand Down
100 changes: 59 additions & 41 deletions README.rst
Expand Up @@ -43,43 +43,44 @@ In a nutshell

`Argh`-powered applications are *simple* but *flexible*:

:Modular:
Declaration of commands can be decoupled from assembling and dispatching;

:Pythonic:
Commands are declared naturally, no complex API calls in most cases;
:Pythonic, KISS:
Commands are plain Python functions. Almost no CLI-specific API to learn.

:Reusable:
Commands are plain functions, can be used directly outside of CLI context;

:Layered:
The complexity of code raises with requirements;
Endpoint functions can be used directly outside of CLI context.

:Transparent:
The full power of argparse is available whenever needed;

:Namespaced:
Nested commands are a piece of cake, no messing with subparsers (though
they are of course used under the hood);

:Unobtrusive:
`Argh` can dispatch a subset of pure-`argparse` code, and pure-`argparse`
code can update and dispatch a parser assembled with `Argh`;
:Static typing friendly:
100% of the code including endpoint functions can be type-checked.
Argh is driven primarily by type annotations.

:DRY:
Don't Repeat Yourself. The amount of boilerplate code is minimal.
Among other things, `Argh` will:

* infer command name from function name;
* infer arguments from function signature;
* infer argument type from the default value;
* infer argument action from the default value (for booleans);
* infer argument types, actions and much more from annotations.

:Modular:
Declaration of commands can be decoupled from assembling and dispatching.

:Layered:
The complexity of code raises with requirements.

:Transparent:
You can directly access `argparse.ArgumentParser` if needed.

:Subcommands:
Easily nested commands. Argh isolates the complexity of subparsers.

:NIH free:
`Argh` supports *completion*, *progress bars* and everything else by being
friendly to excellent 3rd-party libraries. No need to reinvent the wheel.

Sounds good? Check the tutorial!
:Compact:
No dependencies apart from Python's standard library.

Sounds good? Check the :doc:`quickstart` and the :doc:`tutorial`!

Relation to argparse
--------------------
Expand All @@ -98,6 +99,9 @@ Installation
Examples
--------

Hello World
...........

A very simple application with one command:

.. code-block:: python
Expand All @@ -116,6 +120,29 @@ Run it:
$ ./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
Expand All @@ -133,6 +160,9 @@ Run it:
$ ./app.py echo Hey
Hey
Modularity
..........

A potentially modular application with more control over the process:

.. code-block:: python
Expand Down Expand Up @@ -195,31 +225,19 @@ to CLI arguments)::

(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("text", default="hello world", nargs="+", help="The message")
def echo(text: str) -> None:
print text
The approaches can be safely combined even up to this level:

.. code-block:: python
@arg("words", default="hello world", nargs="+", help="The message")
def echo(words: list[str]) -> str:
return " ".join(words)
# adding help to `foo` which is in the function signature:
@arg("foo", help="blah")
# these are not in the signature so they go to **kwargs:
@arg("baz")
@arg("-q", "--quux")
# the function itself:
def cmd(foo: str, bar: int = 1, *args, **kwargs) -> Iterator[str]:
yield foo
yield bar
yield ", ".join(args)
yield kwargs["baz"]
yield kwargs["quux"]
Please note that decorators will soon be fully replaced with annotations.

Links
-----
Expand Down
9 changes: 2 additions & 7 deletions docs/source/cookbook.rst
@@ -1,5 +1,5 @@
Cookbook
~~~~~~~~
========

Multiple values per argument
----------------------------
Expand Down Expand Up @@ -46,9 +46,4 @@ will be eventually the default one):
distros = ("abc", "xyz")
return [d for d in distros if any(p in d for p in patterns)]
if __name__ == "__main__":
parser = argh.ArghParser()
parser.set_default_command(
cmd, name_mapping_policy=argh.assembling.NameMappingPolicy.BY_NAME_IF_KWONLY
)
argh.dispatch(parser)
argh.dispatch_command(cmd, old_name_mapping_policy=False)
1 change: 1 addition & 0 deletions docs/source/index.rst
Expand Up @@ -23,6 +23,7 @@ Details
.. toctree::
:maxdepth: 2

quickstart
tutorial
reference
cookbook
Expand Down
2 changes: 1 addition & 1 deletion docs/source/projects.rst
@@ -1,5 +1,5 @@
Real-life usage
~~~~~~~~~~~~~~~
===============

Below are some examples of applications using `argh`, grouped by supported
version of Python.
Expand Down

0 comments on commit e40014c

Please sign in to comment.