python-attrs / python-attrs/cattrs

TypeError when registering ForwardRefs in python 3.10.2

Open
#206 15 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Python
Stars
1.1k
Forks
159
Avg merge
12h 21m
Merged PRs (30d)
6

Description

  • cattrs version: 1.10.0
  • Python version: 3.10.2
  • Operating System: Ubuntu 20.04.3
Description

This is related to #201, but probably worth making a separate issue for, since it affects one of the (previously working) workarounds mentioned in that issue. Feel free to close this if you feel this is redundant with #201 or #94.

I have a converter that registers a ForwardRef. In python 3.6.x through 3.10.1, this has worked as intended. As of python 3.10.2, this is now throwing a TypeError:

TypeError: Invalid first argument to `register()`. ForwardRef('MyClass') is not a class.

Strangely, I don't see anything in the python 3.10.2 changelog that would indicate changed behavior with ForwardRef or register().

What I Did

Minimal example:

from attr import define, field
from cattr import Converter  # Note: same behavior with GenConverter
from typing import ForwardRef, List

@define()
class MyClass:
    history: List['MyClass'] = field(factory=list)

converter = Converter()
converter.register_structure_hook(
    ForwardRef('MyClass'), lambda obj, _: converter.structure(obj, MyClass)
)

Traceback:

TypeError                                 Traceback (most recent call last)
Input In [17], in <module>
----> 1 converter.register_structure_hook(
      2     ForwardRef("MyClass"), (lambda obj, _: converter.structure(obj, MyClass))
      3 )

File ~/.virtualenvs/rc-310/lib/python3.10/site-packages/cattr/converters.py:269, in Converter.register_structure_hook(self, cl, func)
    267     self._structure_func.clear_cache()
    268 else:
--> 269     self._structure_func.register_cls_list([(cl, func)])

File ~/.virtualenvs/rc-310/lib/python3.10/site-packages/cattr/dispatch.py:57, in MultiStrategyDispatch.register_cls_list(self, cls_and_handler, direct)
     55         self._direct_dispatch[cls] = handler
     56     else:
---> 57         self._single_dispatch.register(cls, handler)
     58         self.clear_direct()
     59 self.dispatch.cache_clear()

File ~/.pyenv/versions/3.10.2/lib/python3.10/functools.py:856, in singledispatch.<locals>.register(cls, func)
    854 else:
    855     if func is not None:
--> 856         raise TypeError(
    857             f"Invalid first argument to `register()`. "
    858             f"{cls!r} is not a class."
    859         )
    860     ann = getattr(cls, '__annotations__', {})
    861     if not ann:

TypeError: Invalid first argument to `register()`. ForwardRef('MyClass') is not a class.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal ForwardRef example and inspect cattr/converters.py around register_structure_hook and cattr/dispatch.py around register_cls_list, as shown in the traceback. Reproduce the failure on Python 3.10.2 and compare supported Python versions. Done means ForwardRef registration works without the TypeError while existing converter behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.