Repository navigation
Conversation
Add a `prepare_end` callback to `BaseStateCallback`, fired once per registered handler at the end of the prepare phase. The CLI fires it after the deploy code has run and change detection is done, but before any of the exit paths that follow, so it runs for dry runs and debug dumps too. The handler receives `dry` and `debug` flags so it can decide what to do for those runs: work that only makes sense when operations will follow should return early when either is set. An exception raised in the handler aborts the run. Signed-off-by: martin f. krafft <madduck@madduck.net>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(api): add prepare_end callback
What this does
Adds a
prepare_endhook toBaseStateCallback, fired once per registered handler atthe end of the prepare phase — after the deploy code has run and change detection is
done, but before any operation executes.
This closes a gap in the callback surface: pyinfra can currently tell you when a host
connects, when an operation starts, and when a host succeeds or fails, but there is no
seam between "the inventory is prepared" and "the run begins". Whole-inventory work that
must happen once per run — cache invalidation, controller-side bookkeeping, or gathering a
fact from every host and caching the result — has nowhere natural to live.
prepare_endfires from the CLI right after_handle_commandsreturns, before thediff-print,
--dry, and debug exit branches. So it also runs for dry runs and debug dumps;handlers learn the run mode through two flags:
dry—Truefor a--dryrun.debug—Truefor--debug-factsor--debug-operations.A handler that only makes sense when operations will follow returns early when either flag
is set:
An exception raised in the handler aborts the run.
Design decisions
Why
prepare_endand notprepare_complete. The event names the end of the phase,not the completion of an abstract thing, and
prepare_endpairs with the existingoperation_start/operation_endvocabulary.Fired from the CLI, not
run_ops. "Prepare" is a phase the CLI drives; the API has noprepare boundary of its own (the caller owns stage management). Firing from
run_opswouldalso double-fire, because the CLI calls
run_opsafter setting the stage. The CLI-onlyhome is what makes the
dry/debugflags meaningful, since those are CLI concepts. Thisis stated in the docstring.
Flags as keyword args with defaults. They are additive and optional, so an API-mode
handler that overrides
prepare_end(state)and ignores run mode keeps working. Thismatches the existing
retry_count/max_retriesparameters on the operation-host hooks.Not fired for commands that end before prepare. A
factquery exits inside_handle_commands, anddebug-inventoryexits before the prepare stage, so neither firesthe hook. That is deliberate and documented.
Once per registered handler. This follows the existing callback contract — the hook
runs once for each handler registered on the state. Because a deploy file is executed once
per host, registering a handler in a deploy file installs one handler per host; the docs
call this out and recommend registering once before the run.
Tests
tests/test_cli/test_cli_callbacks.pyruns the CLI end to end against a single@fakehost and a deploy fixture that records what
prepare_endreceived, covering the normalrun,
--dry,--debug-operations, and--debug-facts.Checklist
uv run pytest --disable-warnings -m 'not end_to_end'— 2251 passedscripts/dev-lint.sh— ruff, mypy (168 files), arguments-sync all cleandocs/api/index.md,docs/deploy-process.md) andCHANGELOG.mdentryAI disclosure
This change was authored with AI assistance (a coding agent running against the pyinfra
tree). The human submitter reviewed the implementation, understands how it interacts with
the CLI stage machine and the callback dispatch in
state.trigger_callbacks, and isresponsible for the final content.
🤖