py-wlcli 0.8.0__py3-none-any.whl
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.
- py_wlcli-0.8.0.dist-info/METADATA +139 -0
- py_wlcli-0.8.0.dist-info/RECORD +320 -0
- py_wlcli-0.8.0.dist-info/WHEEL +4 -0
- py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
- py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
- wl_cli/__init__.py +27 -0
- wl_cli/__main__.py +130 -0
- wl_cli/capabilities.py +79 -0
- wl_cli/cc/__init__.py +5 -0
- wl_cli/cc/checks.py +1019 -0
- wl_cli/cc/cli.py +370 -0
- wl_cli/cc/mutate.py +1350 -0
- wl_cli/cli_click.py +156 -0
- wl_cli/commands/__init__.py +84 -0
- wl_cli/commands/base.py +80 -0
- wl_cli/commands/buildcommands/__init__.py +1 -0
- wl_cli/commands/buildcommands/build.py +71 -0
- wl_cli/commands/buildcommands/build_utils.py +47 -0
- wl_cli/commands/buildcommands/builders/__init__.py +18 -0
- wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
- wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
- wl_cli/commands/buildcommands/builders/environment.py +81 -0
- wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
- wl_cli/commands/buildcommands/builders/stubs.py +58 -0
- wl_cli/commands/buildcommands/dist.py +326 -0
- wl_cli/commands/buildcommands/test.py +89 -0
- wl_cli/commands/clean/__init__.py +1 -0
- wl_cli/commands/clean/build.py +151 -0
- wl_cli/commands/clean/clean.py +294 -0
- wl_cli/commands/clean/clean_utils.py +77 -0
- wl_cli/commands/clean/core.py +236 -0
- wl_cli/commands/clean/dead_code/__init__.py +0 -0
- wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
- wl_cli/commands/clean/dead_code/detector.py +161 -0
- wl_cli/commands/clean/dead_code/fixer.py +176 -0
- wl_cli/commands/clean/dead_code/reporter.py +295 -0
- wl_cli/commands/clean/dead_code/utils.py +0 -0
- wl_cli/commands/clean/lfs.py +215 -0
- wl_cli/commands/clean/python.py +81 -0
- wl_cli/commands/clean/rust.py +91 -0
- wl_cli/commands/clean/utils/__init__.py +34 -0
- wl_cli/commands/clean/venv.py +91 -0
- wl_cli/commands/cleanup.py +73 -0
- wl_cli/commands/click_base.py +79 -0
- wl_cli/commands/config/__init__.py +1 -0
- wl_cli/commands/config/config.py +291 -0
- wl_cli/commands/debug/__init__.py +6 -0
- wl_cli/commands/debug/debug.py +309 -0
- wl_cli/commands/debug/debug_cache.py +86 -0
- wl_cli/commands/debug/debug_subagent.py +156 -0
- wl_cli/commands/debug/output_formatter.py +165 -0
- wl_cli/commands/debug/source_hasher.py +92 -0
- wl_cli/commands/format/__init__.py +13 -0
- wl_cli/commands/format/format_cache_adapter.py +125 -0
- wl_cli/commands/format/format_command.py +197 -0
- wl_cli/commands/format/format_coordinator.py +191 -0
- wl_cli/commands/format/format_executor.py +470 -0
- wl_cli/commands/format/format_outcome.py +44 -0
- wl_cli/commands/format/format_path_handler.py +1016 -0
- wl_cli/commands/format/format_progress.py +62 -0
- wl_cli/commands/format/format_reporter.py +31 -0
- wl_cli/commands/format/python_formatter.py +506 -0
- wl_cli/commands/format/rust_formatter.py +106 -0
- wl_cli/commands/gate/__init__.py +1 -0
- wl_cli/commands/gate/gate_command.py +862 -0
- wl_cli/commands/gate/preflight.py +445 -0
- wl_cli/commands/git/__init__.py +1 -0
- wl_cli/commands/git/config.py +169 -0
- wl_cli/commands/git/git_facts.py +60 -0
- wl_cli/commands/git/git_flow.py +86 -0
- wl_cli/commands/git/hook.py +112 -0
- wl_cli/commands/git/policy.py +216 -0
- wl_cli/commands/git/push_update.py +82 -0
- wl_cli/commands/initenv/__init__.py +1 -0
- wl_cli/commands/initenv/common/__init__.py +23 -0
- wl_cli/commands/initenv/common/check_git_bash.py +78 -0
- wl_cli/commands/initenv/common/error_handler.py +68 -0
- wl_cli/commands/initenv/common/exceptions.py +52 -0
- wl_cli/commands/initenv/components/__init__.py +4 -0
- wl_cli/commands/initenv/components/cargo_sync.py +240 -0
- wl_cli/commands/initenv/components/config_manager.py +106 -0
- wl_cli/commands/initenv/components/directory_utils.py +71 -0
- wl_cli/commands/initenv/components/git/__init__.py +9 -0
- wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
- wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
- wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
- wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
- wl_cli/commands/initenv/components/git/git_utility.py +254 -0
- wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
- wl_cli/commands/initenv/components/git_initializer.py +212 -0
- wl_cli/commands/initenv/components/i18n_manager.py +104 -0
- wl_cli/commands/initenv/components/log_manager.py +241 -0
- wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
- wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
- wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
- wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
- wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
- wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
- wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
- wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
- wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
- wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
- wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
- wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
- wl_cli/commands/initenv/components/rust_initializer.py +145 -0
- wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
- wl_cli/commands/initenv/components/symlink_manager.py +52 -0
- wl_cli/commands/initenv/components/venv_manager.py +118 -0
- wl_cli/commands/initenv/initenv.py +169 -0
- wl_cli/commands/initenv/services/__init__.py +11 -0
- wl_cli/commands/initenv/services/environment_initializer.py +288 -0
- wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
- wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
- wl_cli/commands/lfs/README.md +221 -0
- wl_cli/commands/lfs/__init__.py +5 -0
- wl_cli/commands/lfs/cli.py +106 -0
- wl_cli/commands/lfs/config.py +164 -0
- wl_cli/commands/lfs/hook.py +61 -0
- wl_cli/commands/lfs/initializer.py +531 -0
- wl_cli/commands/lfs/manager.py +95 -0
- wl_cli/commands/lfs/policy.py +165 -0
- wl_cli/commands/lfs/watcher.py +294 -0
- wl_cli/commands/lint/__init__.py +1 -0
- wl_cli/commands/lint/lint_command.py +297 -0
- wl_cli/commands/lint/lint_executor.py +409 -0
- wl_cli/commands/lint/lint_reporter.py +579 -0
- wl_cli/commands/lint/lint_run.py +258 -0
- wl_cli/commands/lint/mypy_config.py +90 -0
- wl_cli/commands/registry.py +103 -0
- wl_cli/commands/self/__init__.py +7 -0
- wl_cli/commands/self/common/__init__.py +8 -0
- wl_cli/commands/self/common/capabilities.py +30 -0
- wl_cli/commands/self/common/config.py +27 -0
- wl_cli/commands/self/common/version_cache.py +85 -0
- wl_cli/commands/self/handlers/__init__.py +19 -0
- wl_cli/commands/self/handlers/base_handler.py +28 -0
- wl_cli/commands/self/handlers/install_handler.py +85 -0
- wl_cli/commands/self/handlers/status_handler.py +66 -0
- wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
- wl_cli/commands/self/handlers/update_handler.py +102 -0
- wl_cli/commands/self/handlers/version_handler.py +16 -0
- wl_cli/commands/self/self_command.py +237 -0
- wl_cli/commands/self/services/__init__.py +15 -0
- wl_cli/commands/self/services/installation_manager.py +187 -0
- wl_cli/commands/self/services/local_installer.py +165 -0
- wl_cli/commands/self/services/pypi_updater.py +278 -0
- wl_cli/commands/self/services/windows_update.py +174 -0
- wl_cli/commands/testcommands/__init__.py +8 -0
- wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
- wl_cli/commands/testcommands/batch_processor.py +278 -0
- wl_cli/commands/testcommands/cache_manager.py +1156 -0
- wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
- wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
- wl_cli/commands/testcommands/progress_manager.py +79 -0
- wl_cli/commands/testcommands/python_env_detector.py +207 -0
- wl_cli/commands/testcommands/report_generator.py +101 -0
- wl_cli/commands/testcommands/single_test_executor.py +250 -0
- wl_cli/commands/testcommands/speed_checker.py +118 -0
- wl_cli/commands/testcommands/test_command_builder.py +165 -0
- wl_cli/commands/testcommands/test_commands.py +786 -0
- wl_cli/commands/testcommands/test_reporter.py +181 -0
- wl_cli/commands/testcommands/test_result_handler.py +96 -0
- wl_cli/commands/testcommands/test_runner.py +1343 -0
- wl_cli/commands/testcommands/test_utils.py +197 -0
- wl_cli/commands/testcommands/utils/__init__.py +17 -0
- wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
- wl_cli/commands/testcommands/utils/env_info.py +36 -0
- wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
- wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
- wl_cli/commands/testcommands/utils/project_root.py +61 -0
- wl_cli/commands/update/__init__.py +9 -0
- wl_cli/commands/update/services/__init__.py +21 -0
- wl_cli/commands/update/services/backup_manager.py +207 -0
- wl_cli/commands/update/services/component_manager.py +83 -0
- wl_cli/commands/update/services/conflict_resolver.py +189 -0
- wl_cli/commands/update/services/vendors_updater.py +85 -0
- wl_cli/commands/update/services/version_tracker.py +127 -0
- wl_cli/commands/update/update_command.py +270 -0
- wl_cli/commands/update/utils/__init__.py +3 -0
- wl_cli/commands/version/__init__.py +9 -0
- wl_cli/commands/version/version_command.py +155 -0
- wl_cli/commands/workflow.py +477 -0
- wl_cli/env/__init__.py +5 -0
- wl_cli/env/cli.py +167 -0
- wl_cli/env_status.py +157 -0
- wl_cli/envelope.py +91 -0
- wl_cli/exceptions.py +85 -0
- wl_cli/lib/__init__.py +3 -0
- wl_cli/lib/rust_utils.py +35 -0
- wl_cli/mcp/__init__.py +6 -0
- wl_cli/mcp/capabilities.py +19 -0
- wl_cli/mcp/cli.py +22 -0
- wl_cli/mcp/gitlab.py +553 -0
- wl_cli/mcp/k8s.py +344 -0
- wl_cli/mcp/protocol.py +159 -0
- wl_cli/platform.py +139 -0
- wl_cli/quality/__init__.py +18 -0
- wl_cli/utils/__init__.py +7 -0
- wl_cli/utils/cache.py +491 -0
- wl_cli/utils/cache_ledger.py +355 -0
- wl_cli/utils/cache_store.py +546 -0
- wl_cli/utils/config/__init__.py +27 -0
- wl_cli/utils/config/config_loader.py +163 -0
- wl_cli/utils/config/core.py +862 -0
- wl_cli/utils/config/defaults.py +142 -0
- wl_cli/utils/config/env_exporter.py +86 -0
- wl_cli/utils/config/env_parser.py +108 -0
- wl_cli/utils/config/validation.py +280 -0
- wl_cli/utils/error_handler.py +165 -0
- wl_cli/utils/file_copier.py +56 -0
- wl_cli/utils/file_operations.py +506 -0
- wl_cli/utils/language_utils.py +122 -0
- wl_cli/utils/lazy_import.py +240 -0
- wl_cli/utils/log_levels.py +35 -0
- wl_cli/utils/log_rotators.py +74 -0
- wl_cli/utils/logging.py +458 -0
- wl_cli/utils/output_context.py +142 -0
- wl_cli/utils/performance/__init__.py +1 -0
- wl_cli/utils/performance/decorators.py +111 -0
- wl_cli/utils/performance/exporter.py +38 -0
- wl_cli/utils/performance/models.py +16 -0
- wl_cli/utils/performance/monitor.py +161 -0
- wl_cli/utils/performance_monitor.py +32 -0
- wl_cli/utils/platform_adapter.py +131 -0
- wl_cli/utils/pre_commit.py +266 -0
- wl_cli/utils/pre_commit_config.py +72 -0
- wl_cli/utils/root.py +521 -0
- wl_cli/utils/root_fallback.py +256 -0
- wl_cli/utils/run_evidence.py +236 -0
- wl_cli/utils/structured_logger.py +18 -0
- wl_cli/utils/structured_logging/__init__.py +19 -0
- wl_cli/utils/structured_logging/config.py +337 -0
- wl_cli/utils/structured_logging/core.py +177 -0
- wl_cli/utils/structured_logging/env.py +56 -0
- wl_cli/utils/structured_logging/filtering.py +75 -0
- wl_cli/utils/structured_logging/filters.py +55 -0
- wl_cli/utils/structured_logging/formatters.py +251 -0
- wl_cli/utils/structured_logging/handlers.py +308 -0
- wl_cli/utils/structured_logging/logger.py +354 -0
- wl_cli/utils/subprocess.py +71 -0
- wl_cli/utils/subprocess_utils.py +260 -0
- wl_cli/utils/test_utils/__init__.py +5 -0
- wl_cli/utils/test_utils/test_filter.py +36 -0
- wl_cli/utils/uv_run.py +79 -0
- wl_cli/utils/uv_tool.py +117 -0
- wl_cli/utils/uv_tree.py +71 -0
- wl_cli/utils/uv_workspace.py +174 -0
- wl_cli/utils/version/__init__.py +15 -0
- wl_cli/utils/version/pypi_version_checker.py +95 -0
- wl_cli/utils/version/version_comparator.py +133 -0
- wl_cli/utils/version/version_detectors.py +312 -0
- wl_cli/utils/version/version_service.py +131 -0
- wl_cli/utils/version/version_updater.py +198 -0
- wl_cli/utils/wl_dir_updater.py +1440 -0
- wl_cli/utils/workspace_detector/__init__.py +28 -0
- wl_cli/utils/workspace_detector/core.py +161 -0
- wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
- wl_cli/utils/workspace_detector/exceptions.py +5 -0
- wl_cli/utils/workspace_detector/types.py +17 -0
- wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
- wl_cli/vendors/config/.codespellrc +2 -0
- wl_cli/vendors/config/dependencies.toml +10 -0
- wl_cli/vendors/config/mypy.ini +62 -0
- wl_cli/vendors/config/pyproject_defaults.toml +28 -0
- wl_cli/vendors/config/template_config.toml +24 -0
- wl_cli/vendors/config/tool_configs.toml +76 -0
- wl_cli/vendors/git/.gitignore +122 -0
- wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
- wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/post-update.sample +8 -0
- wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/pre-commit +45 -0
- wl_cli/vendors/hooks/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/pre-push.sample +60 -0
- wl_cli/vendors/hooks/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
- wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
- wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
- wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
- wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/unix/post-update.sample +8 -0
- wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/unix/pre-commit +190 -0
- wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
- wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/unix/pre-push +417 -0
- wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
- wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/update.sample +128 -0
- wl_cli/vendors/hooks/win/work.bat +63 -0
- wl_cli/vendors/project/.gitignore +90 -0
- wl_cli/vendors/project/.python-version +1 -0
- wl_cli/vendors/project/LICENSE +21 -0
- wl_cli/vendors/project/README.md +0 -0
- wl_cli/vendors/project/pyproject.toml +27 -0
- wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
- wl_cli/vendors/project/pyproject_workspace.toml +14 -0
- wl_cli/vendors/readme/README.md +120 -0
- wl_cli/vendors/rust/Cargo.toml +14 -0
- wl_cli/vendors/rust/lib/__init__.py +3 -0
- wl_cli/vendors/rust/lib/rust_utils.py +31 -0
- wl_cli/vendors/rust/src/lib.rs +13 -0
- wl_cli/vendors/rust/src/main.rs +3 -0
- wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
- wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
- wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
- wl_cli/vendors/sop_doc/python-sop.md +342 -0
- wl_cli/vendors/sop_doc/rust-readme.md +219 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Default configuration constants and helper functions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from wl_cli import __version__
|
|
7
|
+
|
|
8
|
+
# Inlined from .core constants to avoid circular import (core -> defaults -> core)
|
|
9
|
+
ENVIRONMENT_DEVELOPMENT = "development"
|
|
10
|
+
LOG_DIRECTORY = "log"
|
|
11
|
+
LOG_FILE_NAME = "wl_action.log"
|
|
12
|
+
# This is the **single** definition of the ``.wl`` directory name (#119). A second
|
|
13
|
+
# copy used to live in ``core``; the deploy-root path now derives its ``.wl`` from
|
|
14
|
+
# ``Root.wl_dir()`` instead, and ``config_loader`` imports this name for the
|
|
15
|
+
# user-level ``~/.wl/config.json``. It has to stay here rather than in ``core``:
|
|
16
|
+
# this module is the leaf of the ``core -> defaults`` dependency, so only it can
|
|
17
|
+
# be imported by both.
|
|
18
|
+
WL_DIRECTORY = ".wl"
|
|
19
|
+
|
|
20
|
+
DEFAULT_LOG_LEVEL = "INFO"
|
|
21
|
+
|
|
22
|
+
DEFAULT_LOG_FILE = os.path.join(WL_DIRECTORY, LOG_DIRECTORY, LOG_FILE_NAME)
|
|
23
|
+
|
|
24
|
+
DEFAULT_LOG_CONSOLE = False
|
|
25
|
+
|
|
26
|
+
DEFAULT_LOG_CONSOLE_FORMAT = "colored"
|
|
27
|
+
|
|
28
|
+
DEFAULT_LOG_FILE_FORMAT = "human"
|
|
29
|
+
|
|
30
|
+
DEFAULT_LOG_MAX_SIZE = 10 * 1024 * 1024
|
|
31
|
+
|
|
32
|
+
DEFAULT_LOG_MAX_BACKUPS = 5
|
|
33
|
+
|
|
34
|
+
DEFAULT_LOG_ROTATE_DAYS = 7
|
|
35
|
+
|
|
36
|
+
DEFAULT_LANGUAGE = "auto"
|
|
37
|
+
|
|
38
|
+
DEFAULT_ALIASES: dict[str, str] = {}
|
|
39
|
+
|
|
40
|
+
DEFAULT_VERSION = __version__
|
|
41
|
+
|
|
42
|
+
DEFAULT_ENVIRONMENT = ENVIRONMENT_DEVELOPMENT
|
|
43
|
+
|
|
44
|
+
DEFAULT_COVERAGE_FAIL_UNDER = 70.0
|
|
45
|
+
DEFAULT_COVERAGE_CACHE_ENABLED = True
|
|
46
|
+
DEFAULT_COVERAGE_PARALLEL_WORKERS = None
|
|
47
|
+
|
|
48
|
+
# Environment-specific defaults
|
|
49
|
+
ENVIRONMENT_DEFAULTS = {
|
|
50
|
+
"development": {
|
|
51
|
+
"log_level": "DEBUG",
|
|
52
|
+
"log_console": True,
|
|
53
|
+
"log_console_format": "colored",
|
|
54
|
+
"log_file_format": "human",
|
|
55
|
+
},
|
|
56
|
+
"production": {
|
|
57
|
+
"log_level": "INFO",
|
|
58
|
+
"log_console": False,
|
|
59
|
+
"log_console_format": "compact",
|
|
60
|
+
"log_file_format": "json",
|
|
61
|
+
},
|
|
62
|
+
"testing": {
|
|
63
|
+
"log_level": "WARNING",
|
|
64
|
+
"log_console": False,
|
|
65
|
+
"log_file_format": "human",
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
#: Config keys whose value is *derived from the running environment* at load
|
|
71
|
+
#: time (see ``ConfigLoader.get_environment``) rather than owned by the project.
|
|
72
|
+
#:
|
|
73
|
+
#: ``environment`` looked like a harmless default, but ``load_all`` gives the
|
|
74
|
+
#: file's value precedence over detection -- so writing the derived value into a
|
|
75
|
+
#: brand-new ``config.json`` silently turns a derivation into a permanent
|
|
76
|
+
#: override. A ``wl`` sync that first ran under pytest (``testing``) or in CI
|
|
77
|
+
#: (``production``) froze that value forever, because the version stamp then
|
|
78
|
+
#: certified the file as current and nothing ever re-derived it (#208).
|
|
79
|
+
RUNTIME_DERIVED_KEYS = frozenset({"environment"})
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_default_config() -> dict[str, Any]:
|
|
83
|
+
"""Get default configuration dictionary.
|
|
84
|
+
|
|
85
|
+
This is the shape of the *merged* runtime configuration, so it still names
|
|
86
|
+
``environment``: readers need a value even when no file exists. Use
|
|
87
|
+
:func:`get_creation_defaults` when writing a new file -- a derived value
|
|
88
|
+
must not be persisted as if it were a project setting (#208).
|
|
89
|
+
"""
|
|
90
|
+
return {
|
|
91
|
+
"environment": DEFAULT_ENVIRONMENT,
|
|
92
|
+
"log_level": DEFAULT_LOG_LEVEL,
|
|
93
|
+
"log_file": DEFAULT_LOG_FILE,
|
|
94
|
+
"log_console": DEFAULT_LOG_CONSOLE,
|
|
95
|
+
"log_console_format": DEFAULT_LOG_CONSOLE_FORMAT,
|
|
96
|
+
"log_file_format": DEFAULT_LOG_FILE_FORMAT,
|
|
97
|
+
"log_max_size": DEFAULT_LOG_MAX_SIZE,
|
|
98
|
+
"log_max_backups": DEFAULT_LOG_MAX_BACKUPS,
|
|
99
|
+
"log_rotate_days": DEFAULT_LOG_ROTATE_DAYS,
|
|
100
|
+
"language": DEFAULT_LANGUAGE,
|
|
101
|
+
"aliases": DEFAULT_ALIASES,
|
|
102
|
+
"version": DEFAULT_VERSION,
|
|
103
|
+
"test_speed": {
|
|
104
|
+
"enabled": True,
|
|
105
|
+
"threshold": 5.0,
|
|
106
|
+
"fail_on_timeout": True,
|
|
107
|
+
},
|
|
108
|
+
"coverage": {
|
|
109
|
+
"fail_under": DEFAULT_COVERAGE_FAIL_UNDER,
|
|
110
|
+
"cache_enabled": DEFAULT_COVERAGE_CACHE_ENABLED,
|
|
111
|
+
"parallel_workers": DEFAULT_COVERAGE_PARALLEL_WORKERS,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_environment_defaults(environment: str) -> dict[str, Any]:
|
|
117
|
+
"""Get default configuration for specific environment."""
|
|
118
|
+
return ENVIRONMENT_DEFAULTS.get(environment, {})
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_creation_defaults() -> dict[str, Any]:
|
|
122
|
+
"""Payload written when a brand-new ``.wl/config.json`` is created (#208).
|
|
123
|
+
|
|
124
|
+
The distinction this draws is between a *creation default* -- a value the
|
|
125
|
+
project owns from the moment the file appears -- and a *runtime-derived*
|
|
126
|
+
value, which belongs to the process that is running, not to the checkout.
|
|
127
|
+
Persisting the latter is what made the force-write of #202 a trap: the file
|
|
128
|
+
was created under pytest with ``environment=testing`` and every later run
|
|
129
|
+
read that back as an explicit setting.
|
|
130
|
+
|
|
131
|
+
Log-related keys are deliberately *kept*: they are static defaults, and
|
|
132
|
+
``ConfigLoader.load_all`` re-applies the environment's ``log_*`` defaults on
|
|
133
|
+
every load anyway, so they can never pin a run mode.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
A fresh dict of the JSON payload for a new config file.
|
|
137
|
+
"""
|
|
138
|
+
return {
|
|
139
|
+
key: value
|
|
140
|
+
for key, value in get_default_config().items()
|
|
141
|
+
if key not in RUNTIME_DERIVED_KEYS
|
|
142
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Environment variable exporter for WL Commands."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from .defaults import DEFAULT_LANGUAGE, DEFAULT_LOG_FILE, get_environment_defaults
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .config_loader import ConfigLoader
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EnvExporter:
|
|
13
|
+
"""Export configuration as environment variables.
|
|
14
|
+
|
|
15
|
+
EnvExporter handles converting configuration dictionary to properly formatted
|
|
16
|
+
environment variables and generates environment templates for different
|
|
17
|
+
environments.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config_loader: "ConfigLoader") -> None:
|
|
21
|
+
"""Initialize environment exporter.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config_loader: Config loader instance to get environment from.
|
|
25
|
+
"""
|
|
26
|
+
self._loader = config_loader
|
|
27
|
+
|
|
28
|
+
def export_environment_variables(
|
|
29
|
+
self, config: dict[str, Any], environment: str | None = None
|
|
30
|
+
) -> dict[str, str]:
|
|
31
|
+
"""Export current configuration as environment variables.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
config: Configuration dictionary to export.
|
|
35
|
+
environment: Target environment. If None, gets from ConfigLoader.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dictionary of environment variables with WL_ prefix.
|
|
39
|
+
"""
|
|
40
|
+
if environment is None:
|
|
41
|
+
environment = self._loader.get_environment()
|
|
42
|
+
|
|
43
|
+
env_vars = {}
|
|
44
|
+
|
|
45
|
+
# Convert config to environment variables
|
|
46
|
+
for key, value in config.items():
|
|
47
|
+
if key == "environment":
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
env_key = f"WL_{key.upper()}"
|
|
51
|
+
# Convert value to string
|
|
52
|
+
if isinstance(value, bool):
|
|
53
|
+
env_vars[env_key] = str(value).lower()
|
|
54
|
+
elif isinstance(value, list | dict):
|
|
55
|
+
env_vars[env_key] = json.dumps(value)
|
|
56
|
+
else:
|
|
57
|
+
env_vars[env_key] = str(value)
|
|
58
|
+
|
|
59
|
+
# Add environment variable
|
|
60
|
+
env_vars["WL_ENVIRONMENT"] = environment
|
|
61
|
+
|
|
62
|
+
return env_vars
|
|
63
|
+
|
|
64
|
+
def get_environment_template(self, environment: str) -> dict[str, str]:
|
|
65
|
+
"""Get environment variable template for specific environment.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
environment: Target environment for template.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dictionary of template environment variables with default values.
|
|
72
|
+
"""
|
|
73
|
+
env_defaults = get_environment_defaults(environment)
|
|
74
|
+
template = {}
|
|
75
|
+
|
|
76
|
+
# Add environment-specific defaults
|
|
77
|
+
for key, value in env_defaults.items():
|
|
78
|
+
env_key = f"WL_{key.upper()}"
|
|
79
|
+
template[env_key] = str(value)
|
|
80
|
+
|
|
81
|
+
# Add common variables
|
|
82
|
+
template["WL_ENVIRONMENT"] = environment
|
|
83
|
+
template["WL_LOG_FILE"] = DEFAULT_LOG_FILE
|
|
84
|
+
template["WL_LANGUAGE"] = DEFAULT_LANGUAGE
|
|
85
|
+
|
|
86
|
+
return template
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Environment variable parsing utilities for configuration."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EnvConfigParser:
|
|
9
|
+
"""Parse environment variables into configuration values."""
|
|
10
|
+
|
|
11
|
+
ENV_PREFIX = "WL_"
|
|
12
|
+
|
|
13
|
+
def parse_env_value(self, value: str) -> Any:
|
|
14
|
+
"""Parse environment variable value to appropriate Python type.
|
|
15
|
+
|
|
16
|
+
Handles: bool, int, float, JSON lists, JSON dicts, falls back to string.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
value: Raw string value from environment variable
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Parsed value with appropriate type
|
|
23
|
+
"""
|
|
24
|
+
# Handle boolean values
|
|
25
|
+
lower = value.lower()
|
|
26
|
+
if lower in ("true", "1", "yes", "y", "on"):
|
|
27
|
+
return True
|
|
28
|
+
if lower in ("false", "0", "no", "n", "off"):
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
# Handle numeric values
|
|
32
|
+
if value.isdigit():
|
|
33
|
+
return int(value)
|
|
34
|
+
if value.replace(".", "", 1).isdigit():
|
|
35
|
+
try:
|
|
36
|
+
return float(value)
|
|
37
|
+
except ValueError:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Handle lists
|
|
41
|
+
if value.startswith("[") and value.endswith("]"):
|
|
42
|
+
try:
|
|
43
|
+
return json.loads(value)
|
|
44
|
+
except json.JSONDecodeError:
|
|
45
|
+
# Fallback to simple comma-separated parsing
|
|
46
|
+
items = value[1:-1].split(",")
|
|
47
|
+
return [item.strip() for item in items if item.strip()]
|
|
48
|
+
|
|
49
|
+
# Handle dictionaries
|
|
50
|
+
if value.startswith("{") and value.endswith("}"):
|
|
51
|
+
try:
|
|
52
|
+
return json.loads(value)
|
|
53
|
+
except json.JSONDecodeError:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
# Return as string if no other type matches
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
def group_env_variables(self, env_config: dict[str, Any]) -> dict[str, Any]:
|
|
60
|
+
"""Group environment variables by prefix for nested configuration.
|
|
61
|
+
|
|
62
|
+
Converts WL_LOG_MAX_SIZE → log.max_size. Only groups when there are
|
|
63
|
+
more than two parts separated by underscore. Keeps original keys for
|
|
64
|
+
backward compatibility.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
env_config: Dictionary with environment parsed configuration
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Grouped configuration with nested dictionaries
|
|
71
|
+
"""
|
|
72
|
+
grouped = env_config.copy()
|
|
73
|
+
|
|
74
|
+
# Group variables with underscore separators (only for nested groups)
|
|
75
|
+
# Keep original keys for backward compatibility
|
|
76
|
+
for key, value in env_config.items():
|
|
77
|
+
if "_" in key:
|
|
78
|
+
parts = key.split("_")
|
|
79
|
+
if len(parts) > 1:
|
|
80
|
+
# Only group if we have more than 2 parts (e.g., log_max_size -> log.max_size)
|
|
81
|
+
if len(parts) > 2:
|
|
82
|
+
group = parts[0]
|
|
83
|
+
subgroup_key = "_".join(parts[1:])
|
|
84
|
+
|
|
85
|
+
if group not in grouped:
|
|
86
|
+
grouped[group] = {}
|
|
87
|
+
grouped[group][subgroup_key] = value
|
|
88
|
+
|
|
89
|
+
return grouped
|
|
90
|
+
|
|
91
|
+
def load_from_env(self) -> dict[str, Any]:
|
|
92
|
+
"""Load configuration from environment variables with WL_ prefix.
|
|
93
|
+
|
|
94
|
+
Environment variables starting with WL_ are converted:
|
|
95
|
+
WL_LOG_LEVEL → log_level → log.level after grouping.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Configuration dictionary loaded from environment
|
|
99
|
+
"""
|
|
100
|
+
env_config: dict[str, Any] = {}
|
|
101
|
+
|
|
102
|
+
for key, value in os.environ.items():
|
|
103
|
+
if key.startswith(self.ENV_PREFIX):
|
|
104
|
+
config_key = key[len(self.ENV_PREFIX) :].lower()
|
|
105
|
+
parsed_value = self.parse_env_value(value)
|
|
106
|
+
env_config[config_key] = parsed_value
|
|
107
|
+
|
|
108
|
+
return self.group_env_variables(env_config)
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Configuration validation utilities."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
6
|
+
|
|
7
|
+
VALID_CONSOLE_FORMATS = ["colored", "json"]
|
|
8
|
+
|
|
9
|
+
VALID_FILE_FORMATS = ["json", "human"]
|
|
10
|
+
|
|
11
|
+
VALID_CACHE_BACKENDS = ["memory", "disk"]
|
|
12
|
+
|
|
13
|
+
VALID_CONFIG_FORMATS = ["json", "toml", "yaml"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def validate_log_level(log_level: Any) -> str:
|
|
17
|
+
"""Validate log level value."""
|
|
18
|
+
if not isinstance(log_level, str):
|
|
19
|
+
return "INFO"
|
|
20
|
+
return log_level.upper() if log_level.upper() in VALID_LOG_LEVELS else "INFO"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_log_max_size(size: Any) -> int:
|
|
24
|
+
"""Validate log max size value."""
|
|
25
|
+
return size if isinstance(size, int) and size > 0 else 10 * 1024 * 1024
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_log_max_backups(count: Any) -> int:
|
|
29
|
+
"""Validate log max backups value."""
|
|
30
|
+
return count if isinstance(count, int) and count >= 0 else 5
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate_log_rotate_days(days: Any) -> int:
|
|
34
|
+
"""Validate log rotate days value."""
|
|
35
|
+
return days if isinstance(days, int) and days > 0 else 7
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def validate_log_console(value: Any) -> bool:
|
|
39
|
+
"""Validate log console value."""
|
|
40
|
+
return validate_boolean(value)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def validate_log_console_format(fmt: Any) -> str:
|
|
44
|
+
"""Validate log console format value."""
|
|
45
|
+
if not isinstance(fmt, str):
|
|
46
|
+
return "colored"
|
|
47
|
+
return fmt.lower() if fmt.lower() in VALID_CONSOLE_FORMATS else "colored"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def validate_log_file_format(fmt: Any) -> str:
|
|
51
|
+
"""Validate log file format value."""
|
|
52
|
+
if not isinstance(fmt, str):
|
|
53
|
+
return "human"
|
|
54
|
+
return fmt.lower() if fmt.lower() in VALID_FILE_FORMATS else "human"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_cache_backend(backend: Any) -> str:
|
|
58
|
+
"""Validate cache backend value."""
|
|
59
|
+
if not isinstance(backend, str):
|
|
60
|
+
return "memory"
|
|
61
|
+
return backend.lower() if backend.lower() in VALID_CACHE_BACKENDS else "memory"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate_cache_ttl(ttl: Any) -> int:
|
|
65
|
+
"""Validate cache TTL value."""
|
|
66
|
+
return ttl if isinstance(ttl, int) and ttl > 0 else 3600
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def validate_config_format(fmt: Any) -> str:
|
|
70
|
+
"""Validate config format value."""
|
|
71
|
+
if not isinstance(fmt, str):
|
|
72
|
+
return "json"
|
|
73
|
+
return fmt.lower() if fmt.lower() in VALID_CONFIG_FORMATS else "json"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def validate_boolean(value: Any) -> bool:
|
|
77
|
+
"""Validate boolean value."""
|
|
78
|
+
if isinstance(value, bool):
|
|
79
|
+
return value
|
|
80
|
+
if isinstance(value, str):
|
|
81
|
+
normalized_value = value.lower().strip()
|
|
82
|
+
return normalized_value in ["true", "1", "yes", "y", "on"]
|
|
83
|
+
if isinstance(value, int):
|
|
84
|
+
return value > 0
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def validate_integer(value: Any, min_value: int | None = None) -> int:
|
|
89
|
+
"""Validate integer value."""
|
|
90
|
+
if isinstance(value, int):
|
|
91
|
+
if min_value is not None and value < min_value:
|
|
92
|
+
return min_value
|
|
93
|
+
return value
|
|
94
|
+
try:
|
|
95
|
+
int_value = int(value)
|
|
96
|
+
if min_value is not None and int_value < min_value:
|
|
97
|
+
return min_value
|
|
98
|
+
return int_value
|
|
99
|
+
except (ValueError, TypeError):
|
|
100
|
+
return min_value if min_value is not None else 0
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def validate_string(value: Any, default: str = "") -> str:
|
|
104
|
+
"""Validate string value."""
|
|
105
|
+
return str(value) if value is not None else default
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _validate_top_level_logging(config: dict[str, Any]) -> dict[str, Any]:
|
|
109
|
+
"""Validate top-level logging configuration values."""
|
|
110
|
+
validated = config.copy()
|
|
111
|
+
|
|
112
|
+
if "log_level" in validated:
|
|
113
|
+
validated["log_level"] = validate_log_level(validated["log_level"])
|
|
114
|
+
if "log_max_size" in validated:
|
|
115
|
+
validated["log_max_size"] = validate_log_max_size(validated["log_max_size"])
|
|
116
|
+
if "log_max_backups" in validated:
|
|
117
|
+
validated["log_max_backups"] = validate_log_max_backups(
|
|
118
|
+
validated["log_max_backups"]
|
|
119
|
+
)
|
|
120
|
+
if "log_rotate_days" in validated:
|
|
121
|
+
validated["log_rotate_days"] = validate_log_rotate_days(
|
|
122
|
+
validated["log_rotate_days"]
|
|
123
|
+
)
|
|
124
|
+
if "log_console" in validated:
|
|
125
|
+
validated["log_console"] = validate_log_console(validated["log_console"])
|
|
126
|
+
if "log_console_format" in validated:
|
|
127
|
+
validated["log_console_format"] = validate_log_console_format(
|
|
128
|
+
validated["log_console_format"]
|
|
129
|
+
)
|
|
130
|
+
if "log_file_format" in validated:
|
|
131
|
+
validated["log_file_format"] = validate_log_file_format(
|
|
132
|
+
validated["log_file_format"]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return validated
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _validate_top_level_cache(config: dict[str, Any]) -> dict[str, Any]:
|
|
139
|
+
"""Validate top-level cache configuration values."""
|
|
140
|
+
validated = config.copy()
|
|
141
|
+
|
|
142
|
+
if "cache_backend" in validated:
|
|
143
|
+
validated["cache_backend"] = validate_cache_backend(validated["cache_backend"])
|
|
144
|
+
if "cache_ttl" in validated:
|
|
145
|
+
validated["cache_ttl"] = validate_cache_ttl(validated["cache_ttl"])
|
|
146
|
+
|
|
147
|
+
return validated
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _validate_top_level_general(config: dict[str, Any]) -> dict[str, Any]:
|
|
151
|
+
"""Validate top-level general configuration values."""
|
|
152
|
+
validated = config.copy()
|
|
153
|
+
|
|
154
|
+
if "config_format" in validated:
|
|
155
|
+
validated["config_format"] = validate_config_format(validated["config_format"])
|
|
156
|
+
|
|
157
|
+
return validated
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def validate_config(config: dict[str, Any]) -> dict[str, Any]:
|
|
161
|
+
"""Validate and normalize configuration values."""
|
|
162
|
+
validated = config.copy()
|
|
163
|
+
|
|
164
|
+
# Validate top-level configurations
|
|
165
|
+
validated = _validate_top_level_logging(validated)
|
|
166
|
+
validated = _validate_top_level_cache(validated)
|
|
167
|
+
validated = _validate_top_level_general(validated)
|
|
168
|
+
|
|
169
|
+
# Validate nested configurations
|
|
170
|
+
if "logging" in validated and isinstance(validated["logging"], dict):
|
|
171
|
+
validated["logging"] = validate_logging_config(validated["logging"])
|
|
172
|
+
if "cache" in validated and isinstance(validated["cache"], dict):
|
|
173
|
+
validated["cache"] = validate_cache_config(validated["cache"])
|
|
174
|
+
if "test_speed" in validated and isinstance(validated["test_speed"], dict):
|
|
175
|
+
validated["test_speed"] = validate_test_speed_config(validated["test_speed"])
|
|
176
|
+
|
|
177
|
+
return validated
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def validate_logging_config(logging_config: dict[str, Any]) -> dict[str, Any]:
|
|
181
|
+
"""Validate logging configuration."""
|
|
182
|
+
validated = logging_config.copy()
|
|
183
|
+
|
|
184
|
+
if "level" in validated:
|
|
185
|
+
validated["level"] = validate_log_level(validated["level"])
|
|
186
|
+
if "console" in validated:
|
|
187
|
+
validated["console"] = validate_boolean(validated["console"])
|
|
188
|
+
if "console_format" in validated:
|
|
189
|
+
validated["console_format"] = validate_log_console_format(
|
|
190
|
+
validated["console_format"]
|
|
191
|
+
)
|
|
192
|
+
if "file_format" in validated:
|
|
193
|
+
validated["file_format"] = validate_log_file_format(validated["file_format"])
|
|
194
|
+
if "max_size" in validated:
|
|
195
|
+
validated["max_size"] = validate_log_max_size(validated["max_size"])
|
|
196
|
+
if "max_backups" in validated:
|
|
197
|
+
validated["max_backups"] = validate_log_max_backups(validated["max_backups"])
|
|
198
|
+
if "rotate_days" in validated:
|
|
199
|
+
validated["rotate_days"] = validate_log_rotate_days(validated["rotate_days"])
|
|
200
|
+
|
|
201
|
+
return validated
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def validate_cache_config(cache_config: dict[str, Any]) -> dict[str, Any]:
|
|
205
|
+
"""Validate cache configuration."""
|
|
206
|
+
validated = cache_config.copy()
|
|
207
|
+
|
|
208
|
+
if "backend" in validated:
|
|
209
|
+
validated["backend"] = validate_cache_backend(validated["backend"])
|
|
210
|
+
if "ttl" in validated:
|
|
211
|
+
validated["ttl"] = validate_cache_ttl(validated["ttl"])
|
|
212
|
+
if "enabled" in validated:
|
|
213
|
+
validated["enabled"] = validate_boolean(validated["enabled"])
|
|
214
|
+
|
|
215
|
+
return validated
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def validate_test_speed_config(test_speed_config: dict[str, Any]) -> dict[str, Any]:
|
|
219
|
+
"""Validate test speed configuration."""
|
|
220
|
+
validated = test_speed_config.copy()
|
|
221
|
+
|
|
222
|
+
if "enabled" in validated:
|
|
223
|
+
validated["enabled"] = validate_boolean(validated["enabled"])
|
|
224
|
+
else:
|
|
225
|
+
validated["enabled"] = True
|
|
226
|
+
|
|
227
|
+
if "threshold" in validated:
|
|
228
|
+
if (
|
|
229
|
+
isinstance(validated["threshold"], int | float)
|
|
230
|
+
and validated["threshold"] > 0
|
|
231
|
+
):
|
|
232
|
+
validated["threshold"] = float(validated["threshold"])
|
|
233
|
+
else:
|
|
234
|
+
validated["threshold"] = 5.0
|
|
235
|
+
else:
|
|
236
|
+
validated["threshold"] = 5.0
|
|
237
|
+
|
|
238
|
+
if "fail_on_timeout" in validated:
|
|
239
|
+
validated["fail_on_timeout"] = validate_boolean(validated["fail_on_timeout"])
|
|
240
|
+
else:
|
|
241
|
+
validated["fail_on_timeout"] = True
|
|
242
|
+
|
|
243
|
+
return validated
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def validate_config_schema(
|
|
247
|
+
config: dict[str, Any], schema: dict[str, Any]
|
|
248
|
+
) -> dict[str, Any]:
|
|
249
|
+
"""Validate configuration against a schema."""
|
|
250
|
+
validated = config.copy()
|
|
251
|
+
|
|
252
|
+
for key, schema_info in schema.items():
|
|
253
|
+
if key in validated:
|
|
254
|
+
value = validated[key]
|
|
255
|
+
validator = schema_info.get("validator")
|
|
256
|
+
if validator:
|
|
257
|
+
validated[key] = validator(value)
|
|
258
|
+
else:
|
|
259
|
+
# Set default value if not present
|
|
260
|
+
validated[key] = schema_info.get("default")
|
|
261
|
+
|
|
262
|
+
return validated
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Default configuration schema
|
|
266
|
+
DEFAULT_CONFIG_SCHEMA = {
|
|
267
|
+
"log_level": {"validator": validate_log_level, "default": "INFO"},
|
|
268
|
+
"log_console": {"validator": validate_boolean, "default": True},
|
|
269
|
+
"log_console_format": {
|
|
270
|
+
"validator": validate_log_console_format,
|
|
271
|
+
"default": "colored",
|
|
272
|
+
},
|
|
273
|
+
"log_file_format": {"validator": validate_log_file_format, "default": "human"},
|
|
274
|
+
"log_max_size": {"validator": validate_log_max_size, "default": 10 * 1024 * 1024},
|
|
275
|
+
"log_max_backups": {"validator": validate_log_max_backups, "default": 5},
|
|
276
|
+
"log_rotate_days": {"validator": validate_log_rotate_days, "default": 7},
|
|
277
|
+
"cache_backend": {"validator": validate_cache_backend, "default": "memory"},
|
|
278
|
+
"cache_ttl": {"validator": validate_cache_ttl, "default": 3600},
|
|
279
|
+
"config_format": {"validator": validate_config_format, "default": "json"},
|
|
280
|
+
}
|