Skip to content

NewType support broken by recent functools.singledispatch changes #297

@bkurtz

Description

@bkurtz
  • cattrs version: main (290d162)
  • Python version: 3.9.10
  • Operating System: macOS 12.5.1

Description

NewType support was recently added in #255 (see also #94), but appears to have been broken by recent changes to functools.singledispatch that landed in recent pythons (see, e.g. #206, maybe #216).

Aspirationally, something like this should work:

IsoDate = NewType("IsoDate", dt.datetime)
conv.register_structure_hook(IsoDate, ...)

What I Did

python 3.9.10 venv:

Python 3.9.10 (main, Jan 15 2022, 11:48:15) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime as dt
>>> from typing import NewType
>>> import cattrs
>>> conv = cattrs.Converter()
>>> IsoDate = NewType("IsoDate", dt.datetime)
>>> conv.register_structure_hook(IsoDate, lambda s, _: IsoDate(dt.datetime.fromisoformat(s)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/private/tmp/ctest/lib/python3.9/site-packages/cattrs/converters.py", line 254, in register_structure_hook
    self._structure_func.register_cls_list([(cl, func)])
  File "/private/tmp/ctest/lib/python3.9/site-packages/cattrs/dispatch.py", line 57, in register_cls_list
    self._single_dispatch.register(cls, handler)
  File "/usr/local/Cellar/python@3.9/3.9.10/Frameworks/Python.framework/Versions/3.9/lib/python3.9/functools.py", line 855, in register
    raise TypeError(
TypeError: Invalid first argument to `register()`. <function NewType.<locals>.new_type at 0x1025a4c10> is not a class.

python 3.8.9 (because that's the other one I had handy) venv:

Python 3.8.9 (default, Mar 30 2022, 13:51:17) 
[Clang 13.1.6 (clang-1316.0.21.2.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime as dt
>>> from typing import NewType
>>> import cattrs
>>> conv = cattrs.Converter()
>>> IsoDate = NewType("IsoDate", dt.datetime)
>>> conv.register_structure_hook(IsoDate, lambda s, _: IsoDate(dt.datetime.fromisoformat(s)))
>>> conv.structure("2022-01-01", IsoDate)
datetime.datetime(2022, 1, 1, 0, 0)

(also tested this in python:3.9.9-alpine docker image and get similar results to 3.8.9.)

Not sure what the best strategy for this in cattrs would be, but going to be doing some working around it for our local project this week, and if it were easy I might be talked into opening a PR here too.

FWIW, the other common case we're having issues with is generics (related to #216).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions