-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
155 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<p align="center"> | ||
<img alt="arguably logo" src="https://raw.githubusercontent.com/treykeown/arguably/main/assets/arguably_black.png"> | ||
</p> | ||
|
||
<p align="center"> | ||
<em> | ||
turns functions into command line interfaces | ||
</em> | ||
</p> | ||
|
||
<p align="center"> | ||
<!-- TODO badges --> | ||
</p> | ||
<hr> | ||
|
||
`arguably` solves this problem: | ||
1. You've written a Python script | ||
2. Now you want to pass in parameters from the command line | ||
3. You don't want to read the docs for your favorite argument parsing library *again* | ||
|
||
By leveraging as many Python idioms as possible, `arguably` keeps its API small and memorable without sacrificing | ||
funcitonality. `arguably` uses functions and their docstrings to automatically set up argparse. Notably, `arguably` | ||
maps your function signature to a command-line interface like this: | ||
|
||
```python | ||
@arguably.command | ||
def some_function(required, not_required="foo", *others, option="bar"): | ||
... | ||
``` | ||
|
||
<p align="center"><b><em>becomes</em></b></p> | ||
|
||
```text | ||
usage: script [--option OPTION] required [not-required] [others ...] | ||
``` | ||
|
||
In short, `arguably` turns your function's **positional parameters** into **positional command-line arguments**, and | ||
your function's **keyword-only arguments** into **command-line options**. From the example above: | ||
|
||
| Name | Type | Becomes | Usage | | ||
|----------------|-------------------------------------|---------------------------------|---------------------| | ||
| `required` | positional, no default value | required positional arg | `required` | | ||
| `not_required` | positional, with default value | optional positional arg | `[not-required]` | | ||
| `others` | positional, variadic (like `*args`) | the rest of the positional args | `[others ...]` | | ||
| `option` | keyword-only argument | an option | `[--option OPTION]` | | ||
|
||
`arguably` also enables you to easily add subcommands - just annotate more than one function with `@arguably.command`. | ||
You can even have nested subcommands (more on that later). | ||
|
||
`arguably` reads type annotations and automatically converts arguments to the declared types. It has smart handling for | ||
`tuple`, `list`, `enum.Enum`, and `enum.Flag`. There are also a few special behaviors you can attach to a parameter | ||
via `Annotated[]` and the `arguably.arg.*` functions. | ||
|
||
`arguably` parses docstrings to generate descriptions for your commands and parameters. If you want to give a parameter | ||
the alias `-X`, prefix its docstring description with `[-X]`. Wrapping a word in `{}` changes the *metavar* that gets | ||
printed (this is what's shown in the usage string after an option name, don't worry if you aren't familiar with this). | ||
For example: | ||
|
||
```python | ||
#!/usr/bin/env python3 | ||
"""docstrings for the file become the description for the script.""" | ||
__version__ = "1.0.0" # You can also set `version_flag=True` to add a version flag, it will read `__version__` | ||
|
||
import arguably | ||
|
||
@arguably.command(alias="h") | ||
def hello(name: str, *, lastname: str | None = None): | ||
""" | ||
says hello to you | ||
:param name: your name | ||
:param lastname: [-l] your {surname} | ||
""" | ||
full_name = name if lastname is None else f"{name} {lastname}" | ||
print(f"Hello, {full_name}!") | ||
|
||
@arguably.command(alias="g") | ||
def goodbye(name: str, *, is_sad: bool = False): | ||
""" | ||
says goodbye to you | ||
:param name: your name | ||
:param is_sad: [-s] whether or not it's sad to see you go | ||
""" | ||
print(f"Goodbye, {name}!") | ||
if is_sad: | ||
print(f"It's sad to see you go!") | ||
|
||
if __name__ == "__main__": | ||
arguably.run(version_flag=True) | ||
``` | ||
|
||
<p align="center"><b><em>becomes</em></b></p> | ||
|
||
```console | ||
user@machine:~$ python3 script.py | ||
usage: test_scripts.docs2 [-h] [--version] command ... | ||
|
||
docstrings for the file become the description for the script. | ||
|
||
positional arguments: | ||
command | ||
hello (h) says hello to you | ||
goodbye (g) says goodbye to you | ||
|
||
options: | ||
-h, --help show this help message and exit | ||
--version show program's version number and exit | ||
|
||
|
||
user@machine:~$ python3 script.py hello --help | ||
usage: test_scripts.docs2 hello [-h] [-l SURNAME] name | ||
|
||
says hello to you | ||
|
||
positional arguments: | ||
name your name | ||
|
||
options: | ||
-h, --help show this help message and exit | ||
-l, --lastname SURNAME your surname (default: None) | ||
``` | ||
|
||
More docs coming soon... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Converts our README.md to a PyPI-compatible version... needed because PyPI does not yet support <picture>, see: | ||
https://github.com/pypi/warehouse/issues/11251 | ||
""" | ||
|
||
|
||
from pathlib import Path | ||
|
||
project_path = Path(__file__).resolve().parent.parent | ||
readme_src = project_path / "README.md" | ||
readme_dst = project_path / "assets" / "PYPI_README.md" | ||
|
||
# A bit hacky, but works for now. | ||
with readme_src.open("r") as src: | ||
with readme_dst.open("w") as dst: | ||
for line in src.readlines(): | ||
stripped_line = line.lstrip(" ") | ||
if ( | ||
stripped_line.startswith("<picture") | ||
or stripped_line.startswith("</picture") | ||
or stripped_line.startswith("<source") | ||
): | ||
continue | ||
dst.write(line) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ name = "arguably" | |
version = "1.0.0" | ||
description = "turns functions into command line interfaces" | ||
authors = ["treykeown <[email protected]>"] | ||
readme = "README.md" | ||
readme = "assets/PYPI_README.md" | ||
homepage = "https://github.com/treykeown/arguably" | ||
repository = "https://github.com/treykeown/arguably" | ||
|
||
|