reagento / adaptix

An extremely flexible and configurable data model conversion library.
https://adaptix.readthedocs.io
Apache License 2.0
364 stars 23 forks source link

Recursion error on converter with explicit nested models #329

Open Tishka17 opened 4 weeks ago

Tishka17 commented 4 weeks ago

Code:

@dataclass
class A0:
    pass

@dataclass
class A:
    ass: Optional[list["A"]]

@impl_converter()
def a(src: A0, ass: Optional[list[A]]) -> A:
    ...

Part of result

  File "/usr/local/lib/python3.11/site-packages/adaptix/_internal/model_tools/introspection/callable.py", line 58, in get_callable_shape
    signature = inspect.signature(func)
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/inspect.py", line 3263, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/inspect.py", line 3011, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/inspect.py", line 2528, in _signature_from_callable
    return _signature_from_builtin(sigcls, obj,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/inspect.py", line 2328, in _signature_from_builtin
    return _signature_fromstr(cls, func, s, skip_bound_arg)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/inspect.py", line 2179, in _signature_fromstr
    _signature_strip_non_python_syntax(s)
  File "/usr/local/lib/python3.11/inspect.py", line 2121, in _signature_strip_non_python_syntax
    token_stream = tokenize.tokenize(generator)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tokenize.py", line 427, in tokenize
    encoding, consumed = detect_encoding(readline)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded

This code works well on 3.0.0b6 on python 3.11

Tishka17 commented 4 weeks ago

Workaround is to fill recursive fields with defaults in converter and fill manually in wrapper . This could look ugly because there are no extra fields, but in real project there can be more fields, that are processed normally

@impl_converter(
    recipe=[
        link_constant(P[A].ass, value=None),
    ]
)
def _a(src: A0) -> A:
    ...

def a(src: A0, ass: Optional[list[A]]) -> A:
    res = _a(src)
    res.ass = ass
    return res