claude-mpm 4.4.3__py3-none-any.whl → 4.4.4__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +116 -62
- claude_mpm/cli/parsers/configure_parser.py +3 -1
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +6 -6
- claude_mpm/core/framework/formatters/__init__.py +2 -2
- claude_mpm/core/framework/formatters/capability_generator.py +19 -8
- claude_mpm/core/framework/formatters/content_formatter.py +8 -3
- claude_mpm/core/framework/formatters/context_generator.py +7 -3
- claude_mpm/core/framework/loaders/__init__.py +3 -3
- claude_mpm/core/framework/loaders/agent_loader.py +7 -3
- claude_mpm/core/framework/loaders/file_loader.py +16 -6
- claude_mpm/core/framework/loaders/instruction_loader.py +16 -6
- claude_mpm/core/framework/loaders/packaged_loader.py +36 -12
- claude_mpm/core/framework/processors/__init__.py +2 -2
- claude_mpm/core/framework/processors/memory_processor.py +14 -6
- claude_mpm/core/framework/processors/metadata_processor.py +5 -5
- claude_mpm/core/framework/processors/template_processor.py +12 -6
- claude_mpm/core/framework_loader.py +44 -20
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +20 -13
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +1 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +399 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +10 -10
- claude_mpm/services/mcp_gateway/core/process_pool.py +62 -23
- claude_mpm/services/mcp_gateway/tools/__init__.py +6 -5
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +3 -1
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +16 -31
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +111 -126
- claude_mpm/services/unified/config_strategies/config_schema.py +157 -111
- claude_mpm/services/unified/config_strategies/context_strategy.py +91 -89
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +183 -173
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +160 -152
- claude_mpm/services/unified/config_strategies/unified_config_service.py +124 -112
- claude_mpm/services/unified/config_strategies/validation_strategy.py +298 -259
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/METADATA +9 -3
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/RECORD +118 -117
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/top_level.txt +0 -0
@@ -4,26 +4,29 @@ Consolidates 15+ configuration services into a single unified service
|
|
4
4
|
Achieves 65-75% code reduction through strategic patterns
|
5
5
|
"""
|
6
6
|
|
7
|
-
|
8
|
-
from typing import Any, Dict, Optional, Type, Union, List, Callable, TypeVar, Generic
|
9
|
-
from pathlib import Path
|
7
|
+
import hashlib
|
10
8
|
import json
|
11
|
-
import yaml
|
12
9
|
import os
|
13
|
-
|
14
|
-
from dataclasses import dataclass, field
|
10
|
+
import pickle
|
15
11
|
import threading
|
16
|
-
from
|
12
|
+
from abc import ABC, abstractmethod
|
17
13
|
from collections import defaultdict
|
18
|
-
import
|
19
|
-
import
|
14
|
+
from dataclasses import dataclass, field
|
15
|
+
from datetime import datetime, timedelta
|
16
|
+
from enum import Enum
|
17
|
+
from pathlib import Path
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
|
19
|
+
|
20
|
+
import yaml
|
20
21
|
|
21
22
|
from claude_mpm.core.logging_utils import get_logger
|
22
23
|
|
23
|
-
T = TypeVar(
|
24
|
+
T = TypeVar("T")
|
25
|
+
|
24
26
|
|
25
27
|
class ConfigFormat(Enum):
|
26
28
|
"""Supported configuration formats"""
|
29
|
+
|
27
30
|
JSON = "json"
|
28
31
|
YAML = "yaml"
|
29
32
|
ENV = "env"
|
@@ -31,8 +34,10 @@ class ConfigFormat(Enum):
|
|
31
34
|
TOML = "toml"
|
32
35
|
INI = "ini"
|
33
36
|
|
37
|
+
|
34
38
|
class ConfigContext(Enum):
|
35
39
|
"""Configuration contexts for lifecycle management"""
|
40
|
+
|
36
41
|
GLOBAL = "global"
|
37
42
|
PROJECT = "project"
|
38
43
|
USER = "user"
|
@@ -41,9 +46,11 @@ class ConfigContext(Enum):
|
|
41
46
|
RUNTIME = "runtime"
|
42
47
|
TEST = "test"
|
43
48
|
|
49
|
+
|
44
50
|
@dataclass
|
45
51
|
class ConfigMetadata:
|
46
52
|
"""Metadata for configuration tracking"""
|
53
|
+
|
47
54
|
source: str
|
48
55
|
format: ConfigFormat
|
49
56
|
context: ConfigContext
|
@@ -54,28 +61,26 @@ class ConfigMetadata:
|
|
54
61
|
hot_reload: bool = False
|
55
62
|
ttl: Optional[timedelta] = None
|
56
63
|
|
64
|
+
|
57
65
|
class IConfigStrategy(ABC):
|
58
66
|
"""Base strategy interface for configuration operations"""
|
59
67
|
|
60
68
|
@abstractmethod
|
61
69
|
def can_handle(self, source: Union[str, Path, Dict]) -> bool:
|
62
70
|
"""Check if this strategy can handle the given source"""
|
63
|
-
pass
|
64
71
|
|
65
72
|
@abstractmethod
|
66
73
|
def load(self, source: Any, **kwargs) -> Dict[str, Any]:
|
67
74
|
"""Load configuration from source"""
|
68
|
-
pass
|
69
75
|
|
70
76
|
@abstractmethod
|
71
77
|
def validate(self, config: Dict[str, Any], schema: Optional[Dict] = None) -> bool:
|
72
78
|
"""Validate configuration against schema"""
|
73
|
-
pass
|
74
79
|
|
75
80
|
@abstractmethod
|
76
81
|
def transform(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
77
82
|
"""Transform configuration to standard format"""
|
78
|
-
|
83
|
+
|
79
84
|
|
80
85
|
class UnifiedConfigService:
|
81
86
|
"""
|
@@ -97,7 +102,7 @@ class UnifiedConfigService:
|
|
97
102
|
|
98
103
|
def __init__(self):
|
99
104
|
"""Initialize the unified configuration service"""
|
100
|
-
if not hasattr(self,
|
105
|
+
if not hasattr(self, "_initialized"):
|
101
106
|
self.logger = get_logger(self.__class__.__name__)
|
102
107
|
self._strategies: Dict[str, IConfigStrategy] = {}
|
103
108
|
self._loaders: Dict[ConfigFormat, Callable] = {}
|
@@ -134,21 +139,21 @@ class UnifiedConfigService:
|
|
134
139
|
def _initialize_core_validators(self):
|
135
140
|
"""Initialize the 15 composable validators (reduced from 236)"""
|
136
141
|
# Core validators that can be composed for complex validation
|
137
|
-
self._validators[
|
138
|
-
self._validators[
|
139
|
-
self._validators[
|
140
|
-
self._validators[
|
141
|
-
self._validators[
|
142
|
-
self._validators[
|
143
|
-
self._validators[
|
144
|
-
self._validators[
|
145
|
-
self._validators[
|
146
|
-
self._validators[
|
147
|
-
self._validators[
|
148
|
-
self._validators[
|
149
|
-
self._validators[
|
150
|
-
self._validators[
|
151
|
-
self._validators[
|
142
|
+
self._validators["required"] = self._validate_required
|
143
|
+
self._validators["type"] = self._validate_type
|
144
|
+
self._validators["range"] = self._validate_range
|
145
|
+
self._validators["pattern"] = self._validate_pattern
|
146
|
+
self._validators["enum"] = self._validate_enum
|
147
|
+
self._validators["schema"] = self._validate_schema
|
148
|
+
self._validators["dependency"] = self._validate_dependency
|
149
|
+
self._validators["unique"] = self._validate_unique
|
150
|
+
self._validators["format"] = self._validate_format
|
151
|
+
self._validators["length"] = self._validate_length
|
152
|
+
self._validators["custom"] = self._validate_custom
|
153
|
+
self._validators["conditional"] = self._validate_conditional
|
154
|
+
self._validators["recursive"] = self._validate_recursive
|
155
|
+
self._validators["cross_field"] = self._validate_cross_field
|
156
|
+
self._validators["composite"] = self._validate_composite
|
152
157
|
|
153
158
|
def register_strategy(self, name: str, strategy: IConfigStrategy):
|
154
159
|
"""Register a configuration strategy"""
|
@@ -164,7 +169,7 @@ class UnifiedConfigService:
|
|
164
169
|
schema: Optional[Dict] = None,
|
165
170
|
hot_reload: bool = False,
|
166
171
|
ttl: Optional[timedelta] = None,
|
167
|
-
**kwargs
|
172
|
+
**kwargs,
|
168
173
|
) -> Dict[str, Any]:
|
169
174
|
"""
|
170
175
|
Universal configuration loading method
|
@@ -214,7 +219,7 @@ class UnifiedConfigService:
|
|
214
219
|
loaded_at=datetime.now(),
|
215
220
|
checksum=self._calculate_checksum(config),
|
216
221
|
hot_reload=hot_reload,
|
217
|
-
ttl=ttl
|
222
|
+
ttl=ttl,
|
218
223
|
)
|
219
224
|
|
220
225
|
# Cache configuration
|
@@ -235,7 +240,7 @@ class UnifiedConfigService:
|
|
235
240
|
self,
|
236
241
|
config: Dict[str, Any],
|
237
242
|
schema: Union[Dict, str],
|
238
|
-
validators: Optional[List[str]] = None
|
243
|
+
validators: Optional[List[str]] = None,
|
239
244
|
) -> bool:
|
240
245
|
"""
|
241
246
|
Universal validation method using composable validators
|
@@ -263,10 +268,7 @@ class UnifiedConfigService:
|
|
263
268
|
return False
|
264
269
|
|
265
270
|
def get(
|
266
|
-
self,
|
267
|
-
key: str,
|
268
|
-
context: Optional[ConfigContext] = None,
|
269
|
-
default: Any = None
|
271
|
+
self, key: str, context: Optional[ConfigContext] = None, default: Any = None
|
270
272
|
) -> Any:
|
271
273
|
"""Get configuration value by key with context awareness"""
|
272
274
|
# Check specific context first
|
@@ -287,7 +289,7 @@ class UnifiedConfigService:
|
|
287
289
|
key: str,
|
288
290
|
value: Any,
|
289
291
|
context: ConfigContext = ConfigContext.RUNTIME,
|
290
|
-
persist: bool = False
|
292
|
+
persist: bool = False,
|
291
293
|
):
|
292
294
|
"""Set configuration value with optional persistence"""
|
293
295
|
with self._lock:
|
@@ -320,7 +322,9 @@ class UnifiedConfigService:
|
|
320
322
|
self._watchers[key].append(callback)
|
321
323
|
self.logger.debug(f"Added watcher for key: {key}")
|
322
324
|
|
323
|
-
def reload(
|
325
|
+
def reload(
|
326
|
+
self, cache_key: Optional[str] = None, context: Optional[ConfigContext] = None
|
327
|
+
):
|
324
328
|
"""Reload configuration(s)"""
|
325
329
|
with self._lock:
|
326
330
|
if cache_key:
|
@@ -332,7 +336,7 @@ class UnifiedConfigService:
|
|
332
336
|
metadata.context,
|
333
337
|
metadata.format,
|
334
338
|
hot_reload=metadata.hot_reload,
|
335
|
-
ttl=metadata.ttl
|
339
|
+
ttl=metadata.ttl,
|
336
340
|
)
|
337
341
|
elif context:
|
338
342
|
# Reload all configurations in context
|
@@ -344,7 +348,7 @@ class UnifiedConfigService:
|
|
344
348
|
metadata.context,
|
345
349
|
metadata.format,
|
346
350
|
hot_reload=metadata.hot_reload,
|
347
|
-
ttl=metadata.ttl
|
351
|
+
ttl=metadata.ttl,
|
348
352
|
)
|
349
353
|
else:
|
350
354
|
# Reload all configurations
|
@@ -354,14 +358,14 @@ class UnifiedConfigService:
|
|
354
358
|
metadata.context,
|
355
359
|
metadata.format,
|
356
360
|
hot_reload=metadata.hot_reload,
|
357
|
-
ttl=metadata.ttl
|
361
|
+
ttl=metadata.ttl,
|
358
362
|
)
|
359
363
|
|
360
364
|
def merge(
|
361
365
|
self,
|
362
366
|
*configs: Dict[str, Any],
|
363
367
|
strategy: str = "deep",
|
364
|
-
context: ConfigContext = ConfigContext.RUNTIME
|
368
|
+
context: ConfigContext = ConfigContext.RUNTIME,
|
365
369
|
) -> Dict[str, Any]:
|
366
370
|
"""Merge multiple configurations with specified strategy"""
|
367
371
|
if not configs:
|
@@ -372,21 +376,20 @@ class UnifiedConfigService:
|
|
372
376
|
for config in configs:
|
373
377
|
self._deep_merge(result, config)
|
374
378
|
return result
|
375
|
-
|
379
|
+
if strategy == "shallow":
|
376
380
|
result = {}
|
377
381
|
for config in configs:
|
378
382
|
result.update(config)
|
379
383
|
return result
|
380
|
-
|
384
|
+
if strategy == "override":
|
381
385
|
return configs[-1] if configs else {}
|
382
|
-
|
383
|
-
raise ValueError(f"Unknown merge strategy: {strategy}")
|
386
|
+
raise ValueError(f"Unknown merge strategy: {strategy}")
|
384
387
|
|
385
388
|
def export(
|
386
389
|
self,
|
387
390
|
format: ConfigFormat,
|
388
391
|
context: Optional[ConfigContext] = None,
|
389
|
-
path: Optional[Path] = None
|
392
|
+
path: Optional[Path] = None,
|
390
393
|
) -> Union[str, None]:
|
391
394
|
"""Export configuration to specified format"""
|
392
395
|
configs = []
|
@@ -436,7 +439,7 @@ class UnifiedConfigService:
|
|
436
439
|
|
437
440
|
path = Path(source)
|
438
441
|
if path.exists():
|
439
|
-
with open(path
|
442
|
+
with open(path) as f:
|
440
443
|
return json.load(f)
|
441
444
|
|
442
445
|
# Try to parse as JSON string
|
@@ -449,7 +452,7 @@ class UnifiedConfigService:
|
|
449
452
|
|
450
453
|
path = Path(source)
|
451
454
|
if path.exists():
|
452
|
-
with open(path
|
455
|
+
with open(path) as f:
|
453
456
|
return yaml.safe_load(f)
|
454
457
|
|
455
458
|
# Try to parse as YAML string
|
@@ -457,7 +460,7 @@ class UnifiedConfigService:
|
|
457
460
|
|
458
461
|
def _load_env(self, source: Union[str, Path, Dict], **kwargs) -> Dict[str, Any]:
|
459
462
|
"""Load environment variables as configuration"""
|
460
|
-
prefix = kwargs.get(
|
463
|
+
prefix = kwargs.get("prefix", "")
|
461
464
|
config = {}
|
462
465
|
|
463
466
|
for key, value in os.environ.items():
|
@@ -465,7 +468,7 @@ class UnifiedConfigService:
|
|
465
468
|
continue
|
466
469
|
|
467
470
|
# Remove prefix if present
|
468
|
-
clean_key = key[len(prefix):] if prefix else key
|
471
|
+
clean_key = key[len(prefix) :] if prefix else key
|
469
472
|
|
470
473
|
# Convert to lowercase and replace underscores
|
471
474
|
clean_key = clean_key.lower()
|
@@ -490,7 +493,7 @@ class UnifiedConfigService:
|
|
490
493
|
# Extract configuration from module
|
491
494
|
config = {}
|
492
495
|
for key in dir(module):
|
493
|
-
if not key.startswith(
|
496
|
+
if not key.startswith("_"):
|
494
497
|
config[key] = getattr(module, key)
|
495
498
|
|
496
499
|
return config
|
@@ -505,14 +508,14 @@ class UnifiedConfigService:
|
|
505
508
|
|
506
509
|
path = Path(source)
|
507
510
|
if path.exists():
|
508
|
-
with open(path
|
511
|
+
with open(path) as f:
|
509
512
|
return toml.load(f)
|
510
513
|
|
511
514
|
return toml.loads(str(source))
|
512
515
|
|
513
516
|
def _validate_required(self, config: Dict[str, Any], schema: Dict) -> bool:
|
514
517
|
"""Validate required fields"""
|
515
|
-
required = schema.get(
|
518
|
+
required = schema.get("required", [])
|
516
519
|
for field in required:
|
517
520
|
if field not in config:
|
518
521
|
self.logger.error(f"Required field missing: {field}")
|
@@ -521,44 +524,47 @@ class UnifiedConfigService:
|
|
521
524
|
|
522
525
|
def _validate_type(self, config: Dict[str, Any], schema: Dict) -> bool:
|
523
526
|
"""Validate field types"""
|
524
|
-
properties = schema.get(
|
527
|
+
properties = schema.get("properties", {})
|
525
528
|
for key, value in config.items():
|
526
529
|
if key in properties:
|
527
|
-
expected_type = properties[key].get(
|
530
|
+
expected_type = properties[key].get("type")
|
528
531
|
if expected_type and not self._check_type(value, expected_type):
|
529
|
-
self.logger.error(
|
532
|
+
self.logger.error(
|
533
|
+
f"Type mismatch for {key}: expected {expected_type}"
|
534
|
+
)
|
530
535
|
return False
|
531
536
|
return True
|
532
537
|
|
533
538
|
def _validate_range(self, config: Dict[str, Any], schema: Dict) -> bool:
|
534
539
|
"""Validate numeric ranges"""
|
535
|
-
properties = schema.get(
|
540
|
+
properties = schema.get("properties", {})
|
536
541
|
for key, value in config.items():
|
537
542
|
if key in properties:
|
538
543
|
prop = properties[key]
|
539
|
-
if
|
544
|
+
if "minimum" in prop and value < prop["minimum"]:
|
540
545
|
return False
|
541
|
-
if
|
546
|
+
if "maximum" in prop and value > prop["maximum"]:
|
542
547
|
return False
|
543
548
|
return True
|
544
549
|
|
545
550
|
def _validate_pattern(self, config: Dict[str, Any], schema: Dict) -> bool:
|
546
551
|
"""Validate string patterns"""
|
547
552
|
import re
|
548
|
-
|
553
|
+
|
554
|
+
properties = schema.get("properties", {})
|
549
555
|
for key, value in config.items():
|
550
|
-
if key in properties and
|
551
|
-
pattern = properties[key][
|
556
|
+
if key in properties and "pattern" in properties[key]:
|
557
|
+
pattern = properties[key]["pattern"]
|
552
558
|
if not re.match(pattern, str(value)):
|
553
559
|
return False
|
554
560
|
return True
|
555
561
|
|
556
562
|
def _validate_enum(self, config: Dict[str, Any], schema: Dict) -> bool:
|
557
563
|
"""Validate enum values"""
|
558
|
-
properties = schema.get(
|
564
|
+
properties = schema.get("properties", {})
|
559
565
|
for key, value in config.items():
|
560
|
-
if key in properties and
|
561
|
-
if value not in properties[key][
|
566
|
+
if key in properties and "enum" in properties[key]:
|
567
|
+
if value not in properties[key]["enum"]:
|
562
568
|
return False
|
563
569
|
return True
|
564
570
|
|
@@ -579,7 +585,7 @@ class UnifiedConfigService:
|
|
579
585
|
|
580
586
|
def _validate_dependency(self, config: Dict[str, Any], schema: Dict) -> bool:
|
581
587
|
"""Validate field dependencies"""
|
582
|
-
dependencies = schema.get(
|
588
|
+
dependencies = schema.get("dependencies", {})
|
583
589
|
for field, deps in dependencies.items():
|
584
590
|
if field in config:
|
585
591
|
for dep in deps:
|
@@ -590,9 +596,9 @@ class UnifiedConfigService:
|
|
590
596
|
|
591
597
|
def _validate_unique(self, config: Dict[str, Any], schema: Dict) -> bool:
|
592
598
|
"""Validate unique values in arrays"""
|
593
|
-
properties = schema.get(
|
599
|
+
properties = schema.get("properties", {})
|
594
600
|
for key, value in config.items():
|
595
|
-
if key in properties and properties[key].get(
|
601
|
+
if key in properties and properties[key].get("uniqueItems"):
|
596
602
|
if isinstance(value, list) and len(value) != len(set(map(str, value))):
|
597
603
|
return False
|
598
604
|
return True
|
@@ -604,48 +610,48 @@ class UnifiedConfigService:
|
|
604
610
|
|
605
611
|
def _validate_length(self, config: Dict[str, Any], schema: Dict) -> bool:
|
606
612
|
"""Validate string/array lengths"""
|
607
|
-
properties = schema.get(
|
613
|
+
properties = schema.get("properties", {})
|
608
614
|
for key, value in config.items():
|
609
615
|
if key in properties:
|
610
616
|
prop = properties[key]
|
611
|
-
if
|
617
|
+
if "minLength" in prop and len(value) < prop["minLength"]:
|
612
618
|
return False
|
613
|
-
if
|
619
|
+
if "maxLength" in prop and len(value) > prop["maxLength"]:
|
614
620
|
return False
|
615
621
|
return True
|
616
622
|
|
617
623
|
def _validate_custom(self, config: Dict[str, Any], schema: Dict) -> bool:
|
618
624
|
"""Apply custom validation functions"""
|
619
|
-
if
|
620
|
-
validator = schema[
|
625
|
+
if "custom" in schema:
|
626
|
+
validator = schema["custom"]
|
621
627
|
if callable(validator):
|
622
628
|
return validator(config)
|
623
629
|
return True
|
624
630
|
|
625
631
|
def _validate_conditional(self, config: Dict[str, Any], schema: Dict) -> bool:
|
626
632
|
"""Validate conditional requirements"""
|
627
|
-
if
|
628
|
-
condition = schema[
|
633
|
+
if "if" in schema:
|
634
|
+
condition = schema["if"]
|
629
635
|
if self._evaluate_condition(config, condition):
|
630
|
-
if
|
631
|
-
return self._validate_schema(config, schema[
|
632
|
-
elif
|
633
|
-
return self._validate_schema(config, schema[
|
636
|
+
if "then" in schema:
|
637
|
+
return self._validate_schema(config, schema["then"])
|
638
|
+
elif "else" in schema:
|
639
|
+
return self._validate_schema(config, schema["else"])
|
634
640
|
return True
|
635
641
|
|
636
642
|
def _validate_recursive(self, config: Dict[str, Any], schema: Dict) -> bool:
|
637
643
|
"""Recursively validate nested structures"""
|
638
|
-
properties = schema.get(
|
644
|
+
properties = schema.get("properties", {})
|
639
645
|
for key, value in config.items():
|
640
646
|
if key in properties and isinstance(value, dict):
|
641
|
-
if
|
647
|
+
if "properties" in properties[key]:
|
642
648
|
if not self._validate_schema(value, properties[key]):
|
643
649
|
return False
|
644
650
|
return True
|
645
651
|
|
646
652
|
def _validate_cross_field(self, config: Dict[str, Any], schema: Dict) -> bool:
|
647
653
|
"""Validate cross-field constraints"""
|
648
|
-
constraints = schema.get(
|
654
|
+
constraints = schema.get("crossField", [])
|
649
655
|
for constraint in constraints:
|
650
656
|
if not self._evaluate_constraint(config, constraint):
|
651
657
|
return False
|
@@ -653,7 +659,7 @@ class UnifiedConfigService:
|
|
653
659
|
|
654
660
|
def _validate_composite(self, config: Dict[str, Any], schema: Dict) -> bool:
|
655
661
|
"""Composite validation using multiple validators"""
|
656
|
-
validators = schema.get(
|
662
|
+
validators = schema.get("validators", [])
|
657
663
|
for validator_name in validators:
|
658
664
|
if validator_name in self._validators:
|
659
665
|
if not self._validators[validator_name](config, schema):
|
@@ -668,22 +674,22 @@ class UnifiedConfigService:
|
|
668
674
|
path = Path(source)
|
669
675
|
if path.exists():
|
670
676
|
suffix = path.suffix.lower()
|
671
|
-
if suffix ==
|
677
|
+
if suffix == ".json":
|
672
678
|
return ConfigFormat.JSON
|
673
|
-
|
679
|
+
if suffix in [".yaml", ".yml"]:
|
674
680
|
return ConfigFormat.YAML
|
675
|
-
|
681
|
+
if suffix == ".toml":
|
676
682
|
return ConfigFormat.TOML
|
677
|
-
|
683
|
+
if suffix in [".py", ".python"]:
|
678
684
|
return ConfigFormat.PYTHON
|
679
|
-
|
685
|
+
if suffix in [".env"]:
|
680
686
|
return ConfigFormat.ENV
|
681
687
|
|
682
688
|
# Try to detect from content
|
683
689
|
content = str(source)
|
684
|
-
if content.startswith(
|
690
|
+
if content.startswith("{"):
|
685
691
|
return ConfigFormat.JSON
|
686
|
-
|
692
|
+
if ":" in content and "\n" in content:
|
687
693
|
return ConfigFormat.YAML
|
688
694
|
|
689
695
|
return ConfigFormat.JSON
|
@@ -712,11 +718,10 @@ class UnifiedConfigService:
|
|
712
718
|
source: Any,
|
713
719
|
format: ConfigFormat,
|
714
720
|
schema: Optional[Dict],
|
715
|
-
context: ConfigContext
|
721
|
+
context: ConfigContext,
|
716
722
|
):
|
717
723
|
"""Setup hot reload for configuration"""
|
718
724
|
# Implementation for file watching and hot reload
|
719
|
-
pass
|
720
725
|
|
721
726
|
def _trigger_watchers(self, key: str, value: Any):
|
722
727
|
"""Trigger registered watchers for key"""
|
@@ -729,9 +734,10 @@ class UnifiedConfigService:
|
|
729
734
|
def _persist_config(self, key: str, value: Any, context: ConfigContext):
|
730
735
|
"""Persist configuration change"""
|
731
736
|
# Implementation for persisting configuration changes
|
732
|
-
pass
|
733
737
|
|
734
|
-
def _handle_error(
|
738
|
+
def _handle_error(
|
739
|
+
self, error: Exception, source: Any, context: ConfigContext
|
740
|
+
) -> Dict[str, Any]:
|
735
741
|
"""Unified error handling"""
|
736
742
|
for handler in self._error_handlers:
|
737
743
|
try:
|
@@ -747,7 +753,11 @@ class UnifiedConfigService:
|
|
747
753
|
def _deep_merge(self, target: Dict, source: Dict):
|
748
754
|
"""Deep merge source into target"""
|
749
755
|
for key, value in source.items():
|
750
|
-
if
|
756
|
+
if (
|
757
|
+
key in target
|
758
|
+
and isinstance(target[key], dict)
|
759
|
+
and isinstance(value, dict)
|
760
|
+
):
|
751
761
|
self._deep_merge(target[key], value)
|
752
762
|
else:
|
753
763
|
target[key] = value
|
@@ -755,12 +765,12 @@ class UnifiedConfigService:
|
|
755
765
|
def _check_type(self, value: Any, expected_type: str) -> bool:
|
756
766
|
"""Check if value matches expected type"""
|
757
767
|
type_map = {
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
768
|
+
"string": str,
|
769
|
+
"number": (int, float),
|
770
|
+
"integer": int,
|
771
|
+
"boolean": bool,
|
772
|
+
"array": list,
|
773
|
+
"object": dict,
|
764
774
|
}
|
765
775
|
expected = type_map.get(expected_type)
|
766
776
|
if expected:
|
@@ -785,18 +795,20 @@ class UnifiedConfigService:
|
|
785
795
|
def get_statistics(self) -> Dict[str, Any]:
|
786
796
|
"""Get service statistics for monitoring"""
|
787
797
|
return {
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
798
|
+
"total_configs": len(self._cache),
|
799
|
+
"contexts": {
|
800
|
+
ctx.value: len(configs) for ctx, configs in self._contexts.items()
|
801
|
+
},
|
802
|
+
"strategies": len(self._strategies),
|
803
|
+
"loaders": len(self._loaders),
|
804
|
+
"validators": len(self._validators),
|
805
|
+
"watchers": sum(len(w) for w in self._watchers.values()),
|
806
|
+
"cache_size": sum(len(str(c)) for c in self._cache.values()),
|
807
|
+
"metadata_entries": len(self._metadata),
|
796
808
|
}
|
797
809
|
|
798
810
|
|
799
811
|
# Backward compatibility aliases
|
800
812
|
ConfigService = UnifiedConfigService
|
801
813
|
ConfigManager = UnifiedConfigService
|
802
|
-
ConfigLoader = UnifiedConfigService
|
814
|
+
ConfigLoader = UnifiedConfigService
|