Skip to content

Commit

Permalink
Move table out of README.md, give small example instead. Add details …
Browse files Browse the repository at this point in the history
…to tutorial.
  • Loading branch information
treykeown committed Jun 23, 2023
1 parent 1d4de47 commit 3f874a4
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 63 deletions.
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
</p>
<hr>

`arguably` turns functions into command line interfaces (CLIs). `arguably` has a tiny API and is extremely easy to
integrate. You can also use it directly through `python3 -m arguably your_script.py`, more on that
`arguably` turns functions and docstrings into command line interfaces (CLIs). `arguably` has a tiny API and is
extremely easy to integrate. You can also use it directly through `python3 -m arguably your_script.py`, more on that
[here](#no-integration-required).

To use `arguably` in a script, decorate any functions that should appear on the command line with `@arguably.command`,
Expand All @@ -41,11 +41,12 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
option: [-x] keyword-only args are options, short name is in brackets
"""
print(f"{required=}, {not_required=}, {others=}, {option=}")

if __name__ == "__main__":
arguably.run()
Expand All @@ -60,23 +61,27 @@ usage: intro.py [-h] [-x OPTION] required [not-required] [others ...]
this function is on the command line!

positional arguments:
required a required parameter (type: str)
not-required this one isn't required, since it has a default (type: int, default: 2)
required a required argument (type: str)
not-required this one isn't required, since it has a default value (type: int, default: 2)
others all the other positional arguments go here (type: int)

options:
-h, --help show this help message and exit
-x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
-x, --option OPTION keyword-only args are options, short name is in brackets (type: float, default: 3.14)
```

`arguably` looks at any decorated functions and maps their arguments from Python to the CLI:
Arguments to the CLI look just like calling the Python function.

| This Python ... | ... becomes this on the CLI. |
|------------------------------------------------|------------------------------------------------|
| positional args, no default `required` | positional CLI args, required `required` |
| positional args, with default `not_required=2` | positional CLI args, optional `[not-required]` |
| positional args, variadic `*others` | any extra positional CLI args `[others ...]` |
| keyword-only arguments `option` | command-line options `[-x OPTION]` |
```pycon
>>> from intro import some_function
>>> some_function("asdf", 0, 7, 8, 9, option=2.71)
required='asdf', not_required=0, others=(7, 8, 9), option=2.71
```

```console
user@machine:~$ ./intro.py asdf 0 7 8 9 --option 2.71
required='asdf', not_required=0, others=(7, 8, 9), option=2.71
```

`arguably` uses your docstrings to automatically generate help messages. It supports all major formats for docstrings:
reStructuredText, Google, Numpydoc, and Epydoc.
Expand Down
12 changes: 6 additions & 6 deletions arguably/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,10 +838,10 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
option: [-x] keyword-only args are options, short name is in brackets
\"\"\"
if __name__ == "__main__":
Expand All @@ -855,13 +855,13 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
positional arguments:
required a required parameter (type: str)
not-required this one isn't required, since it has a default (type: int, default: 2)
required a required argument (type: str)
not-required this one isn't required, since it has a default value (type: int, default: 2)
others all the other positional arguments go here (type: int)
options:
-h, --help show this help message and exit
-x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
-x, --option OPTION keyword-only args are options, short name is in brackets (type: float, default: 3.14)
```
Or, with multiple commands:
Expand Down
104 changes: 87 additions & 17 deletions docs/tutorial/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,16 @@ Google, Numpydoc, or Epydoc. We'll use Google's style for this example.

```python
@arguably.command
def hello(*, name="world"):
def hello(*from_, name="world"):
"""
this will say hello to someone
Args:
from_: greetings are sent from these people
name: is who this will greet
"""
print(f"Hello, {name}!")
print(f"From: {', '.join(from_)}")
```

```console
Expand All @@ -220,22 +222,62 @@ options:
--name NAME is who this will greet (type: str, default: world)
```

### Short names and metavars
### Option names

By default, any `--options` will have a long name which is a [normalized version](../subcommands/#name-normalization)
of their Python name. Options do not have a short name by default.

Option names can be controlled by prefixing their description with a value in square brackets `[]`:

* `[-t]` &rightarrow; `-t` is the short name
* `[-t/--to]` &rightarrow; `-t` is the short name and `--to` is the long name

<div align="right" class="code-source"><sub>
<a href="https://github.com/treykeown/arguably/blob/main/etc/scripts/hello-6.py">[source]</a>
</sub></div>

```python
@arguably.command
def hello(*from_, name="world"):
"""
this will say hello to someone
There are two special things you can put in your docstring that `arguably` will use.
Args:
from_: greetings are sent from these people
name: [-t/--to] is who this will greet
"""
print(f"Hello, {name}!")
print(f"From: {', '.join(from_)}")
```

| Format | Applies to... | Function |
|-----------------|-----------------|------------------------------|
| `[-n] ...` | `--option` only | Short name for an `--option` |
| `... {WHO} ...` | any argument | Metavar for an argument |
```console
user@machine:~$ python3 hello-6.py -h
usage: hello-6.py [-h] [-t TO] [from ...]

this will say hello to someone

positional arguments:
from greetings are sent from these people (type: str)

options:
-h, --help show this help message and exit
-t, --to TO is who this will greet (type: str, default: world)
```

### Metavars

A metavar is what gets printed in the usage string to represent the user-provided value. More explanation for that
[here](https://docs.python.org/3/library/argparse.html#metavar).

An example of using these directives to alias `--name` to `-n`, and to make its metavar `who`:
By default, the metavar for any argument is the uppercase version of its name. To change the metavar, wrap any word in
its description in curly braces `{}`. Tuples can specify one value or a number of comma-separated values equal to the
tuple length.

* `{who}` &rightarrow; `WHO` is the metavar
* `{x,y,z}` &rightarrow; `X`, `Y`, and `Z` are the metavars for a tuple of length 3

<div align="right" class="code-source"><sub>
<a href="https://github.com/treykeown/arguably/blob/main/etc/scripts/hello-6.py">[source]</a>
<a href="https://github.com/treykeown/arguably/blob/main/etc/scripts/hello-7.py">[source]</a>
</sub></div>

```python
Expand All @@ -246,29 +288,57 @@ def hello(*from_, name="world"):
Args:
from_: greetings are sent from these people
name: [-n] is {who} this will greet
name: [-t/--to] is {who} this will greet
"""
print(f"Hello, {name}!")
print(f"From: {', '.join(from_)}")
```

```console
user@machine:~$ python3 hello-6.py -h
usage: hello-6.py [-h] [-n WHO] [from ...]
user@machine:~$ python3 hello-7.py -h
usage: hello-7.py [-h] [-t WHO] [from ...]

this will say hello to someone

positional arguments:
from greetings are sent from these people (type: str)
from greetings are sent from these people (type: str)

options:
-h, --help show this help message and exit
-n, --name WHO is who this will greet (type: str, default: world)
-h, --help show this help message and exit
-t, --to WHO is who this will greet (type: str, default: world)
```

Compare the last line with how it was before:

```console
Before: --name NAME is who this will greet (type: str, default: world)
After: -n, --name WHO is who this will greet (type: str, default: world)
Before: -t, --to TO is who this will greet (type: str, default: world)
After: -t, --to WHO is who this will greet (type: str, default: world)
```

## Summary

`arguably` looks at all decorated functions and maps their arguments from Python to the CLI.

```python
@arguably.command
def some_function(required, not_required=2, *others: int, option: float = 3.14):
...
```

```console
user@machine:~$ ./intro.py -h
usage: intro.py [-h] [-x OPTION] required [not-required] [others ...]
...
```

| This Python ... | ... becomes this on the CLI. |
|------------------------------------------------|------------------------------------------------|
| positional args, no default `required` | positional CLI args, required `required` |
| positional args, with default `not_required=2` | positional CLI args, optional `[not-required]` |
| positional args, variadic `*others` | any extra positional CLI args `[others ...]` |
| keyword-only arguments `option` | command-line options `[-x OPTION]` |

Docstrings are used for command and argument help messages. They can also:

* Change the short `-n` and long name `--name` of an `--option` by prefixing its description with `[-n/--name]`
* Change the metavar of an argument to `SOMETHING` wrapping a word in curly braces `{something}`
12 changes: 6 additions & 6 deletions docs/why.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
option: [-x] keyword-only args are options, short name is in brackets
"""
print(f"{required=}, {not_required=}, {others=}, {option=}")

Expand All @@ -48,13 +48,13 @@ usage: intro.py [-h] [-x OPTION] required [not-required] [others ...]
this function is on the command line!

positional arguments:
required a required parameter (type: str)
not-required this one isn't required, since it has a default (type: int, default: 2)
required a required argument (type: str)
not-required this one isn't required, since it has a default value (type: int, default: 2)
others all the other positional arguments go here (type: int)

options:
-h, --help show this help message and exit
-x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
-x, --option OPTION keyword-only args are options, short name is in brackets (type: float, default: 3.14)
```

```pycon
Expand Down
35 changes: 20 additions & 15 deletions etc/pypi/PYPI_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</p>
<hr>

`arguably` turns functions into command line interfaces (CLIs). `arguably` has a tiny API and is extremely easy to
integrate. You can also use it directly through `python3 -m arguably your_script.py`, more on that
`arguably` turns functions and docstrings into command line interfaces (CLIs). `arguably` has a tiny API and is
extremely easy to integrate. You can also use it directly through `python3 -m arguably your_script.py`, more on that
[here](#no-integration-required).

To use `arguably` in a script, decorate any functions that should appear on the command line with `@arguably.command`,
Expand All @@ -38,11 +38,12 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
option: [-x] keyword-only args are options, short name is in brackets
"""
print(f"{required=}, {not_required=}, {others=}, {option=}")

if __name__ == "__main__":
arguably.run()
Expand All @@ -57,23 +58,27 @@ usage: intro.py [-h] [-x OPTION] required [not-required] [others ...]
this function is on the command line!

positional arguments:
required a required parameter (type: str)
not-required this one isn't required, since it has a default (type: int, default: 2)
required a required argument (type: str)
not-required this one isn't required, since it has a default value (type: int, default: 2)
others all the other positional arguments go here (type: int)

options:
-h, --help show this help message and exit
-x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
-x, --option OPTION keyword-only args are options, short name is in brackets (type: float, default: 3.14)
```

`arguably` looks at any decorated functions and maps their arguments from Python to the CLI:
Arguments to the CLI look just like calling the Python function.

| This Python ... | ... becomes this on the CLI. |
|------------------------------------------------|------------------------------------------------|
| positional args, no default `required` | positional CLI args, required `required` |
| positional args, with default `not_required=2` | positional CLI args, optional `[not-required]` |
| positional args, variadic `*others` | any extra positional CLI args `[others ...]` |
| keyword-only arguments `option` | command-line options `[-x OPTION]` |
```pycon
>>> from intro import some_function
>>> some_function("asdf", 0, 7, 8, 9, option=2.71)
required='asdf', not_required=0, others=(7, 8, 9), option=2.71
```

```console
user@machine:~$ ./intro.py asdf 0 7 8 9 --option 2.71
required='asdf', not_required=0, others=(7, 8, 9), option=2.71
```

`arguably` uses your docstrings to automatically generate help messages. It supports all major formats for docstrings:
reStructuredText, Google, Numpydoc, and Epydoc.
Expand Down
2 changes: 1 addition & 1 deletion etc/scripts/hello-6.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def hello(*from_, name="world"):
Args:
from_: greetings are sent from these people
name: [-n] is {who} this will greet
name: [-t/--to] is who this will greet
"""
print(f"Hello, {name}!")
print(f"From: {', '.join(from_)}")
Expand Down
16 changes: 16 additions & 0 deletions etc/scripts/hello-7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import arguably

@arguably.command
def hello(*from_, name="world"):
"""
this will say hello to someone
Args:
from_: greetings are sent from these people
name: [-t/--to] is {who} this will greet
"""
print(f"Hello, {name}!")
print(f"From: {', '.join(from_)}")

if __name__ == "__main__":
arguably.run()
6 changes: 3 additions & 3 deletions etc/scripts/intro.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ def some_function(required, not_required=2, *others: int, option: float = 3.14):
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
option: [-x] keyword-only args are options, short name is in brackets
"""
print(f"{required=}, {not_required=}, {others=}, {option=}")

Expand Down

0 comments on commit 3f874a4

Please sign in to comment.