Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions tests/test_single_command_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import typer
from typer.testing import CliRunner

runner: CliRunner = CliRunner()


def test_result_callback_single_command() -> None:
# A list to capture the result from the callback
captured_results: list[str] = []

def my_callback(value: str) -> None:
captured_results.append(value)

# Create app with a result_callback
app: typer.Typer = typer.Typer(result_callback=my_callback)

@app.command()
def main() -> str:
return "single_command_result"

# Invoke the app (using the single command fast-path)
result = runner.invoke(app, [])

# Verify the command ran successfully
assert result.exit_code == 0

# CRITICAL: Verify the callback was actually executed
assert "single_command_result" in captured_results, (
"Result callback was not triggered for single command!"
)


def test_result_callback_single_command_placeholder() -> None:
from typer.models import DefaultPlaceholder

# A list to capture the result from the callback
captured_results: list[str] = []

def my_callback(value: str) -> None:
captured_results.append(value)

# Create app and manually inject a DefaultPlaceholder for the result_callback
app = typer.Typer()
app.info.result_callback = DefaultPlaceholder(my_callback)

@app.command()
def main() -> str:
return "placeholder_result"

# Invoke the app
result = runner.invoke(app, [])

# Verify the command ran successfully
assert result.exit_code == 0

# Verify the callback was actually executed via the placeholder path
assert "placeholder_result" in captured_results, (
"Result callback was not triggered via placeholder!"
)
19 changes: 19 additions & 0 deletions typer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,25 @@ def get_command(typer_instance: Typer) -> click.Command:
if typer_instance._add_completion:
click_command.params.append(click_install_param)
click_command.params.append(click_show_param)

click_callback = click_command.callback
use_result_callback = None
if typer_instance.info.result_callback:
if isinstance(typer_instance.info.result_callback, DefaultPlaceholder):
use_result_callback = typer_instance.info.result_callback.value
else:
use_result_callback = typer_instance.info.result_callback

if click_callback and use_result_callback:

def callback_wrapper(*args: Any, **kwargs: Any) -> Any:
result = click_callback(*args, **kwargs)
ctx = click.get_current_context()
return ctx.invoke(use_result_callback, result)

update_wrapper(callback_wrapper, click_callback)
click_command.callback = callback_wrapper

return click_command
raise RuntimeError(
"Could not get a command for this Typer instance"
Expand Down
Loading