typer icon indicating copy to clipboard operation
typer copied to clipboard

get_app_dir documentation suggests functionality regarding config files which is not there (yet?)

Open tibeer opened this issue 2 years ago • 1 comments

First Check

  • [X] I added a very descriptive title to this issue.
  • [X] I used the GitHub search to find a similar issue and didn't find it.
  • [X] I searched the Typer documentation, with the integrated search.
  • [X] I already searched in Google "How to X in Typer" and didn't find any information.
  • [X] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [X] I already checked if it is not related to Typer but to Click.

Commit to Help

  • [X] I commit to help with one of those options 👆

Example Code

from pathlib import Path

import typer
import somesubcommand


app = typer.Typer(help="FooBar CLI")
app.add_typer(somesubcommand.app)


@app.callback(invoke_without_command=True)
def main():
    app_dir = typer.get_app_dir("foobar")
    app_dir_path = Path(app_dir)
    app_dir_path.mkdir(parents=True, exist_ok=True)
    config_path: Path = Path(app_dir) / "config.json"
    if not config_path.is_file():
        typer.echo("Config file doesn't exist yet. Creating new one.")
        config_path.write_text('''{
    "API_ENDPOINT": "http://1.2.3.4:8080",
    "API_USERNAME": "foo",
    "API_PASSWORD": "bar"
}
''')
        typer.echo(f"Config file created at {config_path}")
        typer.echo("Please alter it according to your needs.")
        config_file_str = str(config_path)
        typer.echo("Opening config directory")
        typer.launch(config_file_str, locate=True)


if __name__ == "__main__":
    app()

Description

The documentation suggests to use click's get_app_dir for storing configuration files. This works perfectly well, but you cannot use them easily and therefore readers are mislead. E.g. you want to use config values inside somesubcommand, you still have to read the config file. To do this, you still have to run typer.get_app_dir inside somesubcommand, but you need to hard-code the application name again. Not very nice :)

Wanted Solution

Improved documentation on how to read / use the config file in a nice way.

Wanted Code

from pathlib import Path

import typer
import somesubcommand


app = typer.Typer(help="FooBar CLI")
app.add_typer(somesubcommand.app)


@app.callback(invoke_without_command=True)
def main():
    app_dir = typer.get_app_dir("foobar")
    app_dir_path = Path(app_dir)
    app_dir_path.mkdir(parents=True, exist_ok=True)
    config_path: Path = Path(app_dir) / "config.json"
    if not config_path.is_file():
        typer.echo("Config file doesn't exist yet. Creating new one.")
        config_path.write_text('''{
    "API_ENDPOINT": "http://1.2.3.4:8080",
    "API_USERNAME": "foo",
    "API_PASSWORD": "bar"
}
''')
        typer.echo(f"Config file created at {config_path}")
        typer.echo("Please alter it according to your needs.")
        config_file_str = str(config_path)
        typer.echo("Opening config directory")
        typer.launch(config_file_str, locate=True)

    # maybe something like this?
    typer.load_config(config_file_str)
    # one would access the values then in other files by
    #
    # import typer.CONFIG
    # CONFIG["API_ENDPOINT"]

if __name__ == "__main__":
    app()

Alternatives

State somewhere, that getting the application directory is great, but that it does not help when you want to read the config file.

Operating System

Linux, macOS

Operating System Details

Operating system should not affect this topic.

Typer Version

0.5.0

Python Version

3.10.5

Additional Context

You do great stuff!

tibeer avatar Jul 08 '22 10:07 tibeer

get_app_dir is only supposed to give you a standard location for where files can be stored based on OS. It's not responsible for interacting with any files you store there..

You might want to reconsider how you're handling creating/reading the config file. For example, for a single file, you can move that logic to the global space outside of Typer commands and callbacks. For a CLI package, you could set up config files in your packages __init__.py. That way you can create and reference your settings object (configparser, Pydantic, whatever else you like to use) from a central location.

I don't think Typer forcing a user into any particular format for loading settings is a good idea but you might want to check out Pydantic's BaseSettings

daddycocoaman avatar Jul 17 '22 09:07 daddycocoaman

Thanks for the help @daddycocoaman and thanks for closing the issue @tibeer ! ☕🍰

tiangolo avatar Nov 07 '22 08:11 tiangolo