Known Issues
This is a collection of known issues and workarounds to address them.
Argument list in config
Related GitHub issues: typer-config#117, typer-config#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 for the inital solution to this problem. Below is a working example of how to deal with an argument list:
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()
# config.yml
opt1: "apple"
opt2: "pear"
arg1: "lemon"
arg2: ["oak", "aspen", "maple"]
$ 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']
Custom Types with Annotated
and from __future__ import annotations
Related Github issue: typer-config#295. Thanks to @ElliottKasoar for catching this!
When custom types are combined with annotations, a RuntimeError
is thrown.
This is because from __future__ import annotations
converts all annotations to strings at runtime, but the click
parser needs the class at runtime.
The eval_str
option was added to inspect.signature()
in python 3.10 to fix this, however there is no solution for 3.9.
from __future__ import annotations
from typing_extensions import Annotated
import typer
from typer_config import use_yaml_config
class CustomClass:
def __init__(self, value: str):
self.value = value
def __str__(self):
return f"<CustomClass: value={self.value}>"
def parse_custom_class(value: str):
return CustomClass(value * 2)
# NOTE: only works for python 3.10+
FooType = Annotated[CustomClass, typer.Argument(parser=parse_custom_class)]
app = typer.Typer()
@app.command()
@use_yaml_config()
def main(foo: FooType = 1):
print(foo)
if __name__ == "__main__":
app()
# fails for python 3.9 or below
$ python3.9 annotated.py foo
RuntimeError: ...
# works for python 3.10 or above
$ python3.10 annotated.py foo
<CustomClass: value=foofoo>
Alternatively, you can use the non-annotated way to define typer arguments/options which will work for all python versions:
from __future__ import annotations
from typing_extensions import Annotated
import typer
from typer_config import use_yaml_config
class CustomClass:
def __init__(self, value: str):
self.value = value
def __str__(self):
return f"<CustomClass: value={self.value}>"
def parse_custom_class(value: str):
return CustomClass(value * 2)
app = typer.Typer()
# NOTE: works for all versions of python
@app.command()
@use_yaml_config()
def main(foo: CustomClass = typer.Argument(parser=parse_custom_class)):
print(foo)
if __name__ == "__main__":
app()
$ python non-annotated.py foo
<CustomClass: value=foofoo>