pytest-dynamic-params 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ProgrammerChengPei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-dynamic-params
3
+ Version: 0.1.0
4
+ Summary: Dynamic parameters plugin for pytest
5
+ Author-email: Your Name <your.email@example.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 ProgrammerChengPei
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Classifier: Framework :: Pytest
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: Operating System :: OS Independent
31
+ Requires-Python: >=3.8
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE
34
+ Requires-Dist: pytest
35
+ Provides-Extra: dev
36
+ Requires-Dist: black; extra == "dev"
37
+ Requires-Dist: flake8; extra == "dev"
38
+ Requires-Dist: isort; extra == "dev"
39
+ Requires-Dist: mypy; extra == "dev"
40
+ Requires-Dist: bandit; extra == "dev"
41
+ Requires-Dist: pre-commit; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # pytest-dynamic-params
45
+
46
+ pytest-dynamic-params 是一个为 pytest 设计的动态参数化插件,通过声明式参数生成器自动处理依赖解析和参数化,支持懒加载、缓存机制和多作用域管理,与现有 pytest 机制无缝集成,简化复杂测试场景的参数管理,提高测试效率和可维护性。
47
+
48
+ ## 特性
49
+
50
+ - **声明式参数生成**:使用装饰器定义参数生成器,专注于"生成什么"而非"如何生成"
51
+ - **自动依赖管理**:系统自动分析生成器函数的参数签名,确定依赖关系并按正确顺序执行
52
+ - **与现有机制兼容**:完全兼容现有的pytest机制,包括`@pytest.mark.parametrize`装饰器和fixture系统
53
+ - **性能优化**:支持懒加载和缓存机制,提高测试执行效率
54
+ - **灵活的作用域管理**:支持function、class、module、session四种作用域
55
+ - **智能错误检测**:自动检测并报告参数生成器之间的循环依赖
56
+ - **可配置性**:支持多种配置选项,满足不同场景需求
57
+
58
+ ## 安装
59
+
60
+ ```bash
61
+ pip install -e .
62
+ ```
63
+
64
+ ## 快速开始
65
+
66
+ 安装后,您可以立即在测试中使用动态参数装饰器:
67
+
68
+ ```python
69
+ from dynamic_params import param_generator, with_dynamic_params
70
+
71
+ @param_generator
72
+ def calculate_result(input_value):
73
+ return input_value * 2
74
+
75
+ @with_dynamic_params(result=calculate_result)
76
+ @pytest.mark.parametrize("input_value", [1, 2, 3])
77
+ def test_basic(input_value, result):
78
+ assert result == input_value * 2
79
+ ```
80
+
81
+ ## 核心概念
82
+
83
+ - **参数生成器**:使用`@param_generator`装饰器定义的函数,用于生成参数值
84
+ - **动态参数**:在运行时生成的参数,可以依赖其他参数或fixture
85
+ - **参数依赖**:参数之间的依赖关系,系统自动按依赖顺序生成参数
86
+ - **懒加载**:推迟参数生成到实际需要时执行,避免不必要的计算
87
+ - **缓存机制**:缓存参数生成结果,避免重复计算,提高性能
88
+ - **作用域管理**:控制参数生成器实例的生命周期,影响缓存和执行时机
89
+
90
+ ## 使用示例
91
+
92
+ 完整的使用示例请参见:
93
+ - `examples/basic_usage.py` - 基础用法示例(可直接复制使用)
94
+ - `examples/advanced_usage.py` - 高级用法示例(包含作用域、缓存和懒加载)
95
+ - `docs/usage-guide.md` - 详细使用指南
96
+
97
+ ## 配置
98
+
99
+ 可以在`pytest.ini`中配置插件行为:
100
+
101
+ ```ini
102
+ [pytest]
103
+ # 动态参数化系统配置
104
+ dynamic_param_cache_enabled = true
105
+ dynamic_param_validation = strict
106
+ dynamic_param_log_level = INFO
107
+
108
+ # 缓存大小配置
109
+ dynamic_param_cache_size_function = 1000
110
+ dynamic_param_cache_size_class = 500
111
+ dynamic_param_cache_size_module = 200
112
+ dynamic_param_cache_size_session = 100
113
+
114
+ # 性能配置
115
+ dynamic_param_lazy_loading = true
116
+ dynamic_param_incremental_generation = true
117
+
118
+ # 测试标记
119
+ markers =
120
+ dynamic_param: 使用动态参数的测试
121
+ param_generator: 参数生成器函数
122
+ ```
123
+
124
+ 也可以通过环境变量配置:
125
+ - `PYTEST_DYNAMIC_PARAM_CACHE`: 控制缓存是否启用
126
+ - `PYTEST_DYNAMIC_PARAM_VALIDATION`: 验证级别
127
+ - `PYTEST_DYNAMIC_PARAM_LOG_LEVEL`: 日志级别
128
+ - `PYTEST_DYNAMIC_PARAM_LAZY_LOADING`: 懒加载设置
129
+ - `PYTEST_DYNAMIC_PARAM_INCREMENTAL`: 增量生成设置
130
+ - `PYTEST_DYNAMIC_PARAM_DEBUG`: 调试模式
131
+ - `PYTEST_DYNAMIC_PARAM_PROFILE`: 性能分析
132
+ - `PYTEST_DYNAMIC_PARAM_CACHE_DIR`: 缓存目录
133
+
134
+ ## 项目结构
135
+
136
+ 项目的主要组成部分:
137
+
138
+ - `src/` - 源代码
139
+ - `tests/` - 测试代码
140
+ - `examples/` - 使用示例
141
+ - `docs/` - 文档
142
+ - `specs/` - 项目规格说明
143
+ - `reports/` - 测试报告
144
+
145
+ 有关详细的源码架构说明,请参见 [docs/structure.md](./docs/structure.md)。
146
+
147
+ ## 运行测试
148
+
149
+ ```bash
150
+ # 运行单元测试
151
+ python -m pytest tests/unit/
152
+
153
+ # 运行功能测试
154
+ python -m pytest tests/functional/
155
+
156
+ # 运行集成测试
157
+ python -m pytest tests/integration/
158
+
159
+ # 运行性能测试
160
+ python -m pytest tests/performance/
161
+
162
+ # 运行所有测试
163
+ python -m pytest tests/
164
+
165
+ # 查看覆盖率
166
+ python -m pytest --cov=src.dynamic_params tests/unit/ --cov-report=html
167
+
168
+ # 生成完整报告(Allure + 覆盖率)
169
+ python -m pytest tests/ --alluredir=reports/allure-results -clean --cov=src.dynamic_params --cov-report=html:reports/coverage-html --cov-report=xml:reports/coverage.xml --cov-report=term
170
+
171
+ # 查看Allure报告
172
+ allure serve reports/allure-results
173
+ ```
174
+
175
+ ## 测试报告
176
+
177
+ 有关测试报告的详细说明,请参见:
178
+ - [docs/reports-readme.md](./docs/reports-readme.md) - 报告使用说明
179
+ - [docs/reports-summary.md](./docs/reports-summary.md) - 测试结果和覆盖率摘要
180
+
181
+ 生成的报告位于 `reports/` 目录:
182
+ - Allure报告:`reports/allure-results`(可通过 `allure serve` 查看)
183
+ - 覆盖率HTML报告:`reports/coverage-html/index.html`
184
+ - 覆盖率XML报告:`reports/coverage.xml`
185
+
186
+ ## 许可证
187
+
188
+ MIT
@@ -0,0 +1,145 @@
1
+ # pytest-dynamic-params
2
+
3
+ pytest-dynamic-params 是一个为 pytest 设计的动态参数化插件,通过声明式参数生成器自动处理依赖解析和参数化,支持懒加载、缓存机制和多作用域管理,与现有 pytest 机制无缝集成,简化复杂测试场景的参数管理,提高测试效率和可维护性。
4
+
5
+ ## 特性
6
+
7
+ - **声明式参数生成**:使用装饰器定义参数生成器,专注于"生成什么"而非"如何生成"
8
+ - **自动依赖管理**:系统自动分析生成器函数的参数签名,确定依赖关系并按正确顺序执行
9
+ - **与现有机制兼容**:完全兼容现有的pytest机制,包括`@pytest.mark.parametrize`装饰器和fixture系统
10
+ - **性能优化**:支持懒加载和缓存机制,提高测试执行效率
11
+ - **灵活的作用域管理**:支持function、class、module、session四种作用域
12
+ - **智能错误检测**:自动检测并报告参数生成器之间的循环依赖
13
+ - **可配置性**:支持多种配置选项,满足不同场景需求
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+
21
+ ## 快速开始
22
+
23
+ 安装后,您可以立即在测试中使用动态参数装饰器:
24
+
25
+ ```python
26
+ from dynamic_params import param_generator, with_dynamic_params
27
+
28
+ @param_generator
29
+ def calculate_result(input_value):
30
+ return input_value * 2
31
+
32
+ @with_dynamic_params(result=calculate_result)
33
+ @pytest.mark.parametrize("input_value", [1, 2, 3])
34
+ def test_basic(input_value, result):
35
+ assert result == input_value * 2
36
+ ```
37
+
38
+ ## 核心概念
39
+
40
+ - **参数生成器**:使用`@param_generator`装饰器定义的函数,用于生成参数值
41
+ - **动态参数**:在运行时生成的参数,可以依赖其他参数或fixture
42
+ - **参数依赖**:参数之间的依赖关系,系统自动按依赖顺序生成参数
43
+ - **懒加载**:推迟参数生成到实际需要时执行,避免不必要的计算
44
+ - **缓存机制**:缓存参数生成结果,避免重复计算,提高性能
45
+ - **作用域管理**:控制参数生成器实例的生命周期,影响缓存和执行时机
46
+
47
+ ## 使用示例
48
+
49
+ 完整的使用示例请参见:
50
+ - `examples/basic_usage.py` - 基础用法示例(可直接复制使用)
51
+ - `examples/advanced_usage.py` - 高级用法示例(包含作用域、缓存和懒加载)
52
+ - `docs/usage-guide.md` - 详细使用指南
53
+
54
+ ## 配置
55
+
56
+ 可以在`pytest.ini`中配置插件行为:
57
+
58
+ ```ini
59
+ [pytest]
60
+ # 动态参数化系统配置
61
+ dynamic_param_cache_enabled = true
62
+ dynamic_param_validation = strict
63
+ dynamic_param_log_level = INFO
64
+
65
+ # 缓存大小配置
66
+ dynamic_param_cache_size_function = 1000
67
+ dynamic_param_cache_size_class = 500
68
+ dynamic_param_cache_size_module = 200
69
+ dynamic_param_cache_size_session = 100
70
+
71
+ # 性能配置
72
+ dynamic_param_lazy_loading = true
73
+ dynamic_param_incremental_generation = true
74
+
75
+ # 测试标记
76
+ markers =
77
+ dynamic_param: 使用动态参数的测试
78
+ param_generator: 参数生成器函数
79
+ ```
80
+
81
+ 也可以通过环境变量配置:
82
+ - `PYTEST_DYNAMIC_PARAM_CACHE`: 控制缓存是否启用
83
+ - `PYTEST_DYNAMIC_PARAM_VALIDATION`: 验证级别
84
+ - `PYTEST_DYNAMIC_PARAM_LOG_LEVEL`: 日志级别
85
+ - `PYTEST_DYNAMIC_PARAM_LAZY_LOADING`: 懒加载设置
86
+ - `PYTEST_DYNAMIC_PARAM_INCREMENTAL`: 增量生成设置
87
+ - `PYTEST_DYNAMIC_PARAM_DEBUG`: 调试模式
88
+ - `PYTEST_DYNAMIC_PARAM_PROFILE`: 性能分析
89
+ - `PYTEST_DYNAMIC_PARAM_CACHE_DIR`: 缓存目录
90
+
91
+ ## 项目结构
92
+
93
+ 项目的主要组成部分:
94
+
95
+ - `src/` - 源代码
96
+ - `tests/` - 测试代码
97
+ - `examples/` - 使用示例
98
+ - `docs/` - 文档
99
+ - `specs/` - 项目规格说明
100
+ - `reports/` - 测试报告
101
+
102
+ 有关详细的源码架构说明,请参见 [docs/structure.md](./docs/structure.md)。
103
+
104
+ ## 运行测试
105
+
106
+ ```bash
107
+ # 运行单元测试
108
+ python -m pytest tests/unit/
109
+
110
+ # 运行功能测试
111
+ python -m pytest tests/functional/
112
+
113
+ # 运行集成测试
114
+ python -m pytest tests/integration/
115
+
116
+ # 运行性能测试
117
+ python -m pytest tests/performance/
118
+
119
+ # 运行所有测试
120
+ python -m pytest tests/
121
+
122
+ # 查看覆盖率
123
+ python -m pytest --cov=src.dynamic_params tests/unit/ --cov-report=html
124
+
125
+ # 生成完整报告(Allure + 覆盖率)
126
+ python -m pytest tests/ --alluredir=reports/allure-results -clean --cov=src.dynamic_params --cov-report=html:reports/coverage-html --cov-report=xml:reports/coverage.xml --cov-report=term
127
+
128
+ # 查看Allure报告
129
+ allure serve reports/allure-results
130
+ ```
131
+
132
+ ## 测试报告
133
+
134
+ 有关测试报告的详细说明,请参见:
135
+ - [docs/reports-readme.md](./docs/reports-readme.md) - 报告使用说明
136
+ - [docs/reports-summary.md](./docs/reports-summary.md) - 测试结果和覆盖率摘要
137
+
138
+ 生成的报告位于 `reports/` 目录:
139
+ - Allure报告:`reports/allure-results`(可通过 `allure serve` 查看)
140
+ - 覆盖率HTML报告:`reports/coverage-html/index.html`
141
+ - 覆盖率XML报告:`reports/coverage.xml`
142
+
143
+ ## 许可证
144
+
145
+ MIT
@@ -0,0 +1,75 @@
1
+ [project]
2
+ name = "pytest-dynamic-params"
3
+ version = "0.1.0"
4
+ description = "Dynamic parameters plugin for pytest"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Your Name", email = "your.email@example.com" }
8
+ ]
9
+ license = { file = "LICENSE" }
10
+ classifiers = [
11
+ "Framework :: Pytest",
12
+ "Programming Language :: Python :: 3",
13
+ "Operating System :: OS Independent"
14
+ ]
15
+ requires-python = ">=3.8"
16
+ dependencies = [
17
+ "pytest"
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ dev = [
22
+ "black",
23
+ "flake8",
24
+ "isort",
25
+ "mypy",
26
+ "bandit",
27
+ "pre-commit"
28
+ ]
29
+
30
+ [project.entry-points.pytest11]
31
+ dynamic-params = "dynamic_params.plugin"
32
+
33
+ [build-system]
34
+ requires = ["setuptools", "wheel"]
35
+ build-backend = "setuptools.build_meta"
36
+
37
+ [tool.basedpyright]
38
+ pythonVersion = "3.8"
39
+ strict = true
40
+ reportMissingImports = true
41
+ reportMissingTypeStubs = true
42
+ reportUnusedVariable = true
43
+ reportUnusedImport = true
44
+
45
+ [tool.pytest.ini_options]
46
+ testpaths = ["tests"]
47
+ pythonpath = ["src"]
48
+
49
+ [tool.black]
50
+ line-length = 88
51
+ target-version = ['py38']
52
+
53
+ [tool.isort]
54
+ profile = "black"
55
+
56
+ [tool.flake8]
57
+ max-line-length = 88
58
+ extend-ignore = [
59
+ "E203", # 与 black 冲突的空格规则
60
+ "ANN101", "ANN102", "ANN103", "ANN104", # 忽略 self/cls 的类型注解检查
61
+ "ANN201", "ANN202", "ANN203", "ANN204", "ANN205", "ANN206", # 忽略返回值类型注解检查
62
+ "ANN001", "ANN002", "ANN003" # 忽略参数类型注解检查
63
+ ]
64
+
65
+ [tool.flake8.plugins]
66
+ flake8-annotations = false
67
+
68
+ [tool.mypy]
69
+ python_version = "3.10"
70
+ ignore_missing_imports = true
71
+ exclude = "^tests/|^venv/|^\\.venv/"
72
+ mypy_path = "src"
73
+ follow_imports = "silent"
74
+ warn_unused_configs = true
75
+ namespace_packages = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ from setuptools import find_packages, setup
2
+
3
+ setup(
4
+ name="pytest-dynamic-params",
5
+ version="0.1.0",
6
+ packages=find_packages(where="src"), # 只查找src目录下的包
7
+ package_dir={"": "src"},
8
+ install_requires=["pytest"],
9
+ entry_points={"pytest11": ["dynamic-params = dynamic_params.plugin"]},
10
+ classifiers=[
11
+ "Framework :: Pytest",
12
+ "Programming Language :: Python :: 3",
13
+ "Operating System :: OS Independent",
14
+ ],
15
+ )
@@ -0,0 +1,21 @@
1
+ from .config import DynamicParamConfig
2
+ from .core.generator import ParamGenerator
3
+ from .core.registry import GeneratorRegistry
4
+ from .decorators import param_generator, with_dynamic_params
5
+ from .errors import DynamicParamError, InvalidGeneratorError, MissingParameterError
6
+ from .lazy import LazyResult
7
+ from .plugin import pytest_configure, pytest_generate_tests
8
+
9
+ __all__ = [
10
+ "ParamGenerator",
11
+ "LazyResult",
12
+ "GeneratorRegistry",
13
+ "DynamicParamError",
14
+ "MissingParameterError",
15
+ "InvalidGeneratorError",
16
+ "param_generator",
17
+ "with_dynamic_params",
18
+ "pytest_configure",
19
+ "pytest_generate_tests",
20
+ "DynamicParamConfig",
21
+ ]
@@ -0,0 +1,163 @@
1
+ import configparser
2
+ import os
3
+ from typing import Any, Dict, Optional
4
+
5
+ from .errors import ConfigurationError
6
+
7
+
8
+ class DynamicParamConfig:
9
+ """动态参数化系统配置类"""
10
+
11
+ DEFAULT_CONFIG = {
12
+ "cache": {
13
+ "enabled": "true",
14
+ "size_function": "1000",
15
+ "size_class": "500",
16
+ "size_module": "200",
17
+ "size_session": "100",
18
+ "cleanup_interval": "100",
19
+ "dir": ".pytest_cache/dynamic_params",
20
+ },
21
+ "validation": {"level": "strict", "log_level": "INFO"},
22
+ "performance": {"lazy_loading": "true", "incremental_generation": "true"},
23
+ "debug": {"enabled": "false", "profile": "false"},
24
+ }
25
+
26
+ _instance: Optional["DynamicParamConfig"] = None
27
+ _config: Dict[str, Any]
28
+
29
+ def __new__(cls):
30
+ if cls._instance is None:
31
+ cls._instance = super().__new__(cls)
32
+ cls._instance._config = cls._instance._load_config()
33
+ return cls._instance
34
+
35
+ @classmethod
36
+ def get_instance(cls):
37
+ """获取单例实例"""
38
+ if cls._instance is None:
39
+ cls._instance = cls()
40
+ return cls._instance
41
+
42
+ def _load_config(self) -> Dict[str, Any]:
43
+ """加载配置"""
44
+ config = configparser.ConfigParser()
45
+ config.read_dict(self.DEFAULT_CONFIG)
46
+
47
+ # 从配置文件加载(优先级:pytest.ini > pyproject.toml > dynamic_params.json/yaml)
48
+ config_files = [
49
+ "pytest.ini",
50
+ "pyproject.toml",
51
+ "dynamic_params.json",
52
+ "dynamic_params.yaml",
53
+ ]
54
+
55
+ for config_file in config_files:
56
+ if os.path.exists(config_file):
57
+ try:
58
+ config.read(config_file)
59
+ except Exception as e:
60
+ print(f"Warning: Failed to read {config_file}: {e}")
61
+
62
+ # 从环境变量加载(优先级最高)
63
+ self._update_from_env(config)
64
+
65
+ return self._normalize_config(config)
66
+
67
+ def _update_from_env(self, config: configparser.ConfigParser):
68
+ """从环境变量更新配置"""
69
+ env_mapping = {
70
+ "PYTEST_DYNAMIC_PARAM_CACHE": "cache.enabled",
71
+ "PYTEST_DYNAMIC_PARAM_VALIDATION": "validation.level",
72
+ "PYTEST_DYNAMIC_PARAM_LOG_LEVEL": "validation.log_level",
73
+ "PYTEST_DYNAMIC_PARAM_LAZY_LOADING": "performance.lazy_loading",
74
+ "PYTEST_DYNAMIC_PARAM_INCREMENTAL": "performance.incremental_generation",
75
+ "PYTEST_DYNAMIC_PARAM_DEBUG": "debug.enabled",
76
+ "PYTEST_DYNAMIC_PARAM_PROFILE": "debug.profile",
77
+ "PYTEST_DYNAMIC_PARAM_CACHE_DIR": "cache.dir",
78
+ }
79
+
80
+ for env_var, config_key in env_mapping.items():
81
+ if env_var in os.environ:
82
+ try:
83
+ section, option = config_key.split(".")
84
+ if section not in config:
85
+ config[section] = {}
86
+ config[section][option] = os.environ[env_var]
87
+ except Exception as e:
88
+ print(f"Warning: Failed to update config from {env_var}: {e}")
89
+
90
+ def _normalize_config(self, config: configparser.ConfigParser) -> Dict[str, Any]:
91
+ """标准化配置值"""
92
+ normalized: Dict[str, Any] = {}
93
+
94
+ for section in config.sections():
95
+ normalized[section] = {}
96
+ for key, value in config[section].items():
97
+ try:
98
+ # 转换布尔值
99
+ if value.lower() in ("true", "false"):
100
+ normalized[section][key] = config[section].getboolean(key)
101
+ # 转换整数
102
+ elif value.isdigit():
103
+ normalized[section][key] = config[section].getint(key)
104
+ else:
105
+ normalized[section][key] = value
106
+ except Exception as e:
107
+ raise ConfigurationError(
108
+ config_key=f"{section}.{key}",
109
+ config_value=value,
110
+ expected_type=str,
111
+ ) from e
112
+
113
+ return normalized
114
+
115
+ def get(self, section: str, option: str, default: Optional[Any] = ...) -> Any:
116
+ """获取配置值"""
117
+ try:
118
+ if section not in self._config:
119
+ if default is not ...:
120
+ return default
121
+ raise KeyError(section)
122
+ if option not in self._config[section]:
123
+ if default is not ...:
124
+ return default
125
+ raise KeyError(option)
126
+ return self._config[section][option]
127
+ except KeyError:
128
+ if default is not ...:
129
+ return default
130
+ raise
131
+
132
+ def get_section(self, section: str) -> Dict[str, Any]:
133
+ """获取整个配置节"""
134
+ return self._config.get(section, {})
135
+
136
+ def validate(self) -> bool:
137
+ """验证配置有效性"""
138
+ try:
139
+ # 验证缓存配置
140
+ if not isinstance(self.get("cache", "enabled"), bool):
141
+ raise ConfigurationError(
142
+ "cache.enabled", self.get("cache", "enabled"), bool
143
+ )
144
+
145
+ # 验证验证级别
146
+ valid_levels = ["strict", "warn", "off"]
147
+ if self.get("validation", "level") not in valid_levels:
148
+ raise ConfigurationError(
149
+ "validation.level", self.get("validation", "level"), str
150
+ )
151
+
152
+ # 验证日志级别
153
+ valid_log_levels = ["DEBUG", "INFO", "WARNING", "ERROR"]
154
+ if self.get("validation", "log_level") not in valid_log_levels:
155
+ raise ConfigurationError(
156
+ "validation.log_level", self.get("validation", "log_level"), str
157
+ )
158
+
159
+ return True
160
+ except ConfigurationError:
161
+ raise
162
+ except Exception as e:
163
+ raise ConfigurationError("config.validation", str(e), str) from e