Source code for depdigest.core.decorator

import inspect
from functools import wraps
from typing import Any, Callable, Dict, Optional
from .checker import check_dependency
from .config import resolve_config
from smonitor import signal

[docs] def dep_digest(library: str, when: Optional[Dict[str, Any]] = None): """ Decorator to declare and enforce a dependency. Resolved dynamically at runtime to support configuration changes. """ def decorator(func: Callable): # 1. Metadata Registration (Still at definition time) if not hasattr(func, '_dependencies'): func._dependencies = [] func._dependencies.append({'library': library, 'when': when}) # Pre-compute signature sig = inspect.signature(func) module_path = func.__module__ @wraps(func) @signal(tags=["dependency"], exception_level="DEBUG") def wrapper(*args, **kwargs): # 2. RESOLVE CONFIG AT RUNTIME # This allows tests to register config AFTER function definition cfg = resolve_config(module_path) should_check = True if when is not None: bound = sig.bind(*args, **kwargs) bound.apply_defaults() args_dict = bound.arguments for k, v in when.items(): if k not in args_dict or args_dict[k] != v: should_check = False break if should_check: lib_info = cfg.libraries.get(library, {}) pypi_name = lib_info.get('pypi') check_dependency(library, pypi_name=pypi_name, caller=func.__name__, exception_class=cfg.exception_class) return func(*args, **kwargs) return wrapper return decorator