From 9661a5c4913e4be314572e12a49c215e7631db88 Mon Sep 17 00:00:00 2001 From: Andrey Rakhmatullin Date: Sun, 7 May 2023 18:50:23 +0400 Subject: [PATCH] Add typing to scrapy/utils/deprecate.py. --- scrapy/utils/deprecate.py | 59 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/scrapy/utils/deprecate.py b/scrapy/utils/deprecate.py index f4d6e0451..ab2719bb3 100644 --- a/scrapy/utils/deprecate.py +++ b/scrapy/utils/deprecate.py @@ -2,12 +2,12 @@ import inspect import warnings -from typing import List, Tuple +from typing import Any, List, Optional, Tuple, Type, overload from scrapy.exceptions import ScrapyDeprecationWarning -def attribute(obj, oldattr, newattr, version="0.12"): +def attribute(obj: Any, oldattr: str, newattr: str, version: str = "0.12") -> None: cname = obj.__class__.__name__ warnings.warn( f"{cname}.{oldattr} attribute is deprecated and will be no longer supported " @@ -18,16 +18,16 @@ def attribute(obj, oldattr, newattr, version="0.12"): def create_deprecated_class( - name, - new_class, - clsdict=None, - warn_category=ScrapyDeprecationWarning, - warn_once=True, - old_class_path=None, - new_class_path=None, - subclass_warn_message="{cls} inherits from deprecated class {old}, please inherit from {new}.", - instance_warn_message="{cls} is deprecated, instantiate {new} instead.", -): + name: str, + new_class: type, + clsdict: Optional[dict[str, Any]] = None, + warn_category: Type[Warning] = ScrapyDeprecationWarning, + warn_once: bool = True, + old_class_path: Optional[str] = None, + new_class_path: Optional[str] = None, + subclass_warn_message: str = "{cls} inherits from deprecated class {old}, please inherit from {new}.", + instance_warn_message: str = "{cls} is deprecated, instantiate {new} instead.", +) -> type: """ Return a "deprecated" class that causes its subclasses to issue a warning. Subclasses of ``new_class`` are considered subclasses of this class. @@ -53,17 +53,20 @@ def create_deprecated_class( OldName. """ - class DeprecatedClass(new_class.__class__): - deprecated_class = None - warned_on_subclass = False + # https://github.com/python/mypy/issues/4177 + class DeprecatedClass(new_class.__class__): # type: ignore[misc, name-defined] + deprecated_class: Optional[type] = None + warned_on_subclass: bool = False - def __new__(metacls, name, bases, clsdict_): + def __new__( + metacls, name: str, bases: Tuple[type, ...], clsdict_: dict[str, Any] + ) -> type: cls = super().__new__(metacls, name, bases, clsdict_) if metacls.deprecated_class is None: metacls.deprecated_class = cls return cls - def __init__(cls, name, bases, clsdict_): + def __init__(cls, name: str, bases: Tuple[type, ...], clsdict_: dict[str, Any]): meta = cls.__class__ old = meta.deprecated_class if old in bases and not (warn_once and meta.warned_on_subclass): @@ -81,10 +84,10 @@ def create_deprecated_class( # see https://www.python.org/dev/peps/pep-3119/#overloading-isinstance-and-issubclass # and https://docs.python.org/reference/datamodel.html#customizing-instance-and-subclass-checks # for implementation details - def __instancecheck__(cls, inst): + def __instancecheck__(cls, inst: Any) -> bool: return any(cls.__subclasscheck__(c) for c in (type(inst), inst.__class__)) - def __subclasscheck__(cls, sub): + def __subclasscheck__(cls, sub: type) -> bool: if cls is not DeprecatedClass.deprecated_class: # we should do the magic only if second `issubclass` argument # is the deprecated class itself - subclasses of the @@ -98,7 +101,7 @@ def create_deprecated_class( mro = getattr(sub, "__mro__", ()) return any(c in {cls, new_class} for c in mro) - def __call__(cls, *args, **kwargs): + def __call__(cls, *args: Any, **kwargs: Any) -> Any: old = DeprecatedClass.deprecated_class if cls is old: msg = instance_warn_message.format( @@ -125,7 +128,7 @@ def create_deprecated_class( return deprecated_cls -def _clspath(cls, forced=None): +def _clspath(cls: type, forced: Optional[str] = None) -> str: if forced is not None: return forced return f"{cls.__module__}.{cls.__name__}" @@ -134,7 +137,17 @@ def _clspath(cls, forced=None): DEPRECATION_RULES: List[Tuple[str, str]] = [] -def update_classpath(path): +@overload +def update_classpath(path: str) -> str: + ... + + +@overload +def update_classpath(path: Any) -> Any: + ... + + +def update_classpath(path: Any) -> Any: """Update a deprecated path from an object with its new location""" for prefix, replacement in DEPRECATION_RULES: if isinstance(path, str) and path.startswith(prefix): @@ -147,7 +160,7 @@ def update_classpath(path): return path -def method_is_overridden(subclass, base_class, method_name): +def method_is_overridden(subclass: type, base_class: type, method_name: str) -> bool: """ Return True if a method named ``method_name`` of a ``base_class`` is overridden in a ``subclass``.