Skip to content

dataclasses's asdict() and astuple() factories should work with TypedDict and NamedTuple #8580

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

Closed
tony opened this issue Aug 20, 2022 · 1 comment

Comments

@tony
Copy link
Contributor

tony commented Aug 20, 2022

Originally posted here

Let me know if I need to scope this better. Even if this gets closed, it's also fine - if you have a better scope / new information it may be worth making a new, improved issue.

I want to downstream users to export a typed tuple and dict from my Details dataclass, dataclasses.astuple and dataclasses.asdict

#!/usr/bin/env python
import dataclasses
from typing import NamedTuple, TypedDict, get_type_hints


class DetailsDict(TypedDict):
    name: str
    age: int
    address: str


class DetailsTuple(NamedTuple):
    name: str
    age: int
    address: str


@dataclasses.dataclass
class Details:
    name: str
    age: int
    address: str

    def to_dict(self) -> DetailsDict:
        # return dataclasses.asdict(self, dict_factory=DetailsDict)
        return DetailsDict(**dataclasses.asdict(self))

    def to_tuple(self) -> DetailsTuple:
        # return dataclasses.astuple(self, tuple_factory=DetailsTuple)
        return DetailsTuple(*dataclasses.astuple(self))


john = Details(name="John", age=25, address="123 Address St")
print(john)
print(john.to_dict())
print(john.to_tuple())
print(get_type_hints(john))
print(get_type_hints(john.to_dict))
print(get_type_hints(john.to_tuple))

Output:

Details(name='John', age=25, address='123 Address St')
{'name': 'John', 'age': 25, 'address': '123 Address St'}
DetailsTuple(name='John', age=25, address='123 Address St')
{'name': <class 'str'>, 'age': <class 'int'>, 'address': <class 'str'>}
{'return': <class '__main__.DetailsDict'>}
{'return': <class '__main__.DetailsTuple'>

Why would a user want to reuse dict/tuple/etc?

  • To maintain mypy strict = True compliance

    • inside of unit testing / assertions. In my case, I have typed pytest fixtures
  • They have an open source library and want tuples / dicts to be available for downstream use.

    In practice, I wanted my dataclasses in libvcs (here) to be able to let the enduser get typed dict/tuple's

    • Spreading into functions *params, **params, e.g. loading data

    • Reuse in args/kwargs of function declarations, e.g.

      Details(*DetailsTuple(name='John', age=25, address='123 Address St'))
      Details(**DetailsDict(**{'name': 'John', 'age': 25, 'address': '123 Address St'}))

Problem

  • Using typed factories (TypedDict, NamedTuple) with the same shape / types as a dataclass don't work

Related

#8518,
python/mypy#4128

@srittau
Copy link
Collaborator

srittau commented Sep 8, 2024

I agree that this should work, but I don't think there's anything actionable here for typeshed to do. Both TypedDict and NamedTuple need to be heavily special-cased by type checkers and I don't see any way for typeshed to help with that.

The good news is that at least with mypy 1.11.2 the tuple case seems to work.

@srittau srittau closed this as not planned Won't fix, can't repro, duplicate, stale Sep 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants