Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypedDict is treated as different class as Dict[str, Any] #96

Closed
hal-314 opened this issue Apr 14, 2022 · 5 comments
Closed

TypedDict is treated as different class as Dict[str, Any] #96

hal-314 opened this issue Apr 14, 2022 · 5 comments
Labels

Comments

@hal-314
Copy link

hal-314 commented Apr 14, 2022

Hi

Thank you for doing this great library!

Current behavior

Currently, I'm facing a strange behavior. I want to annotate a return type of certain method with a TypedDict when it's parent is typed as Dict[str, Any]. However, overrides complains that the return type isn't compatible.

See the example:

from typing import Any, Dict, TypedDict
from overrides import overrides

class MyTypedDict(TypedDict):
    foo: float
    bar: float
        

class A:
    def func(self) -> Dict[str, Any]:
        pass
    
class B(A):
    @overrides
    def func(self) -> Dict[str, float]: # <- works
#     def func(self) -> MyTypedDict: <- fails
        pass

The error is TypeError: B.func: return type `<class '__main__.MyTypedDict'>` is not a `typing.Dict[str, typing.Any].

Looking at the code, the error may come from typing_utils.issubtype.

from typing_utils import issubtype 
issubtype(MyTypedDict, Dict[str, Any]) # Returns False when it should be True

Ideally

From my understanding, it should work as MyTypedDict is a valid return type for typing.Dict[str, typing.Any].

Workaround

Set check_signature to False -> @overrides(check_signature=False)
Sadly, I can't use dataclasses as the library that I'm using doesn't support them.

@ashwin153
Copy link
Collaborator

Hmm this is an interesting corner case. I will look into it later, but FWIW the workaround has nothing to do with dataclasses. Simply add that parameter to @OVERRIDES when you decorate a method to disable type-checking.

class B(A):
    @overrides(check_signature=False)
    def func(self) -> MyTypedDict: 
        pass

@hal-314
Copy link
Author

hal-314 commented Apr 19, 2022

Thank you to look into :)

@mkorpela mkorpela added the bug label Oct 7, 2022
@mkorpela
Copy link
Owner

mkorpela commented Oct 7, 2022

As TypedDict is such an odd type, this requires somewhat more work.
TypedDict itself is nothing but only exists in subtypes. Which are dictionaries with constraint keys.

class A(TypedDict):
      x: string
      y: int

class B(TypedDict):
      z: float

class C(A, B):
     pass

A is Dict[str, Any]
B is Dict[str, Any] also Dict[str, float]
A and B are not compatible.
C is A, C is B.

@mkorpela
Copy link
Owner

mkorpela commented Oct 7, 2022

# From TypedDict source :/ 

    def __subclasscheck__(cls, other):
        # Typed dicts are only for static structural subtyping.
        raise TypeError('TypedDict does not support instance and class checks')

@mkorpela
Copy link
Owner

mkorpela commented Oct 8, 2022

TypedDict is now treated like Dict[str, Any]. This does not completely solve all the corner cases where TypedDict would actually have a clearer structural subtyping https://wiki.c2.com/?StructuralSubtyping but should make most of the problematic cases pass.

@mkorpela mkorpela closed this as completed Oct 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants