bitranox-template-py-cli 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/CHANGELOG.md +17 -0
  2. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/PKG-INFO +2 -2
  3. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/docs/systemdesign/module_reference.md +3 -3
  4. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/pyproject.toml +3 -2
  5. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/__init__conf__.py +1 -1
  6. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/config.py +4 -4
  7. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/config_load.py +12 -12
  8. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/logging/setup.py +46 -22
  9. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/memory/__init__.py +4 -4
  10. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/memory/logging.py +7 -6
  11. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/application/ports.py +5 -5
  12. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/composition/__init__.py +8 -8
  13. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/conftest.py +12 -12
  14. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_env_file.py +1 -1
  15. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_deploy_permissions.py +1 -1
  16. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_logging_dotenv_isolation.py +42 -6
  17. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_ports.py +8 -8
  18. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.devcontainer/devcontainer.json +0 -0
  19. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.devcontainer/settings.json +0 -0
  20. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.env.example +0 -0
  21. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.gitattributes +0 -0
  22. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.github/actions/extract-metadata/action.yml +0 -0
  23. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.github/dependabot.yml +0 -0
  24. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.github/workflows/codeql.yml +0 -0
  25. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.github/workflows/default_cicd_public.yml +0 -0
  26. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.github/workflows/default_release_public.yml +0 -0
  27. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.gitignore +0 -0
  28. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.qlty/qlty.toml +0 -0
  29. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/.snyk +0 -0
  30. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/CONFIG.md +0 -0
  31. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/CONTRIBUTING.md +0 -0
  32. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/DEVELOPMENT.md +0 -0
  33. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/INSTALL.md +0 -0
  34. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/LICENSE +0 -0
  35. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/Makefile +0 -0
  36. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/README.md +0 -0
  37. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/SECURITY.md +0 -0
  38. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/ai-stance.md +0 -0
  39. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/ai-transparency.md +0 -0
  40. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/codecov.yml +0 -0
  41. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/docs/adr/0001-memory-adapters-in-src.md +0 -0
  42. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/notebooks/Quickstart.ipynb +0 -0
  43. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/rename.sh +0 -0
  44. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/rename_dry.sh +0 -0
  45. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/reset_git_history.sh +0 -0
  46. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/__init__.py +0 -0
  47. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/__main__.py +0 -0
  48. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/__init__.py +0 -0
  49. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/__init__.py +0 -0
  50. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/__init__.py +0 -0
  51. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/email/__init__.py +0 -0
  52. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/email/_common.py +0 -0
  53. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/email/send_email.py +0 -0
  54. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/email/send_notification.py +0 -0
  55. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/info.py +0 -0
  56. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/commands/logging.py +0 -0
  57. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/constants.py +0 -0
  58. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/context.py +0 -0
  59. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/exit_codes.py +0 -0
  60. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/main.py +0 -0
  61. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/py.typed +0 -0
  62. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/root.py +0 -0
  63. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/safe_console.py +0 -0
  64. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/cli/typed_click.py +0 -0
  65. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/__init__.py +0 -0
  66. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/40-layered-config.toml +0 -0
  67. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/50-mail.toml +0 -0
  68. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/defaultconfig.d/90-logging.toml +0 -0
  69. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/defaultconfig.toml +0 -0
  70. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/deploy.py +0 -0
  71. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/display.py +0 -0
  72. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/loader.py +0 -0
  73. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/overrides.py +0 -0
  74. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/config/py.typed +0 -0
  75. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/__init__.py +0 -0
  76. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/config.py +0 -0
  77. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/py.typed +0 -0
  78. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/sender.py +0 -0
  79. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/transport.py +0 -0
  80. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/email/validation.py +0 -0
  81. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/logging/__init__.py +0 -0
  82. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/logging/py.typed +0 -0
  83. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/memory/config.py +0 -0
  84. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/memory/email.py +0 -0
  85. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/adapters/py.typed +0 -0
  86. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/application/__init__.py +0 -0
  87. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/application/py.typed +0 -0
  88. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/composition/py.typed +0 -0
  89. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/domain/__init__.py +0 -0
  90. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/domain/behaviors.py +0 -0
  91. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/domain/enums.py +0 -0
  92. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/domain/errors.py +0 -0
  93. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/domain/py.typed +0 -0
  94. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/entry.py +0 -0
  95. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/src/bitranox_template_py_cli/py.typed +0 -0
  96. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_behaviors.py +0 -0
  97. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cache_effectiveness.py +0 -0
  98. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_attachment_refused.py +0 -0
  99. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_config.py +0 -0
  100. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_config_errors.py +0 -0
  101. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_core.py +0 -0
  102. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_email.py +0 -0
  103. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_email_config_errors.py +0 -0
  104. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_exit_codes.py +0 -0
  105. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_main_exit.py +0 -0
  106. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_overrides.py +0 -0
  107. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_cli_validation.py +0 -0
  108. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_config_overrides.py +0 -0
  109. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_declared_dependencies.py +0 -0
  110. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_deploy_mode_safety.py +0 -0
  111. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_display.py +0 -0
  112. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_email_attachment_lists.py +0 -0
  113. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_email_config_translation.py +0 -0
  114. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_email_password_secrecy.py +0 -0
  115. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_email_shipped_defaults.py +0 -0
  116. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_enums.py +0 -0
  117. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_errors.py +0 -0
  118. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_logging.py +0 -0
  119. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_mail.py +0 -0
  120. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_memory_logging.py +0 -0
  121. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_metadata.py +0 -0
  122. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_metadata_sync.py +0 -0
  123. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_module_entry.py +0 -0
  124. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_module_reference_sync.py +0 -0
  125. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_permission_defaults.py +0 -0
  126. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_property_email.py +0 -0
  127. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_property_overrides.py +0 -0
  128. {bitranox_template_py_cli-2.2.0 → bitranox_template_py_cli-2.3.0}/tests/test_safe_console.py +0 -0
@@ -6,6 +6,23 @@ the [Keep a Changelog](https://keepachangelog.com/) format.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.3.0] 2026-10-08 16:14:12
10
+
11
+ ### Changed
12
+ - **`config --profile NAME` checks the profile's logging settings instead of restarting logging.**
13
+ The check now runs through lib_log_rich's `validate_config`, which refuses exactly what starting
14
+ logging refuses, with the same `Error:` lines and exit 78, so nothing changes for an invalid
15
+ setting. What changes: the running logging runtime is no longer shut down and started again, so
16
+ the rest of the command keeps logging with the settings logging started with (not the profile's),
17
+ and a `main()` call from a worker thread no longer replaces the logging every other thread uses.
18
+ The `RestartLogging` port is now `CheckLoggingConfig` (`AppServices.check_logging_config`; the
19
+ testing composition's double refuses nothing). Requires `lib_log_rich>=6.4.2`.
20
+
21
+ ### Fixed
22
+ - **A logging refusal no single setting owns reads `lib_log_rich: <reason>`.** Such a problem (for
23
+ example TLS together with UDP for Graylog, which lib_log_rich 6.4.2 now refuses up front) has no
24
+ key, and its `Error:` line came out as `lib_log_rich.: <reason>` with a dangling dot.
25
+
9
26
  ## [2.2.0] 2026-10-08 13:56:49
10
27
 
11
28
  ### Removed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: bitranox_template_py_cli
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Template CLI application with configuration management and structured logging
5
5
  Project-URL: Homepage, https://github.com/bitranox/bitranox_template_py_cli
6
6
  Project-URL: Repository, https://github.com/bitranox/bitranox_template_py_cli.git
@@ -25,7 +25,7 @@ Requires-Dist: btx-lib-mail>=4.0.0
25
25
  Requires-Dist: click>=8.5.0
26
26
  Requires-Dist: lib-cli-exit-tools>=2.4.0
27
27
  Requires-Dist: lib-layered-config>=7.0.1
28
- Requires-Dist: lib-log-rich>=6.3.9
28
+ Requires-Dist: lib-log-rich>=6.4.2
29
29
  Requires-Dist: orjson>=3.13.0
30
30
  Requires-Dist: pydantic>=2.13.5
31
31
  Requires-Dist: python-dotenv>=1.2.4
@@ -26,14 +26,14 @@ moves or removes a module, a CLI option or a public name.
26
26
  - `src/bitranox_template_py_cli/adapters/email/transport.py` - `send_email` / `send_notification` over btx_lib_mail
27
27
  - `src/bitranox_template_py_cli/adapters/email/sender.py` - Re-exports of `config.py` and `transport.py`
28
28
  - `src/bitranox_template_py_cli/adapters/email/validation.py` - Email recipient validation
29
- - `src/bitranox_template_py_cli/adapters/logging/setup.py` - lib_log_rich initialization; takes only the `LOG_*` lines of a `.env`, and raises `InvalidLoggingConfigError` for a refused `[lib_log_rich]` value or `LOG_*` variable, after starting logging with its defaults (and without the `LOG_*` variables only when one of them is refused); `restart_logging` shuts the running runtime down and starts it again with another configuration
29
+ - `src/bitranox_template_py_cli/adapters/logging/setup.py` - lib_log_rich initialization; takes only the `LOG_*` lines of a `.env`, and raises `InvalidLoggingConfigError` for a refused `[lib_log_rich]` value or `LOG_*` variable, after starting logging with its defaults (and without the `LOG_*` variables only when one of them is refused); `check_logging_config` refuses another configuration's logging settings through lib_log_rich's `validate_config`, without touching the running runtime
30
30
  - `src/bitranox_template_py_cli/adapters/cli/` - CLI adapter package:
31
31
  - `__init__.py` - Public facade
32
32
  - `constants.py` - Shared constants
33
33
  - `safe_console.py` - Encode-safe terminal output; use `safe_console.echo` instead of `click.echo`
34
34
  - `exit_codes.py` - POSIX exit codes (ExitCode IntEnum)
35
35
  - `context.py` - Click context helpers
36
- - `config_load.py` - Configuration load for the CLI; records a load failure, `require_config` refuses with exit 78, `restart_logging` restarts logging with a configuration reloaded by `config --profile`, refusing its logging settings as the root does
36
+ - `config_load.py` - Configuration load for the CLI; records a load failure, `require_config` refuses with exit 78, `check_logging` checks the logging settings of a configuration reloaded by `config --profile`, refusing them as the root does
37
37
  - `root.py` - Root command group
38
38
  - `main.py` - Entry point
39
39
  - `typed_click.py` - Strictly typed wrappers for rich_click's `option`, `version_option` and `get_current_context`
@@ -409,7 +409,7 @@ The `adapters/memory/` package provides lightweight implementations for testing:
409
409
  |---------------------|-----------------------------------------------------------------------------|
410
410
  | `memory/config.py` | `GetConfig`, `GetDefaultConfigPath`, `DeployConfiguration`, `DisplayConfig` |
411
411
  | `memory/email.py` | `SendEmail`, `SendNotification` |
412
- | `memory/logging.py` | `InitLogging`, `RestartLogging` |
412
+ | `memory/logging.py` | `InitLogging`, `CheckLoggingConfig` |
413
413
 
414
414
  `adapters/memory` exports `EmailSpy` (from `memory/email.py`): its `send_email` and
415
415
  `send_notification` methods satisfy the email ports and record each call as a `CapturedEmail` /
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bitranox_template_py_cli"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = "Template CLI application with configuration management and structured logging"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -9,7 +9,8 @@ dependencies = [
9
9
  # Imported directly (adapters/cli/main.py, commands/config.py), not only through rich-click.
10
10
  "click>=8.5.0",
11
11
  "lib_cli_exit_tools>=2.4.0",
12
- "lib_log_rich>=6.3.9",
12
+ # 6.4.2: validate_config, the check config --profile runs, refuses exactly what init() refuses.
13
+ "lib_log_rich>=6.4.2",
13
14
  "python-dotenv>=1.2.4",
14
15
  "lib_layered_config>=7.0.1",
15
16
  "btx_lib_mail>=4.0.0",
@@ -40,7 +40,7 @@ name = "bitranox_template_py_cli"
40
40
  #: Human-readable summary shown in CLI help output.
41
41
  title = "Template CLI application with configuration management and structured logging"
42
42
  #: Current release version pulled from ``pyproject.toml`` by automation.
43
- version = "2.2.0"
43
+ version = "2.3.0"
44
44
  #: Repository homepage presented to users.
45
45
  homepage = "https://github.com/bitranox/bitranox_template_py_cli"
46
46
  #: Author attribution surfaced in CLI output.
@@ -32,7 +32,7 @@ from bitranox_template_py_cli.adapters.config.overrides import nest_overrides
32
32
  from bitranox_template_py_cli.domain.enums import DeployTarget, OutputFormat
33
33
 
34
34
  from .. import safe_console
35
- from ..config_load import load_config, report_load_failure, require_config, restart_logging
35
+ from ..config_load import check_logging, load_config, report_load_failure, require_config
36
36
  from ..constants import CLICK_CONTEXT_SETTINGS
37
37
  from ..context import CLIContext, get_cli_context
38
38
  from ..exit_codes import ExitCode
@@ -110,8 +110,8 @@ def _resolve_config(ctx: click.Context, cli_ctx: CLIContext, profile: str | None
110
110
 
111
111
  When a subcommand-level profile override is specified, reloads config
112
112
  with that profile, the root's ``--env-file`` and any root-level ``--set``
113
- overrides stored in the CLI context, and restarts logging with it, which
114
- refuses its logging settings the way the root refuses its own.
113
+ overrides stored in the CLI context, and checks its logging settings, which
114
+ are refused the way the root refuses its own; logging itself keeps running unchanged.
115
115
 
116
116
  Args:
117
117
  ctx: The running command's click context, exited with 78 when loading failed.
@@ -128,7 +128,7 @@ def _resolve_config(ctx: click.Context, cli_ctx: CLIContext, profile: str | None
128
128
  cli_ctx.services, profile=profile, env_file=cli_ctx.env_file, set_overrides=cli_ctx.set_overrides
129
129
  )
130
130
  if error is None:
131
- error = restart_logging(cli_ctx.services, config, env_file=cli_ctx.env_file)
131
+ error = check_logging(cli_ctx.services, config)
132
132
  if error is not None:
133
133
  report_load_failure(error, show_traceback=cli_ctx.traceback)
134
134
  ctx.exit(ExitCode.CONFIG_ERROR)
@@ -16,7 +16,7 @@ Contents:
16
16
  * :func:`load_config` - load with profile, ``.env`` and ``--set``, or say why not.
17
17
  * :func:`start_logging` - start logging; an invalid ``[lib_log_rich]`` value or ``LOG_*``
18
18
  variable is a load failure.
19
- * :func:`restart_logging` - the same refusal for a configuration loaded after logging started.
19
+ * :func:`check_logging` - the same refusal for a configuration loaded after logging started.
20
20
  * :func:`require_config` - the configuration, or exit 78 naming the failure.
21
21
  * :func:`report_load_failure` - the one-line report, after the traceback on request.
22
22
  * :func:`echo_load_traceback` - the loader's traceback alone, for a caller with its own line.
@@ -105,7 +105,8 @@ def start_logging(
105
105
  refused setting) before it raises, and the commands that read the configuration refuse with
106
106
  exit 78 and the refusal: ``lib_log_rich.<key>: <reason>`` for a problem the section's type
107
107
  check finds, lib_log_rich's own message for a value only lib_log_rich refuses, whether it
108
- came from the section or a ``LOG_*`` variable (``lib_log_rich: Unknown log level: 'bogus'``).
108
+ came from the section or a ``LOG_*`` variable (``lib_log_rich: Invalid runtime settings:
109
+ console_level: Unknown log level: 'bogus'``).
109
110
 
110
111
  Args:
111
112
  services: The composition's services; only ``init_logging`` is used.
@@ -124,25 +125,24 @@ def start_logging(
124
125
  return config, config_error
125
126
 
126
127
 
127
- def restart_logging(services: AppServices, config: Config, *, env_file: str | None) -> Exception | None:
128
- """Restart logging with a configuration loaded after it started; a refused setting is returned.
128
+ def check_logging(services: AppServices, config: Config) -> Exception | None:
129
+ """Judge the logging settings of a configuration loaded after logging started; a refusal is returned.
129
130
 
130
131
  ``config --profile`` reloads the configuration once logging is running, so
131
132
  :func:`start_logging` never sees the reloaded ``[lib_log_rich]`` section; without this an
132
- invalid value there would display with exit 0 while plain ``config`` exits 78. Restarting
133
- runs the very start :func:`start_logging` ran, so it refuses the same settings in the same
134
- words, and the rest of the command logs with the reloaded settings.
133
+ invalid value there would display with exit 0 while plain ``config`` exits 78. The check
134
+ refuses what :func:`start_logging` refuses, in the same words, and never touches the
135
+ running runtime: the command keeps logging with the settings logging started with.
135
136
 
136
137
  Args:
137
- services: The composition's services; only ``restart_logging`` is used.
138
+ services: The composition's services; only ``check_logging_config`` is used.
138
139
  config: The configuration :func:`load_config` returned.
139
- env_file: The ``--env-file`` path, or None; logging reads its ``LOG_*`` lines from it.
140
140
 
141
141
  Returns:
142
- None, or the refusal; logging then runs with its defaults.
142
+ None, or the refusal.
143
143
  """
144
144
  try:
145
- services.restart_logging(config, dotenv_path=env_file)
145
+ services.check_logging_config(config)
146
146
  except InvalidLoggingConfigError as exc:
147
147
  return exc
148
148
  return None
@@ -196,10 +196,10 @@ def require_config(ctx: click.Context, cli_ctx: CLIContext) -> Config:
196
196
 
197
197
 
198
198
  __all__ = [
199
+ "check_logging",
199
200
  "echo_load_traceback",
200
201
  "load_config",
201
202
  "report_load_failure",
202
203
  "require_config",
203
- "restart_logging",
204
204
  "start_logging",
205
205
  ]
@@ -7,7 +7,7 @@ eliminating duplication between module entry (__main__.py) and console script
7
7
  Contents:
8
8
  * :func:`init_logging` - idempotent logging initialization from the ``[lib_log_rich]``
9
9
  section and the ``LOG_*`` variables (the environment, plus the ``LOG_*`` lines of a ``.env``).
10
- * :func:`restart_logging` - stop the running runtime and start it again with another configuration.
10
+ * :func:`check_logging_config` - refuse another configuration's logging settings without touching the runtime.
11
11
  * :class:`InvalidLoggingConfigError` - the ``[lib_log_rich]`` section or a ``LOG_*`` variable
12
12
  cannot configure logging.
13
13
  * :func:`_build_runtime_config` - constructs RuntimeConfig from layered sources.
@@ -100,10 +100,12 @@ class InvalidLoggingConfigError(ConfigurationError):
100
100
  """lib_log_rich refuses its settings: one ``<key>: <reason>`` in :attr:`problems` per problem.
101
101
 
102
102
  The settings are the ``[lib_log_rich]`` section plus any ``LOG_*`` variable. A problem the
103
- type check of the section finds names the key and never repeats the refused value. A value
104
- only lib_log_rich itself refuses, such as an unknown level in ``LOG_CONSOLE_LEVEL`` or in the
105
- section's ``console_level``, is reported in lib_log_rich's own words (``lib_log_rich: Unknown
106
- log level: 'bogus'``), which may name neither the setting nor where it was set.
103
+ type check of the section finds names the key and never repeats the refused value; one no
104
+ single key owns (TLS over UDP for Graylog) reads ``lib_log_rich: <reason>``. A value only
105
+ lib_log_rich itself refuses, such as an unknown level in ``LOG_CONSOLE_LEVEL`` or in the
106
+ section's ``console_level``, is reported in lib_log_rich's own words, which name the setting
107
+ and repeat the value (``lib_log_rich: Invalid runtime settings: console_level: Unknown log
108
+ level: 'bogus'``).
107
109
 
108
110
  Attributes:
109
111
  problems: One line per refused setting.
@@ -128,15 +130,33 @@ _PROJECT_MARKERS = ("pyproject.toml", ".git")
128
130
 
129
131
 
130
132
  def _problems(error: BaseException) -> list[str]:
131
- """One line per problem, from the first pydantic error in ``error``'s chain; never the value."""
133
+ """One line per problem: key and reason from the first pydantic error in ``error``'s chain.
134
+
135
+ A pydantic problem never repeats the refused value; one no single key owns (a model-wide
136
+ check) has no key and reads ``lib_log_rich: <reason>``. Without a pydantic error in the chain,
137
+ the line is lib_log_rich's own first line, which may repeat the value
138
+ (``Invalid runtime settings: console_level: Unknown log level: 'bogus'``).
139
+ """
132
140
  cause: BaseException | None = error
133
141
  while cause is not None and not isinstance(cause, ValidationError):
134
142
  cause = cause.__cause__ or cause.__context__
135
143
  if cause is None:
136
144
  # Not a pydantic error (e.g. an unknown level name in a LOG_* variable): lib_log_rich's
137
- # own first line, which may name neither the variable nor where it was set.
145
+ # own first line.
138
146
  return [f"lib_log_rich: {str(error).splitlines()[0]}"]
139
- return [f"lib_log_rich.{'.'.join(str(part) for part in item['loc'])}: {item['msg']}" for item in cause.errors()]
147
+ return [f"{_problem_key(item['loc'])}: {item['msg']}" for item in cause.errors()]
148
+
149
+
150
+ def _problem_key(loc: tuple[int | str, ...]) -> str:
151
+ """``lib_log_rich.<key>`` for a problem located at a key, bare ``lib_log_rich`` for a model-wide one.
152
+
153
+ Example:
154
+ >>> _problem_key(("rate_limit",))
155
+ 'lib_log_rich.rate_limit'
156
+ >>> _problem_key(())
157
+ 'lib_log_rich'
158
+ """
159
+ return ".".join(["lib_log_rich", *(str(part) for part in loc)])
140
160
 
141
161
 
142
162
  def _nearest_dotenv() -> Path | None:
@@ -281,35 +301,39 @@ def init_logging(config: Config, *, dotenv_path: str | None = None) -> None:
281
301
  lib_log_rich.runtime.attach_std_logging()
282
302
 
283
303
 
284
- def restart_logging(config: Config, *, dotenv_path: str | None = None) -> None:
285
- """Stop the running lib_log_rich runtime, then start it again with ``config``.
304
+ def check_logging_config(config: Config) -> None:
305
+ """Refuse ``config``'s logging settings as :func:`init_logging` would, without touching the runtime.
286
306
 
287
307
  :func:`init_logging` returns at once while a runtime is running, so a configuration loaded
288
308
  after logging started (``config --profile``'s reload) would never have its logging settings
289
- judged. This runs that same start again: a setting lib_log_rich refuses raises exactly as it
290
- does there, and logging is left running with the defaults. lib_log_rich has no public call
291
- that checks settings without starting the runtime.
309
+ judged. lib_log_rich's ``validate_config`` refuses exactly what its ``init`` refuses, with the
310
+ same message and the same ``LOG_*`` overrides, and neither reads nor replaces the running
311
+ runtime, so logging keeps running with the settings it started with, on every thread.
292
312
 
293
313
  Args:
294
314
  config: Already-loaded layered configuration object.
295
- dotenv_path: As for :func:`init_logging`.
296
315
 
297
316
  Raises:
298
- InvalidLoggingConfigError: As for :func:`init_logging`.
317
+ InvalidLoggingConfigError: As for :func:`init_logging`, with the same problem lines.
299
318
 
300
- Side Effects:
301
- Flushes and shuts down the running runtime first (its queue, Graylog and journald
302
- adapters included); the new one carries ``config``'s logging settings.
319
+ Example:
320
+ >>> check_logging_config(Config({"lib_log_rich": {"console_level": "info"}}, {}))
321
+ >>> try:
322
+ ... check_logging_config(Config({"lib_log_rich": {"rate_limit": "100:60"}}, {}))
323
+ ... except InvalidLoggingConfigError as exc:
324
+ ... print(exc)
325
+ lib_log_rich.rate_limit: Input should be a valid tuple
303
326
  """
304
- if lib_log_rich.runtime.is_initialised():
305
- lib_log_rich.runtime.shutdown()
306
- init_logging(config, dotenv_path=dotenv_path)
327
+ try:
328
+ lib_log_rich.runtime.validate_config(_build_runtime_config(config))
329
+ except ValueError as exc: # pydantic's ValidationError is a ValueError
330
+ raise InvalidLoggingConfigError(_problems(exc)) from exc
307
331
 
308
332
 
309
333
  __all__ = [
310
334
  "InvalidLoggingConfigError",
311
335
  "LoggingConfigModel",
336
+ "check_logging_config",
312
337
  "init_logging",
313
338
  "log_variables_hidden",
314
- "restart_logging",
315
339
  ]
@@ -20,28 +20,28 @@ from .config import (
20
20
  get_default_config_path_in_memory,
21
21
  )
22
22
  from .email import EmailSpy
23
- from .logging import init_logging_in_memory, restart_logging_in_memory
23
+ from .logging import check_logging_config_in_memory, init_logging_in_memory
24
24
 
25
25
  # Static conformance assertions
26
26
  if TYPE_CHECKING:
27
27
  from bitranox_template_py_cli.application.ports import (
28
+ CheckLoggingConfig,
28
29
  GetConfig,
29
30
  GetDefaultConfigPath,
30
31
  InitLogging,
31
- RestartLogging,
32
32
  )
33
33
 
34
34
  _assert_get_config: GetConfig = get_config_in_memory
35
35
  _assert_get_default_config_path: GetDefaultConfigPath = get_default_config_path_in_memory
36
36
  _assert_init_logging: InitLogging = init_logging_in_memory
37
- _assert_restart_logging: RestartLogging = restart_logging_in_memory
37
+ _assert_check_logging_config: CheckLoggingConfig = check_logging_config_in_memory
38
38
 
39
39
  __all__ = [
40
40
  "EmailSpy",
41
+ "check_logging_config_in_memory",
41
42
  "deploy_configuration_in_memory",
42
43
  "display_config_in_memory",
43
44
  "get_config_in_memory",
44
45
  "get_default_config_path_in_memory",
45
46
  "init_logging_in_memory",
46
- "restart_logging_in_memory",
47
47
  ]
@@ -63,17 +63,18 @@ def init_logging_in_memory(config: Config, *, dotenv_path: str | None = None) ->
63
63
  )
64
64
 
65
65
 
66
- def restart_logging_in_memory(config: Config, *, dotenv_path: str | None = None) -> None:
67
- """Keep the test runtime: :func:`init_logging_in_memory` ignores the configuration, so a restart changes nothing.
66
+ def check_logging_config_in_memory(config: Config) -> None:
67
+ """Refuse nothing: the test runtime ignores the configuration, so no setting in it can break it.
68
+
69
+ :func:`init_logging_in_memory` starts a fixed runtime whatever the configuration holds.
68
70
 
69
71
  Args:
70
- config: Unused; the parameter exists to satisfy the ``RestartLogging`` protocol.
71
- dotenv_path: Unused for the same reason.
72
+ config: Unused; the parameter exists to satisfy the ``CheckLoggingConfig`` protocol.
72
73
 
73
74
  Example:
74
75
  >>> from lib_layered_config import Config
75
- >>> restart_logging_in_memory(Config({"lib_log_rich": {"rate_limit": "100:60"}}, {}))
76
+ >>> check_logging_config_in_memory(Config({"lib_log_rich": {"rate_limit": "100:60"}}, {}))
76
77
  """
77
78
 
78
79
 
79
- __all__ = ["init_logging_in_memory", "restart_logging_in_memory"]
80
+ __all__ = ["check_logging_config_in_memory", "init_logging_in_memory"]
@@ -109,24 +109,24 @@ class InitLogging(Protocol):
109
109
  def __call__(self, config: Config, *, dotenv_path: str | None = None) -> None: ...
110
110
 
111
111
 
112
- class RestartLogging(Protocol):
113
- """Stop the running logging runtime and start it again with ``config``, as ``InitLogging`` does.
112
+ class CheckLoggingConfig(Protocol):
113
+ """Refuse ``config``'s logging settings as ``InitLogging`` would, leaving the running runtime alone.
114
114
 
115
115
  For a configuration loaded after logging is running; a refused setting raises
116
- ``InvalidLoggingConfigError`` after logging has been started with its defaults.
116
+ ``InvalidLoggingConfigError`` and logging keeps running with the settings it started with.
117
117
  """
118
118
 
119
- def __call__(self, config: Config, *, dotenv_path: str | None = None) -> None: ...
119
+ def __call__(self, config: Config) -> None: ...
120
120
 
121
121
 
122
122
  __all__ = [
123
+ "CheckLoggingConfig",
123
124
  "DeployConfiguration",
124
125
  "DisplayConfig",
125
126
  "GetConfig",
126
127
  "GetDefaultConfigPath",
127
128
  "InitLogging",
128
129
  "LoadEmailConfigFromDict",
129
- "RestartLogging",
130
130
  "SendEmail",
131
131
  "SendNotification",
132
132
  ]
@@ -19,20 +19,20 @@ from ..adapters.email.sender import (
19
19
  )
20
20
 
21
21
  # Logging services
22
- from ..adapters.logging.setup import init_logging, restart_logging
22
+ from ..adapters.logging.setup import check_logging_config, init_logging
23
23
 
24
24
  # Static conformance assertions — pyright verifies that each adapter function
25
25
  # structurally satisfies its corresponding Protocol at type-check time.
26
26
  if TYPE_CHECKING:
27
27
  from ..adapters.memory.email import EmailSpy
28
28
  from ..application.ports import (
29
+ CheckLoggingConfig,
29
30
  DeployConfiguration,
30
31
  DisplayConfig,
31
32
  GetConfig,
32
33
  GetDefaultConfigPath,
33
34
  InitLogging,
34
35
  LoadEmailConfigFromDict,
35
- RestartLogging,
36
36
  SendEmail,
37
37
  SendNotification,
38
38
  )
@@ -45,7 +45,7 @@ if TYPE_CHECKING:
45
45
  _assert_send_notification: SendNotification = send_notification
46
46
  _assert_load_email_config_from_dict: LoadEmailConfigFromDict = load_email_config_from_dict
47
47
  _assert_init_logging: InitLogging = init_logging
48
- _assert_restart_logging: RestartLogging = restart_logging
48
+ _assert_check_logging_config: CheckLoggingConfig = check_logging_config
49
49
 
50
50
 
51
51
  @dataclass(frozen=True, slots=True)
@@ -60,7 +60,7 @@ class AppServices:
60
60
  send_notification: SendNotification
61
61
  load_email_config_from_dict: LoadEmailConfigFromDict
62
62
  init_logging: InitLogging
63
- restart_logging: RestartLogging
63
+ check_logging_config: CheckLoggingConfig
64
64
 
65
65
 
66
66
  def build_production() -> AppServices:
@@ -74,7 +74,7 @@ def build_production() -> AppServices:
74
74
  send_notification=send_notification,
75
75
  load_email_config_from_dict=load_email_config_from_dict,
76
76
  init_logging=init_logging,
77
- restart_logging=restart_logging,
77
+ check_logging_config=check_logging_config,
78
78
  )
79
79
 
80
80
 
@@ -91,12 +91,12 @@ def build_testing(*, spy: EmailSpy | None = None) -> AppServices:
91
91
  """
92
92
  from ..adapters.memory import ( # noqa: PLC0415 - deferred: keeps in-memory test doubles out of the production import path
93
93
  EmailSpy,
94
+ check_logging_config_in_memory,
94
95
  deploy_configuration_in_memory,
95
96
  display_config_in_memory,
96
97
  get_config_in_memory,
97
98
  get_default_config_path_in_memory,
98
99
  init_logging_in_memory,
99
- restart_logging_in_memory,
100
100
  )
101
101
 
102
102
  email_spy = spy if spy is not None else EmailSpy()
@@ -111,7 +111,7 @@ def build_testing(*, spy: EmailSpy | None = None) -> AppServices:
111
111
  # The real translation: an in-memory one would let every CLI test skip it.
112
112
  load_email_config_from_dict=load_email_config_from_dict,
113
113
  init_logging=init_logging_in_memory,
114
- restart_logging=restart_logging_in_memory,
114
+ check_logging_config=check_logging_config_in_memory,
115
115
  )
116
116
 
117
117
 
@@ -119,13 +119,13 @@ __all__ = [
119
119
  "AppServices",
120
120
  "build_production",
121
121
  "build_testing",
122
+ "check_logging_config",
122
123
  "deploy_configuration",
123
124
  "display_config",
124
125
  "get_config",
125
126
  "get_default_config_path",
126
127
  "init_logging",
127
128
  "load_email_config_from_dict",
128
- "restart_logging",
129
129
  "send_email",
130
130
  "send_notification",
131
131
  ]
@@ -396,7 +396,7 @@ def inject_config(
396
396
  result = cli_runner.invoke(cli, ["config"], obj=factory)
397
397
  assert "key" in result.output
398
398
  """
399
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
399
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
400
400
  from bitranox_template_py_cli.composition import AppServices, build_production
401
401
 
402
402
  def _inject(config: Config) -> Callable[[], AppServices]:
@@ -415,7 +415,7 @@ def inject_config(
415
415
  # The quiet runtime: production init_logging queues INFO lines that race into
416
416
  # CliRunner's stderr, so a stderr assertion would depend on timing.
417
417
  init_logging=init_logging_in_memory,
418
- restart_logging=restart_logging_in_memory,
418
+ check_logging_config=check_logging_config_in_memory,
419
419
  )
420
420
  return lambda: test_services
421
421
 
@@ -451,7 +451,7 @@ def inject_config_with_profile_capture(
451
451
  cli_runner.invoke(cli, ["--profile", "staging", "config"], obj=factory)
452
452
  assert captured == ["staging"]
453
453
  """
454
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
454
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
455
455
  from bitranox_template_py_cli.composition import AppServices, build_production
456
456
 
457
457
  def _inject(config: Config, captured_profiles: list[str | None]) -> Callable[[], AppServices]:
@@ -471,7 +471,7 @@ def inject_config_with_profile_capture(
471
471
  # The quiet runtime: production init_logging queues INFO lines that race into
472
472
  # CliRunner's stderr, so a stderr assertion would depend on timing.
473
473
  init_logging=init_logging_in_memory,
474
- restart_logging=restart_logging_in_memory,
474
+ check_logging_config=check_logging_config_in_memory,
475
475
  )
476
476
  return lambda: test_services
477
477
 
@@ -507,7 +507,7 @@ def inject_deploy_with_profile_capture(
507
507
  cli_runner.invoke(cli, ["--profile", "prod", "config-deploy", ...], obj=factory)
508
508
  assert captured == ["prod"]
509
509
  """
510
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
510
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
511
511
  from bitranox_template_py_cli.composition import AppServices, build_production
512
512
 
513
513
  def _inject(deployed_path: Path, captured_profiles: list[str | None]) -> Callable[[], AppServices]:
@@ -536,7 +536,7 @@ def inject_deploy_with_profile_capture(
536
536
  # The quiet runtime: production init_logging queues INFO lines that race into
537
537
  # CliRunner's stderr, so a stderr assertion would depend on timing.
538
538
  init_logging=init_logging_in_memory,
539
- restart_logging=restart_logging_in_memory,
539
+ check_logging_config=check_logging_config_in_memory,
540
540
  )
541
541
  return lambda: test_services
542
542
 
@@ -568,7 +568,7 @@ def inject_deploy_configuration() -> Callable[[Callable[..., list[Path]]], Calla
568
568
  cli_runner.invoke(cli, ["config-deploy", "--target", "user"], obj=factory)
569
569
  assert len(calls) == 1
570
570
  """
571
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
571
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
572
572
  from bitranox_template_py_cli.composition import AppServices, build_production
573
573
 
574
574
  def _inject(deploy_fn: Callable[..., list[Path]]) -> Callable[[], AppServices]:
@@ -584,7 +584,7 @@ def inject_deploy_configuration() -> Callable[[Callable[..., list[Path]]], Calla
584
584
  # The quiet runtime: production init_logging queues INFO lines that race into
585
585
  # CliRunner's stderr, so a stderr assertion would depend on timing.
586
586
  init_logging=init_logging_in_memory,
587
- restart_logging=restart_logging_in_memory,
587
+ check_logging_config=check_logging_config_in_memory,
588
588
  )
589
589
  return lambda: test_services
590
590
 
@@ -670,7 +670,7 @@ def email_cli_context(
670
670
  assert ctx.spy.sent_notifications[0].subject == "Hi"
671
671
  """
672
672
  from bitranox_template_py_cli.adapters.email.config import load_email_config_from_dict
673
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
673
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
674
674
  from bitranox_template_py_cli.adapters.memory.email import EmailSpy as EmailSpyImpl
675
675
  from bitranox_template_py_cli.composition import AppServices, build_production
676
676
 
@@ -693,7 +693,7 @@ def email_cli_context(
693
693
  # The quiet runtime: production init_logging queues INFO lines that race into
694
694
  # CliRunner's stderr, so a stderr assertion would depend on timing.
695
695
  init_logging=init_logging_in_memory,
696
- restart_logging=restart_logging_in_memory,
696
+ check_logging_config=check_logging_config_in_memory,
697
697
  )
698
698
  return EmailCliContext(factory=lambda: test_services, spy=spy)
699
699
 
@@ -725,7 +725,7 @@ def config_cli_context(
725
725
  result = cli_runner.invoke(cli, ["config"], obj=factory)
726
726
  assert "key" in result.output
727
727
  """
728
- from bitranox_template_py_cli.adapters.memory import init_logging_in_memory, restart_logging_in_memory
728
+ from bitranox_template_py_cli.adapters.memory import check_logging_config_in_memory, init_logging_in_memory
729
729
  from bitranox_template_py_cli.composition import AppServices, build_production
730
730
 
731
731
  def _create(config_data: dict[str, Any]) -> Callable[[], AppServices]:
@@ -746,7 +746,7 @@ def config_cli_context(
746
746
  # The quiet runtime: production init_logging queues INFO lines that race into
747
747
  # CliRunner's stderr, so a stderr assertion would depend on timing.
748
748
  init_logging=init_logging_in_memory,
749
- restart_logging=restart_logging_in_memory,
749
+ check_logging_config=check_logging_config_in_memory,
750
750
  )
751
751
  return lambda: test_services
752
752
 
@@ -52,7 +52,7 @@ def inject_config_with_dotenv_capture(
52
52
  send_notification=prod.send_notification,
53
53
  load_email_config_from_dict=prod.load_email_config_from_dict,
54
54
  init_logging=prod.init_logging,
55
- restart_logging=prod.restart_logging,
55
+ check_logging_config=prod.check_logging_config,
56
56
  )
57
57
  return lambda: test_services
58
58
 
@@ -77,7 +77,7 @@ def inject_deploy_with_permission_capture(
77
77
  send_notification=prod.send_notification,
78
78
  load_email_config_from_dict=prod.load_email_config_from_dict,
79
79
  init_logging=prod.init_logging,
80
- restart_logging=prod.restart_logging,
80
+ check_logging_config=prod.check_logging_config,
81
81
  )
82
82
  return lambda: test_services
83
83