Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

close: 115 infinite recursion when dataclass decorator comes after match class typing #116

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions strongtyping/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
@created: 20.07.20
@author: felix
"""
import inspect
import logging
import os
from types import MethodType
from typing import Any, Type, Union
from typing import Type, Union

from strongtyping.config import SEVERITY_LEVEL

Expand Down Expand Up @@ -109,7 +108,7 @@ def action(f, frefs, type_function): # type: ignore
"""
if f.__qualname__ == action.qualname:
if any(action.f[fref] is not None for fref in frefs.split("_")):
raise AttributeError(f"decorator defined twice")
raise AttributeError("decorator defined twice")
else:
action.f.update({}.fromkeys(action.f, None)) # reset all values to None
action.qualname = f.__qualname__
Expand Down
2 changes: 1 addition & 1 deletion strongtyping/easy_property/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def action(f, frefs):
"""
if f.__qualname__ == action.qualname:
if any(action.f[fref] is not None for fref in frefs.split("_")):
raise AttributeError(f"decorator defined twice")
raise AttributeError("decorator defined twice")
else:
action.f.update({}.fromkeys(action.f, None)) # reset all values to None
action.qualname = f.__qualname__
Expand Down
18 changes: 13 additions & 5 deletions strongtyping/strong_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
default_return_queue,
)

# CACHE_IGNORE_CLASS_FUNCTIONS = ("__init__", "__str__", "__repr__")
CACHE_IGNORE_CLASS_FUNCTIONS = ("__init__",)


def match_typing(
_func=None,
Expand All @@ -52,11 +55,14 @@ def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
if arg_names and severity_level > SEVERITY_LEVEL.DISABLED.value:

args = remove_subclass(args, subclass)
if cached_set is not None and func.__name__ not in ("__init__",):

if cached_set is not None and func.__name__ not in CACHE_IGNORE_CLASS_FUNCTIONS:
# check if func with args and kwargs was checked once before with positive result
cached_key = (func, args.__str__(), kwargs.__str__())
arg_vals = args.__str__() if args else None
kwarg_vals = kwargs.__str__() if args else None
cached_key = (func, arg_vals, kwarg_vals)

if cached_key in cached_set:
return func(*args, **kwargs)

Expand Down Expand Up @@ -104,7 +110,7 @@ def inner(*args, **kwargs):
else:
warnings.warn(msg, RuntimeWarning)

if cached_set is not None and func.__name__ not in ("__init__",):
if cached_set is not None and func.__name__ not in CACHE_IGNORE_CLASS_FUNCTIONS:
cached_set.add(cached_key)
return func(*args, **kwargs)

Expand Down Expand Up @@ -220,6 +226,8 @@ def __find_methods(_cls):
and __has_annotations__(getattr(_cls, func))
and not hasattr(getattr(_cls, func), "__fe_strng_mtch__")
and not isinstance(getattr(_cls, func), classmethod)
and len(list(inspect.signature(getattr(_cls, func)).parameters.keys()))
> 1 # if it is a function without parameter there is no need to wrap it
]

def __add_decorator(_cls):
Expand Down Expand Up @@ -252,7 +260,7 @@ def inner(*args, **cls_kwargs):
return inner

if cls is not None:
from typing import Type, _TypedDictMeta
from typing import _TypedDictMeta

try:
from typing_extensions import _TypedDictMeta as _TypedDictMetaExtension
Expand Down
4 changes: 2 additions & 2 deletions strongtyping/strong_typing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from collections.abc import Callable, Iterable
from functools import lru_cache, partial
from queue import Queue
from typing import Any, TypeVar, _GenericAlias, _SpecialForm, _type_repr # type: ignore
from typing import Any, TypeVar # type: ignore

try:
from typing import _AnyMeta
Expand All @@ -29,7 +29,7 @@
from strongtyping_modules.strongtyping_modules import list_elements # type: ignore
from strongtyping_modules.strongtyping_modules import set_elements # type: ignore
from strongtyping_modules.strongtyping_modules import tuple_elements # type: ignore
except ImportError as e:
except ImportError:
extension_module: bool = False
else:
extension_module = bool(int(os.environ["ST_MODULES_INSTALLED"]))
Expand Down
63 changes: 63 additions & 0 deletions strongtyping/tests/test_dataclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
@created: 12.02.23
@author: felix
"""
from dataclasses import dataclass, field

import pytest

from strongtyping.strong_typing import match_class_typing
from strongtyping.strong_typing_utils import TypeMisMatch


def test_create_instance():
@match_class_typing
@dataclass
class MyType:
value: float = None

def method(self):
pass

req = MyType(2.3)
assert req is not None
assert req.value == 2.3


def test_call_method():
@match_class_typing
@dataclass()
class MyType:
value: float = None

def method(self):
pass

req = MyType(2.3)
assert req.method() is None


def test_dataclass_with_function_annotation():
@match_class_typing
@dataclass
class D:
x: list = field(default_factory=list)

def add(self, element: list[int]):
self.x += element

datacls = D()
assert datacls is not None

with pytest.raises(TypeMisMatch):
datacls.add("2")

with pytest.raises(TypeMisMatch):
datacls.add(["2"])

datacls.add([2])
assert datacls.x == [2]


if __name__ == "__main__":
pytest.main(["-vv", "-s", __file__])
2 changes: 1 addition & 1 deletion strongtyping/type_namedtuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""
from collections import namedtuple
from keyword import iskeyword
from typing import Any, List, Tuple, Union
from typing import List, Tuple, Union

from strongtyping.docstring_typing import check_doc_str_type
from strongtyping.strong_typing import check_type, match_typing
Expand Down