Skip to content

Commit 9556792

Browse files
committed
Fix readme for PyPI
1 parent 3e2ee1f commit 9556792

File tree

4 files changed

+155
-1
lines changed

4 files changed

+155
-1
lines changed

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,10 @@ repos:
2323
hooks:
2424
- id: mypy
2525
exclude: ^test/
26+
- repo: local
27+
hooks:
28+
- id: pypi_readme
29+
name: PyPI README
30+
entry: python3 assets/make_pypi_readme.py
31+
language: system
32+
files: ^README\.md$

assets/PYPI_README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<p align="center">
2+
<img alt="arguably logo" src="https://raw.githubusercontent.com/treykeown/arguably/main/assets/arguably_black.png">
3+
</p>
4+
5+
<p align="center">
6+
<em>
7+
turns functions into command line interfaces
8+
</em>
9+
</p>
10+
11+
<p align="center">
12+
<!-- TODO badges -->
13+
</p>
14+
<hr>
15+
16+
`arguably` solves this problem:
17+
1. You've written a Python script
18+
2. Now you want to pass in parameters from the command line
19+
3. You don't want to read the docs for your favorite argument parsing library *again*
20+
21+
By leveraging as many Python idioms as possible, `arguably` keeps its API small and memorable without sacrificing
22+
funcitonality. `arguably` uses functions and their docstrings to automatically set up argparse. Notably, `arguably`
23+
maps your function signature to a command-line interface like this:
24+
25+
```python
26+
@arguably.command
27+
def some_function(required, not_required="foo", *others, option="bar"):
28+
...
29+
```
30+
31+
<p align="center"><b><em>becomes</em></b></p>
32+
33+
```text
34+
usage: script [--option OPTION] required [not-required] [others ...]
35+
```
36+
37+
In short, `arguably` turns your function's **positional parameters** into **positional command-line arguments**, and
38+
your function's **keyword-only arguments** into **command-line options**. From the example above:
39+
40+
| Name | Type | Becomes | Usage |
41+
|----------------|-------------------------------------|---------------------------------|---------------------|
42+
| `required` | positional, no default value | required positional arg | `required` |
43+
| `not_required` | positional, with default value | optional positional arg | `[not-required]` |
44+
| `others` | positional, variadic (like `*args`) | the rest of the positional args | `[others ...]` |
45+
| `option` | keyword-only argument | an option | `[--option OPTION]` |
46+
47+
`arguably` also enables you to easily add subcommands - just annotate more than one function with `@arguably.command`.
48+
You can even have nested subcommands (more on that later).
49+
50+
`arguably` reads type annotations and automatically converts arguments to the declared types. It has smart handling for
51+
`tuple`, `list`, `enum.Enum`, and `enum.Flag`. There are also a few special behaviors you can attach to a parameter
52+
via `Annotated[]` and the `arguably.arg.*` functions.
53+
54+
`arguably` parses docstrings to generate descriptions for your commands and parameters. If you want to give a parameter
55+
the alias `-X`, prefix its docstring description with `[-X]`. Wrapping a word in `{}` changes the *metavar* that gets
56+
printed (this is what's shown in the usage string after an option name, don't worry if you aren't familiar with this).
57+
For example:
58+
59+
```python
60+
#!/usr/bin/env python3
61+
"""docstrings for the file become the description for the script."""
62+
__version__ = "1.0.0" # You can also set `version_flag=True` to add a version flag, it will read `__version__`
63+
64+
import arguably
65+
66+
@arguably.command(alias="h")
67+
def hello(name: str, *, lastname: str | None = None):
68+
"""
69+
says hello to you
70+
:param name: your name
71+
:param lastname: [-l] your {surname}
72+
"""
73+
full_name = name if lastname is None else f"{name} {lastname}"
74+
print(f"Hello, {full_name}!")
75+
76+
@arguably.command(alias="g")
77+
def goodbye(name: str, *, is_sad: bool = False):
78+
"""
79+
says goodbye to you
80+
:param name: your name
81+
:param is_sad: [-s] whether or not it's sad to see you go
82+
"""
83+
print(f"Goodbye, {name}!")
84+
if is_sad:
85+
print(f"It's sad to see you go!")
86+
87+
if __name__ == "__main__":
88+
arguably.run(version_flag=True)
89+
```
90+
91+
<p align="center"><b><em>becomes</em></b></p>
92+
93+
```console
94+
user@machine:~$ python3 script.py
95+
usage: test_scripts.docs2 [-h] [--version] command ...
96+
97+
docstrings for the file become the description for the script.
98+
99+
positional arguments:
100+
command
101+
hello (h) says hello to you
102+
goodbye (g) says goodbye to you
103+
104+
options:
105+
-h, --help show this help message and exit
106+
--version show program's version number and exit
107+
108+
109+
user@machine:~$ python3 script.py hello --help
110+
usage: test_scripts.docs2 hello [-h] [-l SURNAME] name
111+
112+
says hello to you
113+
114+
positional arguments:
115+
name your name
116+
117+
options:
118+
-h, --help show this help message and exit
119+
-l, --lastname SURNAME your surname (default: None)
120+
```
121+
122+
More docs coming soon...

assets/make_pypi_readme.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Converts our README.md to a PyPI-compatible version... needed because PyPI does not yet support <picture>, see:
4+
https://github.com/pypi/warehouse/issues/11251
5+
"""
6+
7+
8+
from pathlib import Path
9+
10+
project_path = Path(__file__).resolve().parent.parent
11+
readme_src = project_path / "README.md"
12+
readme_dst = project_path / "assets" / "PYPI_README.md"
13+
14+
# A bit hacky, but works for now.
15+
with readme_src.open("r") as src:
16+
with readme_dst.open("w") as dst:
17+
for line in src.readlines():
18+
stripped_line = line.lstrip(" ")
19+
if (
20+
stripped_line.startswith("<picture")
21+
or stripped_line.startswith("</picture")
22+
or stripped_line.startswith("<source")
23+
):
24+
continue
25+
dst.write(line)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "arguably"
33
version = "1.0.0"
44
description = "turns functions into command line interfaces"
55
authors = ["treykeown <2755914+treykeown@users.noreply.github.com>"]
6-
readme = "README.md"
6+
readme = "assets/PYPI_README.md"
77
homepage = "https://github.com/treykeown/arguably"
88
repository = "https://github.com/treykeown/arguably"
99

0 commit comments

Comments
 (0)