Open ssbarnea opened 8 months ago
Sorry for the late reply. I've been trying to fix this issue for several days.
Let's start with the easy stuff.
There is not explicit support for searching parent directories, but I designed this library to be easy to extend. You can define your own file loader with that behavior and plug it in to the rest of the typer-config
machinery. Here is an example:
from pathlib import Path
from typing import Any, Dict, Union
import typer
from typing_extensions import Annotated
from typer_config import conf_callback_factory
from typer_config.decorators import use_config
from typer_config.loaders import yaml_loader
def search_path_parents(filename: Union[str, Path]) -> Path:
path = Path(filename).absolute()
for _dir in path.parents:
_path = _dir.joinpath(path.name)
if _path.exists():
return _path
raise FileNotFoundError(
f"Could not find {path.name} in {path.parent} or any of its parents."
)
def parent_loader(param_value: str) -> Dict[str, Any]:
param_value = search_path_parents(param_value)
conf = yaml_loader(param_value)
return conf
### You can define the same loader using the loader_transformer combinator:
#
# from typer_config.loaders import loader_transformer
# parent_loader = loader_transformer(
# yaml_loader,
# param_transformer=search_path_parents,
# )
parent_callback = conf_callback_factory(parent_loader)
app = typer.Typer()
@app.command()
@use_config(parent_callback)
def main(
arg1: str,
opt1: Annotated[str, typer.Option()],
opt2: Annotated[str, typer.Option()] = "hello",
):
typer.echo(f"{opt1} {opt2} {arg1}")
if __name__ == "__main__":
app()
I could add this functionality in the default decorators, e.g. @use_yaml_config(search_parents=True)
. I think this particular behavior would be okay to add, but I don't want the decorators to become a soup of feature flags with every other feature suggestion. Everybody has their own opinion on default behavior.
I did not have a test for the case to ensure you can get to --help
when you have a default value that is a not an existing file (it's surprisingly hard to test all the edge cases of this library 😅), so this bug slipped through. I spent several days poking around the typer/click internals to see how I can fix this. Since click
parses the parameters in a certain order (and I must use is_eager=True
), it's impossible to tell if --help
has been provided while parsing --config
. So, I can't distinguish between the cli
and cli --help
invocations since the default value is passed to --config
in both cases. I see two options:
Emit a warning when the config file isn't found rather than raising a FileNotFoundError
. This will let the execution continue and most likely fail on a missing argument. For example:
/Users/maxb2/Programs/typer-config/typer_config/callbacks.py:61: UserWarning: Could not load config from config.yml.
warn(f"Could not load config from {param_value}.")
Usage: app.py [OPTIONS] ARG1
Try 'app.py --help' for help.
Error: Missing argument 'ARG1'.
I could see this being confusing to the end user if they don't notice the message at the top. Plus, you will no longer get the nice error message that typer
provides when you give invalid input to --config
.
Fork/extend click
to adjust the order in which parameters are parsed so that --help
always comes before --config
. This would be a lot of work and a maintenance nightmare. I don't have enough free time to do that.
I think option 1 is a decent compromise (I can even make the error handling configurable for those that want to raise errors rather than just warn).
Let me know what you think! I'll try to get an alpha release out soon if you would like to test it and give feedback.
My $0.02 would be to emit a warning if there is no configuration file. If we can catch the UserWarning
, then we can either ignore it (and use whatever the defaults are) or create a configuration file based on some template.
Please let me know if you want me to test anything…
Most tools are able to detect existing config file in current directory, its parents or user home directory. Currently I seen no support in typer-config for having a dynamic detection of the config file.
Even worse, when passing the
default_value="mytool.yml"
to it, it will fail if the config file is not present.How can we make it not fail when a config file does not exist and also to document the default config filename?