fastapi / typer

Typer, build great CLIs. Easy to code. Based on Python type hints.
https://typer.tiangolo.com/
MIT License
15.44k stars 656 forks source link

[FEATURE] Split commands in the same group into different files. #187

Open tomgrin10 opened 3 years ago

tomgrin10 commented 3 years ago

Is your feature request related to a problem

Currently there's no way to split commands in the same group / subcommand into different files easily.

The solution you would like

I'm thinking of making it so when app.add_typer() gets no name argument it will add all commands to registered_commands, without creating a subcommand.

Example:

create.py

import typer

app = typer.Typer()

@app.command()
def create(item: str):
    typer.echo(f"Creating item: {item}")

delete.py

import typer

app = typer.Typer()

@app.command()
def delete(item: str):
    typer.echo(f"Deleting item: {item}")

main.py

import typer

import create
import delete

app = typer.Typer()
app.add_typer(create.app)
app.add_typer(delete.app)

Output:

$ python main.py --help

Usage: main.py items [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  create
  delete

Additional context

Currently giving no name to add_typer results in not adding the commands at all, even not in a subcommand.

lllama commented 3 years ago

I was looking to do something similar and had an idea (currently untested).

If you put the various commands into their own files as raw functions, then you can import them into your main.py. As @app.command() is just a decorator, you can can just wrap your imported functions using it.

e.g.

from .create import create
from .delete import delete

app = typer.Typer()

app.command()(create)
app.command()(delete)
tomgrin10 commented 3 years ago

@lllama I currently just do this

import typer

import create
import delete

app = typer.Typer()
app.registered_commands += create.app.registered_commands + delete.app.registered_commands

I don't currently have the original code, so this may not be exactly what I wrote, but you get the idea.

teauxfu commented 3 years ago

@lllama I currently just do this

import typer

import create
import delete

app = typer.Typer()
app.registered_commands += create.app.registered_commands + delete.app.registered_commands

I don't currently have the original code, so this may not be exactly what I wrote, but you get the idea.

Thanks for this!

I am working on a little Typer app and I wanted to write my commands modularly with the style shown here, since the docs mention that Typer can recognize when a Typer app has only one command https://typer.tiangolo.com/tutorial/commands/one-or-multiple/ I was hoping to use add_typer to insert each of these But loading a 'sub-Typer' with only one command defined just adds a clunky extra layer

using a pattern like in your snippet lets me define my commands in separate files, compose them into the main application, and avoid having redundant nesting in the command hierarchy 👍🏻

I think it would be a great feature if Typer could emulate this directly when it observes that a sub-Typer has only one command. It seems strange that add_typer will silently accept being called without a 'name' argument at all

possibly related: https://github.com/tiangolo/typer/issues/119

Alirezaaraby commented 1 year ago

178

NikosAlexandris commented 9 months ago

@lllama I currently just do this

import typer

import create
import delete

app = typer.Typer()
app.registered_commands += create.app.registered_commands + delete.app.registered_commands

I don't currently have the original code, so this may not be exactly what I wrote, but you get the idea.

This is same same but less typing in the "main.py" cli module than the solution given by @lllama ?