Source code for pyecs.helpers.Deprecation

# ruff: noqa: B010
import functools
import warnings
from collections.abc import Callable
from typing import Protocol


[docs] class DeprecatedCallable[**P, R](Protocol): """Protocol for deprecated callable with custom attributes.""" __deprecated__: bool __deprecation_reason__: str | None __use_instead__: str | None def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: ...
[docs] def deprecated_ecs[**P, R]( reason: str | None = None, use_instead: str | None = None ) -> Callable[[Callable[P, R]], Callable[P, R]]: """ ECS-specific deprecation decorator that includes version information. Args: reason: Optional reason for deprecation use_instead: Optional alternative function/method to use """ def decorator(func: Callable[P, R]) -> Callable[P, R]: @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: msg = f"PyECS: {func.__name__} is deprecated" if reason: msg += f". {reason}" if use_instead: msg += f". Use {use_instead} instead" warnings.warn(msg, DeprecationWarning, stacklevel=3) return func(*args, **kwargs) setattr(wrapper, "__deprecated__", True) setattr(wrapper, "__deprecation_reason__", reason) setattr(wrapper, "__use_instead__", use_instead) return wrapper return decorator
[docs] def deprecated_external[**P, R]( reason: str | None = None, use_instead: str | None = None, allowed_modules: list[str] | None = None, ) -> Callable[[Callable[P, R]], Callable[P, R]]: """ Deprecation decorator that only warns for external calls. Args: reason: Optional reason for deprecation use_instead: Optional alternative to suggest allowed_modules: List of module names that are allowed to call without warning """ if allowed_modules is None: allowed_modules = [] def decorator(func: Callable[P, R]) -> Callable[P, R]: @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: import inspect frame = inspect.currentframe() if frame and frame.f_back and frame.f_back.f_back: globals_dict = frame.f_back.f_back.f_globals caller_module: str = ( globals_dict.get("__name__", "") if isinstance(globals_dict.get("__name__"), str) else "" ) for allowed in allowed_modules: if caller_module.startswith(allowed): return func(*args, **kwargs) msg = f"PyECS: {func.__name__} is deprecated" if reason: msg += f". {reason}" if use_instead: msg += f". Use {use_instead} instead" warnings.warn(msg, DeprecationWarning, stacklevel=3) return func(*args, **kwargs) setattr(wrapper, "__deprecated__", True) setattr(wrapper, "__deprecation_reason__", reason) setattr(wrapper, "__use_instead__", use_instead) return wrapper return decorator
[docs] def warn_deprecated(message: str, use_instead: str | None = None, stacklevel: int = 3) -> None: """ Issue a deprecation warning for usage patterns that can't be decorated. Args: message: The deprecation message use_instead: Optional alternative to suggest stacklevel: Stack level for warning (default 3) """ msg = f"PyECS: {message}" if use_instead: msg += f". Use {use_instead} instead" warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel)