Search code examples
pythonpython-3.7

Can typing.Mapping and such be used with functools.singledispatch in 3.7 and up?


I had a function using functools.singledispatch with generics break after an upgrade to version 3.7. What used to work in 3.6:

>>> from functools import singledispatch                             
>>> from typing import Mapping                                   
>>> @singledispatch                                 
... def f(_):                               
...  raise NotImplementedError
... 
>>> @f.register(Mapping)
... def _(x):
...  return x
... 

now raises

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/user/cpython/Lib/functools.py", line 831, in register
    raise TypeError(
TypeError: Invalid first argument to `register()`: typing.Mapping. Use either `@register(some_class)` or plain `@register` on an annotated function.

Registering an annotated version fares no better:

>>> @f.register
... def _(x: Mapping):
...  return x
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/home/user/cpython/Lib/functools.py", line 842, in register
    raise TypeError(
TypeError: Invalid annotation for 'x'. typing.Mapping is not a class.

Have I been misusing single dispatch and generics before, and is there a way to achieve equivalent behaviour in version 3.7 and up?


Solution

  • You need collections.abc.Mapping. Generic classes are mostly incompatible with any sort of runtime checks.

    What little compatibility exists is mostly undocumented and changing frequently. For example, typing.Mapping used to be a class, and the docs still say it's a class, but it's really just pretending to be one now. functools.singledispatch needs a real class.