Route exclusion-match messages through logging (quiet library, unchanged CLI)#593
Merged
Conversation
…ged CLI) is_name_excluded() printed "Excluded prefix/suffix" lines via utils.print_grey (a raw print()), bypassing logging entirely. These fire during collection construction (RoleDetailList / ManagedPolicyDetails) for every service-linked role and AWS-managed policy, so any library parse of GetAccountAuthorizationDetails floods stdout with no way to silence it. Route the prefix/suffix branches through a _report_exclusion() helper: logger.debug by default (library — silenceable via standard logging) and print_grey when a ContextVar toggle is on. The CLI group callback enables the toggle for the duration of the invocation and restores the prior value via ctx.call_on_close(), so CLI output is byte-for-byte unchanged and an in-process CLI run does not leak printing state into later library use (the Codex adversarial-review finding). Using a ContextVar instead of a module global keeps the override scoped/thread-safe and avoids the discouraged `global` statement. TDD: new test/shared/test_exclusion_output.py covers library-quiet (prefix+suffix), the logger.debug path, CLI printing, the returned previous value, and an in-process CLI regression proving the toggle is restored. test/conftest.py adds an autouse reset fixture as defense-in-depth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add docs/plans/exclusion-message-logging.md: make the "Excluded prefix/suffix" lines from is_name_excluded quiet for library consumers (logger.debug) while keeping CLI output byte-for-byte identical via a module-level toggle the CLI group callback flips on. Fact-checked by an Opus review subagent (verdict: yes-with-fixes); the three required fixes are folded in. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v2 of the plan. The Codex adversarial review (verdict: needs-attention) flagged the v1 sticky process-global toggle: an in-process caller that runs the CLI and then uses the library would keep printing exclusion matches, violating the quiet-by-default library contract. Fix: scope set_exclusion_output(True) to the Click invocation and restore the prior value via ctx.call_on_close(); set_exclusion_output now returns the previous value. Add an embedded-CLI regression test (TDD item 5) that runs the CLI in-process then asserts the library is quiet afterward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 14, 2026
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.
What
Makes the
Excluded prefix:/Excluded suffix:messages fromis_name_excludedquiet for library consumers while keeping CLI output byte-for-byte identical.Why
is_name_excludedemitted prefix/suffix matches viautils.print_grey— a rawprint()that bypasses logging entirely. These fire during collection construction (RoleDetailList/ManagedPolicyDetails) for every service-linked role and AWS-managed policy, so any library parse ofget-account-authorization-details(e.g. constructingAuthorizationDetails/PolicyDocument) floods stdout with no way to silence it via standardlogging. The exact-match branch already usedlogger.debug; only prefix/suffix were inconsistent.How
exclusions.py: route the prefix/suffix branches through a_report_exclusion()helper —logger.debugby default (library, silenceable via standard logging),print_greywhen a toggle is on.bin/cli.py: the Click group callback enables the toggle for the duration of the invocation and restores the prior value viactx.call_on_close()— so CLI output is unchanged and an in-process CLI run does not leak printing state into later library use.ContextVar(not a moduleglobal) holds the toggle — lint-clean (PLW0603) and naturally scoped/thread-safe.Behavior
AuthorizationDetailswith a service-linked role now prints nothing to stdout (messages go tologger.debug).cloudsplaining scanon the example data still prints the greyExcluded prefixlines (verified: 20 lines, exit 0).downloadCLI command never emitted these (it's pure boto3 and never builds scan objects); the leak came from the library parsing path, which is what's now quiet.Tests (TDD)
test/shared/test_exclusion_output.py— library-quiet (prefix + suffix), thelogger.debugpath, CLI printing, the returned previous value, and an in-process CLI regression proving the toggle is restored after a run.test/conftest.pyadds an autouse reset fixture (defense-in-depth).Review trail
yes-with-fixes(folded in).ctx.call_on_close()restore + regression test.The implementation plan is included at
docs/plans/2026-06-14-exclusion-message-logging.md.Gates
just pre-pushgreen: lint, unit-tests (130 passed), type-check, test-js (47 passing), safety-scan.🤖 Generated with Claude Code