Skip to content

Commit 49ccdd0

Browse files
authored
Allow setting default args for commands / callbacks (#157)
Allows overriding the default keyword arguments passed to commands. E.g., consider: ```python @click.command() @click.option("-f", "--flag") @click.option("-t", "--test", default="not set") def example(flag, test, default_kwd=None): ``` ```toml [tool.spin.kwargs] ".spin/cmds.py:example" = {"test" = "default override", "default_kwd" = 3} ``` This will override the `--test` flag's default value to `default override`. It will also override the `default_kwd` keyword, which is not associated with a flag.
2 parents bdaa222 + c6d5f4b commit 49ccdd0

File tree

8 files changed

+97
-62
lines changed

8 files changed

+97
-62
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,29 @@ def example():
147147
print(config["tool.spin"])
148148
```
149149

150+
### Argument overrides
151+
152+
Default arguments can be overridden for any command.
153+
The custom command above, e.g., has the following signature:
154+
155+
```python
156+
@click.command()
157+
@click.option("-f", "--flag")
158+
@click.option("-t", "--test", default="not set")
159+
def example(flag, test, default_kwd=None):
160+
"""🧪 Example custom command.
161+
...
162+
"""
163+
```
164+
165+
Use the `[tool.spin.kwargs]` section to override default values for
166+
click options or function keywords:
167+
168+
```toml
169+
[tool.spin.kwargs]
170+
".spin/cmds.py:example" = {"test" = "default override", "default_kwd" = 3}
171+
```
172+
150173
### Advanced: adding arguments to built-in commands
151174

152175
Instead of rewriting a command from scratch, a project may want to add a flag to a built-in `spin` command, or perhaps do some pre- or post-processing.

example_pkg/.spin/cmds.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
@click.command()
99
@click.option("-f", "--flag")
10-
def example(flag):
10+
@click.option("-t", "--test", default="not set")
11+
def example(flag, test, default_kwd=None):
1112
"""🧪 Example custom command.
1213
1314
Accepts arbitrary flags, and shows how to access `pyproject.toml`
@@ -20,6 +21,11 @@ def example(flag):
2021
click.secho("Flag provided with --flag is: ", fg="yellow", nl=False)
2122
print(flag or None)
2223

24+
click.secho("Flag provided with --test is: ", fg="yellow", nl=False)
25+
print(test or None)
26+
27+
click.secho(f"Default kwd is: {default_kwd}")
28+
2329
click.secho("\nDefined commands:", fg="yellow")
2430
for section in commands:
2531
print(f" {section}: ", end="")

example_pkg/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ package = 'example_pkg'
4848
"Install" = [
4949
"spin.cmds.pip.install"
5050
]
51+
52+
[tool.spin.kwargs]
53+
".spin/cmds.py:example" = {"test" = "default override", "default_kwd" = 3}

spin/__main__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def group(ctx):
9494
"spin.python": _cmds.meson.python,
9595
"spin.shell": _cmds.meson.shell,
9696
}
97+
cmd_default_kwargs = toml_config.get("tool.spin.kwargs", {})
9798

9899
for section, cmds in config_cmds.items():
99100
for cmd in cmds:
@@ -133,6 +134,18 @@ def group(ctx):
133134
print(f"!! Could not load command `{func}` from file `{path}`.\n")
134135
continue
135136

137+
default_kwargs = cmd_default_kwargs.get(cmd)
138+
import functools
139+
140+
if default_kwargs:
141+
callback = cmd_func.callback
142+
cmd_func.callback = functools.partial(callback, **default_kwargs)
143+
144+
# Also override option defaults
145+
for option in cmd_func.params:
146+
if option.name in default_kwargs:
147+
option.default = default_kwargs[option.name]
148+
136149
commands[cmd] = cmd_func
137150

138151
group.add_command(commands[cmd], section=section)

spin/tests/test_build_cmds.py

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,10 @@
44
import tempfile
55
from pathlib import Path
66

7-
import pytest
7+
from testutil import skip_on_windows, skip_unless_linux, spin, stdout
88

9-
import spin as libspin
109
from spin.cmds.util import run
1110

12-
skip_on_windows = pytest.mark.skipif(
13-
sys.platform.startswith("win"), reason="Skipped; platform is Windows"
14-
)
15-
16-
on_linux = pytest.mark.skipif(
17-
not sys.platform.startswith("linux"), reason="Skipped; platform not Linux"
18-
)
19-
20-
21-
def spin(*args, **user_kwargs):
22-
default_kwargs = {
23-
"stdout": subprocess.PIPE,
24-
"stderr": subprocess.PIPE,
25-
"sys_exit": True,
26-
}
27-
return run(["spin"] + list(args), **{**default_kwargs, **user_kwargs})
28-
29-
30-
def stdout(p):
31-
return p.stdout.decode("utf-8").strip()
32-
33-
34-
def stderr(p):
35-
return p.stderr.decode("utf-8").strip()
36-
37-
38-
def test_get_version():
39-
p = spin("--version")
40-
assert stdout(p) == f"spin {libspin.__version__}"
41-
4211

4312
def test_basic_build():
4413
"""Does the package build?"""
@@ -145,7 +114,7 @@ def test_spin_install():
145114
run(["pip", "uninstall", "-y", "--quiet", "example_pkg"])
146115

147116

148-
@on_linux
117+
@skip_unless_linux
149118
def test_gdb():
150119
p = spin(
151120
"gdb",

spin/tests/test_cli.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from testutil import spin, stdout
2+
3+
import spin as libspin
4+
5+
6+
def test_get_version():
7+
p = spin("--version")
8+
assert stdout(p) == f"spin {libspin.__version__}"
9+
10+
11+
def test_arg_override():
12+
p = spin("example")
13+
assert "--test is: default override" in stdout(p)
14+
assert "Default kwd is: 3" in stdout(p)
15+
16+
p = spin("example", "-t", 6)
17+
assert "--test is: 6" in stdout(p)

spin/tests/testutil.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import subprocess
2+
import sys
3+
4+
import pytest
5+
6+
from spin.cmds.util import run
7+
8+
skip_on_windows = pytest.mark.skipif(
9+
sys.platform.startswith("win"), reason="Skipped; platform is Windows"
10+
)
11+
12+
skip_unless_linux = pytest.mark.skipif(
13+
not sys.platform.startswith("linux"), reason="Skipped; platform not Linux"
14+
)
15+
16+
17+
def spin(*args, **user_kwargs):
18+
args = (str(el) for el in args)
19+
default_kwargs = {
20+
"stdout": subprocess.PIPE,
21+
"stderr": subprocess.PIPE,
22+
"sys_exit": True,
23+
}
24+
return run(["spin"] + list(args), **{**default_kwargs, **user_kwargs})
25+
26+
27+
def stdout(p):
28+
return p.stdout.decode("utf-8").strip()
29+
30+
31+
def stderr(p):
32+
return p.stderr.decode("utf-8").strip()

spin/tests/util.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)