Skip to content

Commit

Permalink
feat: list argument callback (#129)
Browse files Browse the repository at this point in the history
* feat: list argument callback

* fix: unused import

* fix: windows line ends
  • Loading branch information
maxb2 authored Nov 17, 2023
1 parent 47a3d2f commit aabeee2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
55 changes: 55 additions & 0 deletions docs/known_issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Known Issues

This is a collection of known issues and workarounds to address them.

## Argument list in config

> Related GitHub issues: [typer-config#117](https://github.com/maxb2/typer-config/issues/117), [typer-config#124](https://github.com/maxb2/typer-config/issues/124).
Providing values for a list argument in a config file doesn't work out of the box.
You must use a custom callback for list arguments to extract the values from the config.
Thanks to [@jlwhelan28](https://github.com/jlwhelan28) for the inital solution to this problem.
Below is a working example of how to deal with an argument list:

```{.python title="arg_list.py" test="true"}
from typing import List
import typer
from typer_config import use_yaml_config
from typer_config.callbacks import argument_list_callback
app = typer.Typer()
@app.command()
@use_yaml_config()
def main(
arg1: str,
arg2: List[str] = typer.Argument(default=None, callback=argument_list_callback),
opt1: str = typer.Option(...),
opt2: str = typer.Option("hello"),
):
typer.echo(f"{opt1} {opt2} {arg1}")
typer.echo(f"{arg2}")
if __name__ == "__main__":
app()
```

```yaml title="config.yml"
# config.yml
opt1: "apple"
opt2: "pear"
arg1: "lemon"
arg2: ["oak", "aspen", "maple"]
```
```{.bash title="Terminal"}
$ python arg_list.py --config config.yml
apple pear lemon
['oak', 'aspen', 'maple']

$ python arg_list.py strawberry bear wolf snake tiger --config config.yml
apple pear strawberry
['bear', 'wolf', 'snake', 'tiger']
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ nav:
- 'examples/schema.md'
- 'examples/explicit_config.md'
- 'examples/save_config.md'
- Known Issues: known_issues.md
- API Reference: api.md
- Changelog: changelog.md

Expand Down
7 changes: 6 additions & 1 deletion tests/doc_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,12 @@ def exec_bash_fence(fence: Fence, **kwargs):

for command in commands:
result = run(command["input"], shell=True, check=True, capture_output=True)
assert result.stdout.decode().strip() == command["output"].strip()
assert (
result.stdout.decode()
.strip()
.replace("\r", "") # NOTE: fixing windows line ends
== command["output"].strip()
)


register_executor("bash", exec_bash_fence)
Expand Down
6 changes: 5 additions & 1 deletion tests/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from .doc_examples import check_typer_md_file


@pytest.mark.parametrize("fpath", Path("docs/examples").glob("*.md"), ids=str)
@pytest.mark.parametrize(
"fpath",
[*list(Path("docs/examples").glob("*.md")), Path("docs/known_issues.md")],
ids=str,
)
def test_doc_examples(fpath: Path):
"""Test doc file.
Expand Down
34 changes: 33 additions & 1 deletion typer_config/callbacks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
"""Typer Configuration Parameter Callbacks."""

from __future__ import annotations

from typing import List, Optional

from typer import BadParameter, CallbackParam, Context

from .__typing import ConfigLoader, ConfigParameterCallback, TyperParameterValue
# NOTE: I'm not sure why, but these types must be imported at runtime
# for the tests to pass...
from .__typing import ( # noqa: TCH001
ConfigLoader,
ConfigParameterCallback,
TyperParameterValue,
)
from .loaders import (
dotenv_loader,
json_loader,
Expand Down Expand Up @@ -126,3 +136,25 @@ def _callback(
Returns:
TyperParameterValue: must return back the given parameter
"""


def argument_list_callback(
ctx: Context, param: CallbackParam, param_value: Optional[List[str]]
) -> List[str]:
"""Argument list callback.
Note:
This is a shim to fix list arguments in a config.
See [maxb2/typer-config#124](https://github.com/maxb2/typer-config/issues/124).
Args:
ctx (typer.Context): typer context
param (typer.CallbackParam): typer parameter
param_value (Optional[List[str]]): typer parameter value
Returns:
List[str]: argument list
"""
ctx.default_map = ctx.default_map or {}
default = ctx.default_map.get(param.name, []) if param.name else []
return param_value if param_value else default

0 comments on commit aabeee2

Please sign in to comment.