Source code for depdigest.core.config
from __future__ import annotations
from dataclasses import dataclass, field
from importlib import import_module
from typing import Any, Dict, Optional, Type
from contextlib import contextmanager
from functools import lru_cache
[docs]
@dataclass(frozen=True)
class DepConfig:
libraries: Dict[str, Dict[str, str]] = field(default_factory=dict)
mapping: Dict[str, str] = field(default_factory=dict)
show_all_capabilities: bool = True
exception_class: Type[Exception] = ImportError
_PACKAGE_CONFIGS: Dict[str, DepConfig] = {}
[docs]
def register_package_config(package_name: str, config: DepConfig):
"""Manually register a configuration for a package root."""
if not package_name or not isinstance(package_name, str):
raise ValueError("package_name must be a non-empty string")
if not isinstance(config, DepConfig):
raise TypeError("config must be an instance of DepConfig")
_PACKAGE_CONFIGS[package_name] = config
resolve_config.cache_clear()
[docs]
def unregister_package_config(package_name: str):
"""Remove a manually registered package configuration if present."""
_PACKAGE_CONFIGS.pop(package_name, None)
resolve_config.cache_clear()
[docs]
def clear_package_configs():
"""Clear all manually registered configurations. Useful for testing."""
_PACKAGE_CONFIGS.clear()
resolve_config.cache_clear()
[docs]
@contextmanager
def temporary_package_config(package_name: str, config: DepConfig):
"""
Temporarily register a package config within a context.
Restores previous state after context exit.
"""
had_previous = package_name in _PACKAGE_CONFIGS
previous = _PACKAGE_CONFIGS.get(package_name)
register_package_config(package_name, config)
try:
yield
finally:
if had_previous:
_PACKAGE_CONFIGS[package_name] = previous
else:
_PACKAGE_CONFIGS.pop(package_name, None)
resolve_config.cache_clear()
[docs]
@lru_cache(maxsize=128)
def resolve_config(module_path: Optional[str]) -> DepConfig:
if not module_path:
return DepConfig()
package_root = module_path.split('.')[0]
if package_root in _PACKAGE_CONFIGS:
return _PACKAGE_CONFIGS[package_root]
config_module_path = f"{package_root}._depdigest"
try:
module = import_module(config_module_path)
return DepConfig(
libraries=getattr(module, "LIBRARIES", {}),
mapping=getattr(module, "MAPPING", {}),
show_all_capabilities=getattr(module, "SHOW_ALL_CAPABILITIES", True),
exception_class=getattr(module, "EXCEPTION_CLASS", ImportError)
)
except ModuleNotFoundError as exc:
if exc.name == config_module_path:
return DepConfig()
raise