[New-bugs-announce] [issue44919] TypedDict subtypes ignore any other metaclasses in 3.9+

Nikita Sobolev report at bugs.python.org
Sun Aug 15 08:23:36 EDT 2021


New submission from Nikita Sobolev <mail at sobolevn.me>:

Some context. I have a `User` class defined as a `TypedDict`:

```python
from typing import TypedDict

class User(TypedDict):
    name: str
    registered: bool
```

Now, I want to check if some `dict` is an instance of `User` like so: `isinstance(my_dict, User)`. But, I can't. Because it raises `TypeError('TypedDict does not support instance and class checks')`. 

Ok. Let's change `__instancecheck__` method then. We can only do this in a metaclass:

```python
from typing import _TypedDictMeta

class UserDictMeta(_TypedDictMeta):
    def __instancecheck__(cls, arg: object) -> bool:
        return (
            isinstance(arg, dict) and
            isinstance(arg.get('name'), str) and
            isinstance(arg.get('registered'), bool)
        )


class UserDict(User, metaclass=UserDictMeta):
    ...
```

It looks like it should work. It used to work like this in Python3.7 and Python3.8.

But since Python3.9 it does not work.
Let's try to debug what happens:

```python
print(type(UserDict))  # <class 'typing._TypedDictMeta'>
print(UserDict.__instancecheck__(UserDict, {}))  # TypeError: TypedDict does not support instance and class checks
```

It looks like my custom `UserDictMeta` is completely ignored.
And I cannot change how `__instancecheck__` behaves.

I suspect that the reason is in these 2 lines: https://github.com/python/cpython/blob/ad0a8a9c629a7a0fa306fbdf019be63c701a8028/Lib/typing.py#L2384-L2385

What's the most unclear in this behavior is that it does not match regular Python subclass patterns. Simplified example of the same behavior, using only primite types:

```python
class FirstMeta(type):
    def __instancecheck__(cls, arg: object) -> bool:
        raise TypeError('You cannot use this type in isinstance() call')


class First(object, metaclass=FirstMeta):
    ...

# User space:

class MyClass(First): # this looks like a user-define TypedDict subclass
    ...


class MySubClassMeta(FirstMeta):
    def __instancecheck__(cls, arg: object) -> bool:
        return True  # just an override example


class MySubClass(MyClass, metaclass=MySubClassMeta):
    ...

print(isinstance(1, MySubClass))  # True
print(isinstance(1, MyClass))  # TypeError
```

As you can see our `MySubClassMeta` works perfectly fine this way.

I suppose that this is a bug in `TypedDict`, not a desired behavior. Am I correct?

----------
components: Library (Lib)
messages: 399615
nosy: sobolevn
priority: normal
severity: normal
status: open
title: TypedDict subtypes ignore any other metaclasses in 3.9+
type: behavior
versions: Python 3.10, Python 3.11, Python 3.9

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue44919>
_______________________________________


More information about the New-bugs-announce mailing list