summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoe Gaumont <ngaumont@logilab.fr>2020-05-29 14:46:14 +0200
committerNoe Gaumont <ngaumont@logilab.fr>2020-05-29 14:46:14 +0200
commite333b18c03f2c1b62bdf1dd481e1f2c7a19dffec (patch)
tree979f1527f8b4dae713038e8cdd1e31fe9610e256
parent69566c7711918f8c7fbc7b45824ed4059cbdcb49 (diff)
downloadlogilab-common-e333b18c03f2c1b62bdf1dd481e1f2c7a19dffec.tar.gz
refactor(logilab.common.deprecation): add types
-rw-r--r--logilab/common/deprecation.py113
-rw-r--r--logilab/common/pytest.py2
-rw-r--r--logilab/common/textutils.py2
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