Replies: 17 comments
-
|
@allinhtml One way to accomplish this is to have a module level object, a dataclass instance or a dict etc, to hold the state, and a callback to set it. from typing import Optional, List
import typer
import os
app = typer.Typer(add_completion=False)
state = {}
@app.callback()
def _main(
debug: bool = typer.Option(False, "--debug", help="If set print debug messages"),
output_dir: str = typer.Option(os.getcwd(), help="The output directory"),
):
state["debug"] = debug
state["output_dir"] = output_dir
@app.command()
def start(
flows: Optional[List[str]] = typer.Option(None, "--flow", "-f"),
):
typer.echo(f"Debug mode: {state['debug']}")
typer.echo(f"Output Dir: {state['output_dir']}")
typer.echo(f"start flows: {flows}")
@app.command()
def stop():
typer.echo(f"Debug mode: {state['debug']}")
typer.echo(f"Output Dir: {state['output_dir']}")
typer.echo("STOP!")
@app.command()
def clean():
typer.echo(f"Debug mode: {state['debug']}")
typer.echo(f"Output Dir: {state['output_dir']}")
typer.echo("STOP!")
if __name__ == "__main__":
app()with debug: ❯ python issue.py --debug --output-dir GitHub start
Debug mode: True
Output Dir: GitHub
start flows: ()without: ❯ python issue.py --output-dir GitHub clean
Debug mode: False
Output Dir: GitHub
STOP!:) |
Beta Was this translation helpful? Give feedback.
-
|
Is there a way to create common CLI options such, that they are configurable on the sub command level instead of on the command level? e.g. So that the command could be given as follows: python issue.py clean --output-dir GitHub |
Beta Was this translation helpful? Give feedback.
-
|
I need this feature, too. I posted some code related to working around this, here. The workarounds are "OK" but not great. |
Beta Was this translation helpful? Give feedback.
-
|
You can use the Context object no? import typer
app = typer.Typer()
@app.callback()
def main_app(
ctx: typer.Context,
verbose: Optional[bool] = typer.Option(None, help="Enable verbose mode.")
):
obj = ctx.ensure_object(dict)
obj["verbose"] = verbose
@app.command()
def test(ctx: typer.Context):
obj = ctx.ensure_object(dict)
print(obj)see https://typer.tiangolo.com/tutorial/commands/context/ |
Beta Was this translation helpful? Give feedback.
-
|
Will have to check that out, hopefully this weekend. |
Beta Was this translation helpful? Give feedback.
-
|
Not a perfect solution, but what I've been doing is defining the |
Beta Was this translation helpful? Give feedback.
-
Actually, this is a good solution. Listing the input arguments in a function's definition (signature) makes code that is easy to read and understand. Defining typer |
Beta Was this translation helpful? Give feedback.
-
|
I'm using something similar to the previous example to share options. Here's a contrived example: Note that the only difference between the I'm trying to figure out some way I can use a single Anyone got any ideas how I might implement this? |
Beta Was this translation helpful? Give feedback.
-
|
I tried this: Sadly, this throws a couple of flake8 syntax errors:
|
Beta Was this translation helpful? Give feedback.
-
Following works for me : ❯ cat test.pyfrom datetime import datetime
from types import SimpleNamespace
from typing import Annotated, Optional
import typer
app = typer.Typer()
def make_date_option(help="Enter a date"):
return Annotated[
Optional[datetime],
typer.Option(
formats=["%Y-%m-%d"],
help=help,
),
]
@app.command()
def main(
start_date: make_date_option(help='Start') = None,
end_date: make_date_option(help='End') = None,
):
print(f"{start_date} - {end_date}")and ❯ typer test.py run --start-date '2010-01-01' --end-date '2011-02-02'
2010-01-01 00:00:00 - 2011-02-02 00:00:00I think you have a comma left-over in the end of the |
Beta Was this translation helpful? Give feedback.
-
Good spot, but I think that's a copy/paste error and not the source of the F722 error. Re-visiting, I've found that this code runs just fine (with the extra comma removed), but the Syntastic flake8 check still throws the error. Using the power of google, I found this solution: https://stackoverflow.com/a/73235243 Adding SimpleNamesapce to the mix, I ended up with this final code: In a real project, |
Beta Was this translation helpful? Give feedback.
-
I have many shared options, as per your |
Beta Was this translation helpful? Give feedback.
-
Only you can decide what's "worth the effort" |
Beta Was this translation helpful? Give feedback.
-
I don't understand why this only forks with |
Beta Was this translation helpful? Give feedback.
-
I guess because it needs to be a function, and cannot be a variable/constant. |
Beta Was this translation helpful? Give feedback.
-
|
Ended up here trying to answer a similar question. My approach might be of interest to others:
Code is a bit rough as it's hacked from other stuff. from click.core import Context, Parameter
from typer.core import TyperCommand, TyperOption
from rich.logging import RichHandler
import logging
from typing import override
from typer import Typer, Argument, Option
from typer.models import ArgumentInfo, OptionInfo
from functools import partial
from typing import Annotated, Callable
logger = logging.getLogger(__name__)
def as_callback(func: Callable):
def wrapper(
ctx: Context,
param: TyperOption,
*args,
**kwargs,
):
func(*args, **kwargs)
return wrapper
def verbose(
value: bool,
) -> None:
"""
Increase otuput level.
"""
handler = RichHandler(
show_path=False,
show_level=False,
markup=True,
)
log_format = "%(message)s"
date_format = "[%X]"
if value:
logging.basicConfig(
datefmt=date_format,
format=log_format,
level=logging.DEBUG,
handlers=[handler],
)
else:
logging.basicConfig(
datefmt=date_format,
format=log_format,
level=logging.INFO,
handlers=[handler],
)
class Command(TyperCommand):
"""
Command class to add a verbose option to subcommands.
"""
@override
def get_params(self, ctx: Context) -> list[Parameter]:
params = super().get_params(ctx)
verbose_opt = TyperOption(
param_decls=["--verbose", "-v"],
type=bool,
is_flag=True,
show_default=False,
callback=as_callback(verbose),
help=verbose.__doc__,
is_eager=True,
)
params.append(verbose_opt)
return params
@override
def invoke(self, ctx: Context):
if "verbose" in ctx.params:
del ctx.params["verbose"]
return super().invoke(ctx)
app = Typer()
app.command = partial(app.command, cls=Command)
def name_arg() -> ArgumentInfo:
return Argument(
show_default=False,
help="Name to say hi to.",
)
def count_arg():
return Option(
default=1,
show_default=True,
help="How many times to say hi.",
)
def title_arg():
return Argument(
show_default=True,
help='title for name.'
)
@app.command()
def foo(
name: Annotated[str, name_arg()]
):
logger.debug("in foo command")
logger.info(f"got name {name}")
@app.command()
def bar(
name: Annotated[str, name_arg()],
title: Annotated[str, title_arg()] = "sir",
count: int = count_arg(),
):
logger.debug("in bar command")
for _ in range(count):
logger.info(f"got name {title} {name}")
# verbose is meaningless - remove the arg.
@app.command(cls=None)
def version() -> None:
"""
Print version and exit.
"""
from rich.console import Console
console = Console(
log_path=False,
)
package = __package__
if package is None:
console.print("dev package")
else:
from importlib.metadata import version as meta_version
console.print(f'{package} version {meta_version(package)}')
from typer import Exit
raise Exit
if __name__ == "__main__":
app() |
Beta Was this translation helpful? Give feedback.
-
|
Let's treat this as a feature request and track in #153 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
First Check
Commit to Help
Example Code
Description
How can we easily add common options into multiple commands like debug or output_directory?
Related question - #153 - But this is not working as expected. Help section don't show message properly as commented here.
Operating System
Linux, Windows, macOS
Operating System Details
No response
Typer Version
ALL
Python Version
ALL
Additional Context
No response
Beta Was this translation helpful? Give feedback.
All reactions