diff options
Diffstat (limited to 'logilab')
-rw-r--r-- | logilab/common/deprecation.py | 113 | ||||
-rw-r--r-- | logilab/common/pytest.py | 2 | ||||
-rw-r--r-- | logilab/common/textutils.py | 2 |
3 files changed, 78 insertions, 39 deletions
diff --git a/logilab/common/deprecation.py b/logilab/common/deprecation.py index 47f2895..8b8f8a5 100644 --- a/logilab/common/deprecation.py +++ b/logilab/common/deprecation.py @@ -23,9 +23,11 @@ import os import sys from warnings import warn from functools import WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES +from typing import Any, Callable, Dict, Optional, Iterable +from typing_extensions import Protocol -def lazy_wraps(wrapped): +def lazy_wraps(wrapped: Callable) -> Callable: """ This is the equivalent of the @wraps decorator of functools except it won't try to grabs attributes of the targeted function on decoration but on access. @@ -44,19 +46,19 @@ def lazy_wraps(wrapped): >>> def wrapper(*args, **kwargs): ... """ - def update_wrapper_attributes(wrapper): - def __getattribute__(self, attribute): + def update_wrapper_attributes(wrapper: Callable) -> Callable: + def __getattribute__(self, attribute: str) -> Any: if attribute in WRAPPER_ASSIGNMENTS: return getattr(wrapped, attribute) return super(self.__class__, self).__getattribute__(attribute) - wrapper.__getattribute__ = __getattribute__ + wrapper.__getattribute__ = __getattribute__ # type: ignore for attribute in WRAPPER_UPDATES: getattr(wrapper, attribute).update(getattr(wrapped, attribute, {})) - wrapper.__wrapped__ = wrapped + wrapper.__wrapped__ = wrapped # type: ignore return wrapper @@ -67,16 +69,16 @@ class DeprecationWrapper(object): """proxy to print a warning on access to any attribute of the wrapped object """ - def __init__(self, proxied, msg=None, version=None): + def __init__(self, proxied, msg: Optional[str] = None, version: Optional[str] = None) -> None: self._proxied = proxied - self._msg = msg - self.version = version + self._msg: str = msg if msg else "" + self.version: Optional[str] = version - def __getattr__(self, attr): + def __getattr__(self, attr: str) -> Any: send_warning(self._msg, stacklevel=3, version=self.version) return getattr(self._proxied, attr) - def __setattr__(self, attr, value): + def __setattr__(self, attr: str, value: Any) -> None: if attr in ("_proxied", "_msg"): self.__dict__[attr] = value else: @@ -84,7 +86,7 @@ class DeprecationWrapper(object): setattr(self._proxied, attr, value) -def _get_module_name(number=1): +def _get_module_name(number: int = 1) -> str: """ automagically try to determine the package name from which the warning has been triggered by loop other calling frames. @@ -111,7 +113,12 @@ def _get_module_name(number=1): return file_name -def send_warning(reason, version=None, stacklevel=2, module_name=None): +def send_warning( + reason: str, + version: Optional[str] = None, + stacklevel: int = 2, + module_name: Optional[str] = None, +) -> None: """Display a deprecation message only if the version is older than the compatible version. """ @@ -125,7 +132,9 @@ def send_warning(reason, version=None, stacklevel=2, module_name=None): warn(reason, DeprecationWarning, stacklevel=stacklevel) -def callable_renamed(old_name, new_function, version=None): +def callable_renamed( + old_name: str, new_function: Callable, version: Optional[str] = None +) -> Callable: """use to tell that a callable has been renamed. It returns a callable wrapper, so that when its called a warning is printed @@ -154,10 +163,12 @@ def callable_renamed(old_name, new_function, version=None): return wrapped -renamed = callable_renamed(old_name="renamed", new_function=callable_renamed) +renamed: Callable[[str, Callable, Optional[str]], Callable] = callable_renamed( + old_name="renamed", new_function=callable_renamed +) -def argument_removed(old_argument_name, version=None): +def argument_removed(old_argument_name: str, version: Optional[str] = None) -> Callable: """ callable decorator to allow getting backward compatibility for renamed keyword arguments. @@ -170,7 +181,7 @@ def argument_removed(old_argument_name, version=None): 42 """ - def _wrap(func): + def _wrap(func: Callable) -> Callable: @lazy_wraps(func) def check_kwargs(*args, **kwargs): if old_argument_name in kwargs: @@ -192,15 +203,17 @@ def argument_removed(old_argument_name, version=None): @argument_removed("name") @argument_removed("doc") -def callable_deprecated(reason=None, version=None, stacklevel=2): +def callable_deprecated( + reason: Optional[str] = None, version: Optional[str] = None, stacklevel: int = 2 +) -> Callable: """Display a deprecation message only if the version is older than the compatible version. """ - def decorator(func): + def decorator(func: Callable) -> Callable: @lazy_wraps(func) - def wrapped(*args, **kwargs): - message = reason or 'The function "%s" is deprecated' + def wrapped(*args, **kwargs) -> Callable: + message: str = reason or 'The function "%s" is deprecated' if "%s" in message: message %= func.__name__ @@ -212,13 +225,24 @@ def callable_deprecated(reason=None, version=None, stacklevel=2): return decorator -deprecated = callable_renamed(old_name="deprecated", new_function=callable_deprecated) +class CallableDeprecatedCallable(Protocol): + def __call__( + self, reason: Optional[str] = None, version: Optional[str] = None, stacklevel: int = 2 + ) -> Callable: + ... -def class_deprecated(old_name, parent, class_dict): - class DeprecatedClass(*parent): +deprecated: CallableDeprecatedCallable = callable_renamed( + old_name="deprecated", new_function=callable_deprecated +) + + +def class_deprecated(old_name: str, parent: Iterable[type], class_dict: Dict[str, Any]) -> type: + class DeprecatedClass(*parent): # type: ignore def __init__(self, *args, **kwargs): - msg = class_dict.get("__deprecation_warning__", f"{old_name} is deprecated") + msg: str = class_dict.get( + "__deprecation_warning__", f"{old_name} is deprecated" + ) # type: ignore send_warning( msg, stacklevel=class_dict.get("__deprecation_warning_stacklevel__", 3), @@ -234,7 +258,7 @@ def class_deprecated(old_name, parent, class_dict): return DeprecatedClass -def attribute_renamed(old_name, new_name, version=None): +def attribute_renamed(old_name: str, new_name: str, version: Optional[str] = None) -> Callable: """ class decorator to allow getting backward compatibility for renamed attributes. @@ -257,21 +281,21 @@ def attribute_renamed(old_name, new_name, version=None): True """ - def _class_wrap(klass): + def _class_wrap(klass: type) -> type: reason = ( f"{klass.__name__}.{old_name} has been renamed and is deprecated, use " f"{klass.__name__}.{new_name} instead" ) - def _get_old(self): + def _get_old(self) -> Any: send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__) return getattr(self, new_name) - def _set_old(self, value): + def _set_old(self, value: Any) -> None: send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__) setattr(self, new_name, value) - def _del_old(self): + def _del_old(self) -> None: send_warning(reason, stacklevel=3, version=version, module_name=klass.__module__) delattr(self, new_name) @@ -282,7 +306,7 @@ def attribute_renamed(old_name, new_name, version=None): return _class_wrap -def argument_renamed(old_name, new_name, version=None): +def argument_renamed(old_name: str, new_name: str, version: Optional[str] = None) -> Callable: """ callable decorator to allow getting backward compatibility for renamed keyword arguments. @@ -296,9 +320,9 @@ def argument_renamed(old_name, new_name, version=None): 42 """ - def _wrap(func): + def _wrap(func: Callable) -> Callable: @lazy_wraps(func) - def check_kwargs(*args, **kwargs): + def check_kwargs(*args, **kwargs) -> Callable: if old_name in kwargs and new_name in kwargs: raise ValueError( f"argument {old_name} of callable {func.__name__} has been " @@ -326,7 +350,9 @@ def argument_renamed(old_name, new_name, version=None): @argument_renamed(old_name="modpath", new_name="module_path") @argument_renamed(old_name="objname", new_name="object_name") -def callable_moved(module_name, object_name, version=None, stacklevel=2): +def callable_moved( + module_name: str, object_name: str, version: Optional[str] = None, stacklevel: int = 2 +) -> Callable: """use to tell that a callable has been moved to a new module. It returns a callable wrapper, so that when its called a warning is printed @@ -352,10 +378,18 @@ def callable_moved(module_name, object_name, version=None, stacklevel=2): return callnew -moved = callable_renamed(old_name="moved", new_function=callable_moved) +moved: Callable[[Optional[str], Optional[str], int], Callable] = callable_renamed( + old_name="moved", new_function=callable_moved +) -def class_renamed(old_name, new_class, message=None, version=None, module_name=None): +def class_renamed( + old_name: str, + new_class: type, + message: Optional[str] = None, + version: Optional[str] = None, + module_name: Optional[str] = None, +) -> type: """automatically creates a class which fires a DeprecationWarning when instantiated. @@ -365,7 +399,7 @@ def class_renamed(old_name, new_class, message=None, version=None, module_name=N s = Set() >>> """ - class_dict = {} + class_dict: Dict[str, Any] = {} if message is None: message = "%s is deprecated, use %s instead" % (old_name, new_class.__name__) @@ -381,7 +415,12 @@ def class_renamed(old_name, new_class, message=None, version=None, module_name=N return class_deprecated(old_name, (new_class,), class_dict) -def class_moved(new_class, old_name=None, message=None, version=None): +def class_moved( + new_class: type, + old_name: Optional[str] = None, + message: Optional[str] = None, + version: Optional[str] = None, +) -> type: """nice wrapper around class_renamed when a class has been moved into another module """ diff --git a/logilab/common/pytest.py b/logilab/common/pytest.py index 88e22f9..e0758c4 100644 --- a/logilab/common/pytest.py +++ b/logilab/common/pytest.py @@ -100,7 +100,7 @@ import re import sys import os.path as osp from time import process_time, time -from re import Match +from re import Match # type: ignore import warnings import types import inspect diff --git a/logilab/common/textutils.py b/logilab/common/textutils.py index 95464e5..cfb48d7 100644 --- a/logilab/common/textutils.py +++ b/logilab/common/textutils.py @@ -46,7 +46,7 @@ __docformat__ = "restructuredtext en" import sys import re import os.path as osp -from re import Pattern, Match +from re import Pattern, Match # type: ignore from warnings import warn from unicodedata import normalize as _uninormalize from typing import Any, Optional, Tuple, List, Callable, Dict, Union |