feat(cli): add --verbose and --loglevel flags, align env vars to NAC_TEST_ prefix#599
Conversation
…debug flag (#512) Standardize internal environment variables to use NAC_TEST_PYATS_ prefix for consistency with the public NAC_TEST_ convention. Add --debug CLI flag for easier troubleshooting with verbose output and archive retention. Environment variable renames: - PYATS_MAX_WORKERS → NAC_TEST_PYATS_PROCESSES - MAX_CONNECTIONS → NAC_TEST_PYATS_MAX_CONNECTIONS - NAC_API_CONCURRENCY → NAC_TEST_PYATS_API_CONCURRENCY - NAC_SSH_CONCURRENCY → NAC_TEST_PYATS_SSH_CONCURRENCY - PYATS_OUTPUT_BUFFER_LIMIT → NAC_TEST_PYATS_OUTPUT_BUFFER_LIMIT - KEEP_HTML_REPORT_DATA → NAC_TEST_PYATS_KEEP_REPORT_DATA - NAC_TEST_OVERFLOW_DIR → NAC_TEST_PYATS_OVERFLOW_DIR - PYATS_DEBUG → NAC_TEST_DEBUG New --debug flag enables verbose output for pabot/PyATS and preserves intermediate archive files for troubleshooting. The following env vars remain as undocumented internal tuning knobs, not exposed as CLI flags: - NAC_TEST_SENTINEL_TIMEOUT - NAC_TEST_PIPE_DRAIN_DELAY - NAC_TEST_PIPE_DRAIN_TIMEOUT - NAC_TEST_BATCH_SIZE - NAC_TEST_BATCH_TIMEOUT - NAC_TEST_QUEUE_SIZE
- Replace obsolete NAC_API_CONCURRENCY, NAC_SSH_CONCURRENCY with new NAC_TEST_PYATS_* prefixed variables - Add all documented PyATS tuning variables to diagnostic output - Add separate section for undocumented internal tuning variables
…idden When --debug is set without an explicit --verbosity flag, verbosity now defaults to DEBUG instead of WARNING. Explicit --verbosity always wins. - Add tests for all debug/verbosity flag combinations (11 test cases) - Extract shared run_cli_with_temp_dirs() helper to conftest.py - Refactor test_main_exit_codes.py to use shared helper
Replace 8 repetitive test methods with a single parametrized test, reducing boilerplate while maintaining the same coverage.
Wire --verbosity flag through to OutputProcessor so PyATS log messages are filtered consistently with nac-test's own logger output. This gives users control over PyATS noise without requiring the NAC_TEST_DEBUG environment variable. Behavior by verbosity level: - WARNING (default) or ERROR: Suppress all PyATS logs, show only critical messages (ERROR, FAILED, Traceback, etc.) - INFO (-v INFO): Show PyATS logs at INFO level and above - DEBUG (-v DEBUG or --debug): Show all output Performance optimizations: - Pre-compile regex patterns at module load time - Early exit paths based on verbosity level - Fast string check before regex for common patterns - Benchmarked against 412k lines: <0.1s at default verbosity Changes: - Add debug and verbosity parameters to OutputProcessor - Thread verbosity through PyATSOrchestrator and CombinedOrchestrator - Replace DEBUG_MODE env check with self.debug for section progress - Convert parse failure print() to logger.debug() - Update e2e test: --debug now shows PyATS output (implies DEBUG verbosity)
The TestE2EDebugEnv test was redundant with TestE2EDebug since both test the same debug behavior. The env var test only verified that Typer's envvar= parameter works, which is Typer's responsibility. The actual debug behavior (verbose output, PyATS logs) is already covered by TestE2EDebug which uses the --debug CLI flag. Note: The extra_env_vars parameter in _run_e2e_scenario is retained for potential future use cases.
b8dcb9e to
ebec315
Compare
Fix issue where PyATS ignores nac-test's --verbosity flag. The root cause was that aetest's configure_logging() overwrites standard logging config. Solution: Set managed_handlers.screen.setLevel() directly in generated job files, which is the only reliable way to control PyATS console output. Changes: - JobGenerator: Accept verbosity param, set screen handler level in job files - SubprocessRunner: Map verbosity to PyATS CLI flags (-v, -q, -qq, -qqq) - OutputProcessor: Simplify _should_show_line() since filtering now happens at source via managed_handlers - logging.py: Add VERBOSITY_TO_LOGLEVEL and LOGLEVEL_NAME_TO_VALUE mappings as module-level constants for reuse across components Tests: - Add unit tests for job file generation (test_job_generator.py) - Add unit tests for verbosity CLI flag construction - Add unit tests for VERBOSITY_TO_LOGLEVEL mapping - Add E2E scenario (DEBUG_WITH_INFO_SCENARIO) verifying --debug --verbosity INFO filters %EASYPY-DEBUG while showing %EASYPY-INFO Docs: - Update README with --verbosity interaction with --debug flag
Decouple Robot's verbose output (pabot --verbose) from loglevel (--loglevel). Previously both were tied to --debug flag. Now: - --debug: enables pabot verbose output (console) - --verbosity DEBUG: sets Robot --loglevel DEBUG - --verbosity INFO/WARNING/etc: no --loglevel set (Robot default) - Explicit --loglevel arg always takes precedence This aligns Robot behavior with PyATS verbosity handling. Changes: - Add loglevel parameter to run_pabot(), separate from verbose - Update RobotOrchestrator to compute loglevel from verbosity - Add unit tests for loglevel precedence logic - Update README to document the behavior
- Reduce test permutations from 11 to 7 (remove -v alias and order tests) - Add verification that verbosity and debug are passed to CombinedOrchestrator - Add descriptive test IDs for better readability
…ired - Make verbosity a required parameter in JobGenerator.__init__ to ensure explicit intent and prevent test escapes if verbosity handling is removed - Refactor test_job_generator.py to use base class pattern with fixtures: - Add default_test_files fixture for tests that don't care about paths - Use generate_content fixture consistently across all tests - Make verbosity optional in generate_content callable (defaults to WARNING) - Remove redundant test_contains_screen_handler_setlevel (covered by parametrized test_verbosity_mapped_to_screen_handler) - Remove test_init_default_verbosity (no longer applicable with required param) - Reverse parameter order in generate_content: test_files first, then verbosity
Add DEFAULT_VERBOSITY constant to nac_test/utils/logging.py to centralize the default verbosity level (WARNING). This makes it easier to change the default in one place if needed in the future. Changes: - Add DEFAULT_VERBOSITY = VerbosityLevel.WARNING in utils/logging.py - Update 6 source files to import DEFAULT_VERBOSITY from utils.logging - Update 3 test files to use DEFAULT_VERBOSITY for default behavior tests Tests that check specific verbosity level behavior (parametrized tests covering all levels, explicit --verbosity flag tests) intentionally remain hardcoded to test each level individually.
Add _get_positive_numeric() helper to core/constants.py that validates environment variables return positive values, falling back to defaults otherwise. This consolidates scattered inline parsing patterns. Changes: - core/constants.py: Add TypeVar-based helper for int/float support, update DEFAULT_API_CONCURRENCY and DEFAULT_SSH_CONCURRENCY to use it - pyats_core/constants.py: Import helper and migrate all env var constants (buffer limit, sentinel timeout, pipe drain, batching) - Rename DEFAULT_BUFFER_LIMIT → PYATS_OUTPUT_BUFFER_LIMIT - Move BATCH_SIZE, BATCH_TIMEOUT_SECONDS, OVERFLOW_QUEUE_SIZE, OVERFLOW_MEMORY_LIMIT_MB from batching_reporter.py to constants.py Helper is defined in core/ (not utils/) to avoid circular import chain: core/constants → utils/ → utils/environment → core/constants
Update PRD_AND_ARCHITECTURE.md Progress Reporting section to include explicit method references for each component, making it easier for developers to navigate the codebase: - ProgressReporterPlugin: _emit_event(), pre/post_job(), pre/post_task() - OutputProcessor: process_line(), _handle_progress_event(), _finalize_orphaned_tests() - ProgressReporter: report_test_start(), report_test_end(), get_next_test_id() - SubprocessRunner: execute_job(), _process_output_realtime(), _drain_remaining_buffer_safe() Keeps descriptions high-level without exposing internal implementation details, while providing clear entry points for code exploration.
The --debug flag name was misleading as it controls verbose output rather than debug-level functionality. Rename to --verbose with corresponding NAC_TEST_VERBOSE environment variable. Changes: - CLI: --debug → --verbose flag - Env var: NAC_TEST_DEBUG → NAC_TEST_VERBOSE (user-facing) - Internal: debug parameter → verbose in orchestrators - Tests: rename fixtures/debug → fixtures/verbose, update test classes - Docs: update README.md and PRD_AND_ARCHITECTURE.md Note: NAC_TEST_DEBUG is retained as a hidden variable for developer-level debugging (DEBUG_MODE in constants.py).
Rename the CLI argument from --verbosity to --loglevel for clarity, as it controls the logging level, not verbosity. The --verbose flag now controls verbose output mode separately. Key changes: - Rename VerbosityLevel enum to LogLevel with enhanced functionality (int_value property, comparison operators) - Add --loglevel (-l) as the new CLI argument for log level control - Deprecate --verbosity (-v) as hidden alias for backwards compatibility - Add --verbose flag for enabling verbose output mode - Simplify Robot loglevel: only set to DEBUG when nac-test loglevel is DEBUG - Rename run_pabot's loglevel parameter to robot_loglevel - Update all internal variable names from verbosity to loglevel - Remove unused NAC_VALIDATE_VERBOSITY env var (use NAC_TEST_LOGLEVEL) - Update README with new CLI options and breaking change documentation BREAKING CHANGE: Robot Framework --loglevel pass-through no longer supported. Use --variable approach documented in README instead.
DEBUG is the lowest log level, so <= is misleading and suggests impossible states. Aligns with other DEBUG checks in the codebase.
The orchestrator and connection manager were using non-standard env var names (PYATS_MAX_WORKERS, MAX_SSH_CONNECTIONS) that didn't match the documented NAC_TEST_PYATS_* naming convention. The existing unit tests only tested the SystemResourceCalculator utility with default parameters, missing the fact that callers were overriding the env_var parameter with incorrect names. Add integration tests that verify the actual call sites use the correct env var names, preventing future regressions. - nac_test/pyats_core/orchestrator.py: PYATS_MAX_WORKERS -> NAC_TEST_PYATS_PROCESSES - nac_test/pyats_core/ssh/connection_manager.py: MAX_SSH_CONNECTIONS -> NAC_TEST_PYATS_MAX_SSH_CONNECTIONS - dev-docs/: Update stale env var references in documentation
SENTINEL_TIMEOUT_SECONDS (env: NAC_TEST_PYATS_SENTINEL_TIMEOUT) was introduced in d1c3865 as a deadlock guard for sentinel-based IPC synchronization, but was never wired into the subprocess runner. The sentinel mechanism in _process_output_realtime() works by reading lines until EOF, then falling back to _drain_remaining_buffer_safe() if no stream_complete sentinel was received. A meaningful timeout would require wrapping the entire post-EOF drain wait — which is already covered by PIPE_DRAIN_TIMEOUT_SECONDS. There is no natural place to apply a separate sentinel timeout without a design change. Remove the constant definition, __all__ export, diagnostic script entry, and PRD_AND_ARCHITECTURE.md table row. The env var name is left available for a future PR if a deadlock guard is ever implemented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…checks
Replace raw os.environ.get("NAC_TEST_DEBUG") checks in orchestrator.py
and multi_archive_generator.py with the DEBUG_MODE constant from
core/constants.py. This ensures consistent semantics (only "true"
triggers debug mode, not empty strings or other truthy values) and
uses the single source of truth for the flag.
Also update log messages to use "or" instead of "/" for clarity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t.py Replace per-fixture local imports of scenario constants with a single module-level import block. All scenario constants from tests.e2e.config are now imported at the top of the file, consistent with standard Python import conventions and the existing module-level E2EScenario import. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a comment explaining why pyats.log.managed_handlers is imported inside the test body rather than at module level: a collection-time ImportError gives too little context; keeping it here ensures failures surface in the right test with a clear name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a comment explaining why test_pabot_verbose_shows_test_result and test_easypy_info_in_stdout are duplicated from TestE2EVerbose rather than extracted into an intermediate base class. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The helper is intentionally imported across package boundaries (to avoid circular imports), so a leading underscore is misleading. The new name signals it is a shared utility and clarifies its purpose. Add unit tests in tests/unit/core/test_constants.py covering int and float parsing, zero/negative/non-numeric/empty fallback to default, and the not-set case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the hardcoded /tmp/test.py with a tmp_path-based file, making the fixture portable and consistent with pytest conventions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
apic.test.com could theoretically resolve; replace with RFC 2606 .invalid to make the non-functional intent explicit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Hey @oboehmer, thank you for the PR — the motivation here is solid. Standardizing the env var naming to the I did a comprehensive review on this, and I think we need some changes before merge. The implementation introduces a few patterns that don't align with our existing codebase conventions, and more critically, there are some runtime-breaking issues and incomplete refactoring steps that would cause the application to fail on startup. TLDR-ish: 1. Runtime TypeError:
|
Address PR 599 review comments 7 and 8: - 7: Add warning logging when env var contains invalid (non-numeric) or non-positive values. Silent fallback to default only when env var is unset. New warn_on_invalid parameter allows callers to suppress warnings if needed. - 8: Move get_positive_numeric_env() from core/constants.py to new nac_test/_env.py module. The underscore prefix signals internal API. Placement at package root avoids circular import via utils/__init__.py (see #610 for broader discussion on utils re-export strategy). Tests added for warning behavior (4 new test cases).
Address PR review comment 9: - core/constants.py: Add annotations to 25 public constants - pyats_core/constants.py: Add annotations to 10 public constants Private variables (e.g., _pipe_drain_default) left unannotated.
Address PR review comment 11: - Add module docstring - Add docstring to version_callback()
…TLEVELSPLIT PR review comment 12: Add get_bool_env() helper for consistent boolean env var parsing (accepts 'true', 'yes', '1'). Rename NO_TESTLEVELSPLIT to DISABLE_TESTLEVELSPLIT for clarity and NAC_TEST_ prefix alignment. Breaking change: NAC_TEST_NO_TESTLEVELSPLIT is now NAC_TEST_DISABLE_TESTLEVELSPLIT
Move NAC_TEST_BATCHING_REPORTER env var parsing to centralized constant using get_bool_env(). Update step_interceptor.py to use the constant.
|
Thanks for the review, @aitestino ! I've addressed comments 7-12: Comment 7 - Warning logging for invalid env vars: Added warning logs to Comment 8 - Relocate helper: Moved Comment 9 - Type annotations: Added explicit type annotations to all public constants in Comment 10 - Comment 11 - Docstrings: Added module docstring and Comment 12 - Regarding comments 1-6: Not sure what state you were reviewing, but none of these issues are present on the current branch. For example, I would appreciate a quick re-check so we can unblock the alpha release. |
…eta' into feature/512-env-var-alignment-v2
The pabot.writer module uses a singleton that captures stdout at creation time. When running multiple E2E scenarios sequentially, the second test would reuse the same singleton with a stale stdout reference, causing verbose output to go to the wrong stream. This fix resets the singleton at the start of each scenario execution. Workaround for #611
1297224 to
96eb8ad
Compare
…eta' into feature/512-env-var-alignment-v2
aitestino
left a comment
There was a problem hiding this comment.
Hey @oboehmer, thank you for the detailed response and for the patience on this.
You're right about comments 1-6 — I went back and verified each one against e960c6c, and the code was already in the correct state when I reviewed. It looks like my review was comparing against a stale base rather than looking at the actual branch files. That's my mistake, and I apologize for the noise and the delay it caused.
Your work on comments 7-12 looks great:
-
get_positive_numeric_env()warning logging (comment 7) — Clean implementation with thewarn_on_invalidkwarg. Tests cover all four paths (valid, invalid, non-positive, disabled). -
_env.pyrelocation (comment 8) — The underscore-prefix convention at package root is a pragmatic solution given the real circular import constraint. The comment explaining why it lives there instead ofutils/is appreciated, and filing #610 for the broader re-export discussion is the right call. -
Type annotations (comment 9) — All 26 constants annotated across both files. Complete.
-
@total_orderinginvestigation (comment 10) — You're correct that it doesn't work with(str, Enum)inheritance. I verified independently —str.__gt__takes MRO precedence over the decorator-generated method, soLogLevel.CRITICAL > LogLevel.ERRORreturnsFalse(alphabetic). The explicit overrides are necessary. -
Docstrings (comment 11) — Complete.
-
get_bool_env()andDISABLE_TESTLEVELSPLIT(comment 12) — This went beyond the fix by also improving the constant name and properly documenting the breaking change in CHANGELOG. The parametrized tests covering 13 input combinations are solid.
Also noticed the BATCHING_REPORTER_ENABLED constant wiring into step_interceptor.py — good consistency with the overall centralization goal.
LGTM — let's unblock the alpha release.
P.S. — This comment was drafted using voice-to-text via Claude Code. If the tone comes across as overly direct or terse, please know that's just how it tends to phrase things. No offense or criticism is intended — this is purely an objective technical review of the PR. Thanks for understanding! 🙂
Merge base branch incorporating PR #599 (logging refactor: VerbosityLevel -> LogLevel) and PR #554 (dry-run support). Conflict resolution: - Import merges: combined both our auth-related imports (get_env_var_prefix, AUTH_SUCCESS) with the new logging imports (LogLevel, DEFAULT_LOGLEVEL) - test_main_exit_codes.py: adopted upstream parametrized test structure, added PreFlightFailure test case as additional parametrize entry - Incidental conflicts (subprocess_runner, batching_reporter, robot orchestrator): took upstream changes
…ution-in-base-class-v2 Brings in critical upstream improvements from v2.0.0a1 release: - Fail-fast authentication validation (#531) - Dry-run support for PyATS (#554) - Clean up stale test artifacts (#526, #571) - Improved CLI with --verbose and --loglevel flags (#599) - Better test isolation for parallel execution (#569) - GitHub templates for issues and PRs (#510) - Multiple dependency updates and bug fixes These changes provide the test infrastructure improvements needed for the defaults resolution feature, particularly the controller auth validation that addresses test fixture concerns. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Description
Standardize internal environment variables to use
NAC_TEST_PYATS_prefix for consistency with the publicNAC_TEST_convention. Add--verboseCLI flag for easier troubleshooting. Implement--loglevelflag support to control log detail levels for both PyATS and Robot Framework.Environment variable renames:
PYATS_MAX_WORKERSNAC_TEST_PYATS_PROCESSESMAX_CONNECTIONSNAC_TEST_PYATS_MAX_CONNECTIONSNAC_API_CONCURRENCYNAC_TEST_PYATS_API_CONCURRENCYNAC_SSH_CONCURRENCYNAC_TEST_PYATS_SSH_CONCURRENCYPYATS_OUTPUT_BUFFER_LIMITNAC_TEST_PYATS_OUTPUT_BUFFER_LIMITKEEP_HTML_REPORT_DATANAC_TEST_PYATS_KEEP_REPORT_DATANAC_TEST_OVERFLOW_DIRNAC_TEST_PYATS_OVERFLOW_DIRPYATS_DEBUGNAC_TEST_DEBUGNew
--verboseflag behavior:--verbose) showing Robot console output--verbose, PyATS output is suppressed)--loglevel DEBUGunless explicitly overridden--loglevelflag controls log detail level:--loglevel--loglevel--verbose)--verbose)--verbose)--verbose)Key behaviors:
--verbose. Without it, PyATS output is suppressed regardless of--loglevel.--verbose: Enables console output for both frameworks, implies--loglevel DEBUG--verbose --loglevel INFO: Shows PyATS console output filtered to INFO+, Robot uses default loglevel--verbose --loglevel ERROR: Shows PyATS console output filtered to ERROR+ only, Robot uses default loglevel--loglevelcontrols nac-test internal logging level in addition to framework output (independent of--verbose), no change in this PRDeprecation notice:
--verbosityis now a hidden alias for--logleveland will be removed in a future version. Use--loglevelinstead.Breaking change:
--loglevelcan no longer be controlled via pass-through arguments. See README for workarounds using--variableandSet Log Levelkeyword.PyATS output filtering is optimized for high-volume runs (benchmarked: 412k lines in <0.1s).
Note: To preserve intermediate archive/JSONL files for troubleshooting, set
NAC_TEST_DEBUG=trueorNAC_TEST_PYATS_KEEP_REPORT_DATA=trueas environment variables. The--verboseCLI flag enables verbose output but does not retain these files.The following env vars remain as undocumented internal tuning knobs, however I have moved all of them to
nac_test/pyats_core/constants.pyso we can manage them centrally. Due to this move I changed the respective internal constant name:Note:
SENTINEL_TIMEOUT_SECONDS/NAC_TEST_PYATS_SENTINEL_TIMEOUTwere introduced in d1c3865 but never wired into the subprocess runner. Removed entirely in commit 3ab23b3 within this PR — see that commit message for the full reasoning.Closes
Related Issue(s)
Type of Change
Test Framework Affected
Network as Code (NaC) Architecture Affected
Platform Tested
Key Changes
--verboseCLI flag with env var support (NAC_TEST_VERBOSE)--verboseenables verbose output for both pabot and PyATS execution--verboseimplies DEBUG loglevel unless--loglevelis explicitly set--loglevelCLI argument to control log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)--verbosityargument (hidden alias for--loglevel, will be removed in future version)--verboseis set;--loglevelfilters what is shown--loglevelin generated job files viamanaged_handlers.screen.setLevel()verbose(pabot console) fromloglevel(Robot--loglevel)--loglevel DEBUGonly when nac-test--loglevel DEBUG--loglevelpass-through no longer supported; use--variableapproach documented in READMELogLevelenum class with comparison operators--verboseand--verbose --loglevel INFObehaviorTesting Done
pytest/pre-commit run -a)Test Commands Used
uv run pytest -n auto --dist loadscope # Full test suite uv run pytest tests/unit/cli/ -v uv run pytest tests/unit/robot/ -v uv run pytest tests/unit/pyats_core/execution/ -v uv run pytest tests/unit/utils/test_logging.py -v uv run pytest tests/unit/test_pabot_args.py -v uv run pytest tests/e2e -k verbose -v uv run pre-commit run --all-filesChecklist
pre-commit run -apasses)