diff --git a/docs/tutorial/typer-command.md b/docs/tutorial/typer-command.md index 803b44d0f5..802237e9c7 100644 --- a/docs/tutorial/typer-command.md +++ b/docs/tutorial/typer-command.md @@ -215,6 +215,32 @@ Hello Camila +### Run a package or module with specified function + +You can also specify a function directly using the colon syntax: + +
+ +```console +$ typer my_package.main:my_function run --name Camila + +Hello Camila +``` + +
+ +or with the file path + +
+ +```console +$ typer main.py:my_function run --name Camila + +Hello Camila +``` + +
+ ## Options You can specify one of the following **CLI options**: @@ -222,6 +248,8 @@ You can specify one of the following **CLI options**: * `--app`: the name of the variable with a `Typer()` object to run as the main app. * `--func`: the name of the variable with a function that would be used with `typer.run()`. +Alternatively, you can specify the function directly using the syntax `module:function`. + ### Defaults When your run a script with the `typer` command it will use the app from the following priority: diff --git a/tests/test_cli/test_multi_func.py b/tests/test_cli/test_multi_func.py index 8b4b43a94d..62f6c8b362 100644 --- a/tests/test_cli/test_multi_func.py +++ b/tests/test_cli/test_multi_func.py @@ -85,6 +85,26 @@ def test_script_func_not_function(): assert "Not a function:" in result.stderr +def test_script_colon_not_function(): + result = subprocess.run( + [ + sys.executable, + "-m", + "coverage", + "run", + "-m", + "typer", + "tests/assets/cli/multi_func.py:message", + "run", + "--name", + "Camila", + ], + capture_output=True, + encoding="utf-8", + ) + assert "Not a function:" in result.stderr + + def test_script_func(): result = subprocess.run( [ @@ -104,3 +124,41 @@ def test_script_func(): ) assert "Hello" not in result.stdout assert "Stuff" in result.stdout + + +def test_script_module_colon_func(): + result = subprocess.run( + [ + sys.executable, + "-m", + "coverage", + "run", + "-m", + "typer", + "tests.assets.cli.multi_func:say_stuff", + "run", + ], + capture_output=True, + encoding="utf-8", + ) + assert "Hello" not in result.stdout + assert "Stuff" in result.stdout + + +def test_script_file_colon_func(): + result = subprocess.run( + [ + sys.executable, + "-m", + "coverage", + "run", + "-m", + "typer", + "tests/assets/cli/multi_func.py:say_stuff", + "run", + ], + capture_output=True, + encoding="utf-8", + ) + assert "Hello" not in result.stdout + assert "Stuff" in result.stdout diff --git a/typer/cli.py b/typer/cli.py index 3fe3d3ee7f..82166be0cb 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -43,6 +43,10 @@ def __init__(self) -> None: def maybe_update_state(ctx: click.Context) -> None: path_or_module = ctx.params.get("path_or_module") if path_or_module: + if ":" in path_or_module: + module_part, func_part = path_or_module.rsplit(":", 1) + path_or_module = module_part + ctx.params.update({"func": func_part}) file_path = Path(path_or_module) if file_path.exists() and file_path.is_file(): state.file = file_path