jacebrowning / datafiles

A file-based ORM for Python dataclasses.
https://datafiles.readthedocs.io
MIT License
198 stars 18 forks source link

TypedDict is not supported #266

Open tsuga opened 2 years ago

tsuga commented 2 years ago

Hi there,

I'm new to datafiles but found it very useful. However, my codebase has a TypedDict, which causes an error.

from datafiles import datafile
from dataclasses import dataclass
from typing import TypedDict  # for Python 3.8 or newer
# from typing_extensions import TypedDict  # for Python 3.7 or older

class Job(TypedDict):
    title: str
    salary: int 

@datafile("{self.name}.yml")
class Person:
    name: str
    nationality: str
    job: Job

Person("Bob", "UK", {"name":"CEO", "salary": 999999999})

I got the following error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[<ipython-input-30-7f9cbb849bd8>](https://localhost:8080/#) in <module>()
     15     job: Job
     16 
---> 17 Person("Bob", "UK", {"name":"CEO", "salary": 999999999})

3 frames
[/usr/local/lib/python3.7/dist-packages/datafiles/model.py](https://localhost:8080/#) in modified_init(self, *args, **kwargs)
     81         with hooks.disabled():
     82             init(self, *args, **kwargs)
---> 83         Model.__post_init__(self)
     84 
     85     cls.__init__ = modified_init

[/usr/local/lib/python3.7/dist-packages/datafiles/model.py](https://localhost:8080/#) in __post_init__(self)
     16         log.debug(f"Initializing {self.__class__} object")
     17 
---> 18         self.datafile = create_mapper(self)
     19 
     20         if settings.HOOKS_ENABLED:

[/usr/local/lib/python3.7/dist-packages/datafiles/mapper.py](https://localhost:8080/#) in create_mapper(obj, root)
    286             self_name = f"self.{field.name}"
    287             if pattern is None or self_name not in pattern:
--> 288                 attrs[field.name] = map_type(resolve(field.type, obj), name=field.name)  # type: ignore
    289 
    290     return Mapper(

[/usr/local/lib/python3.7/dist-packages/datafiles/converters/__init__.py](https://localhost:8080/#) in map_type(cls, name, item_cls)
    165         return Enumeration.of_type(cls)
    166 
--> 167     raise TypeError(f"Could not map type: {cls}")

TypeError: Could not map type: <class '__main__.Job'>
jacebrowning commented 2 years ago

While it doesn't add support for enforcing the TypedDict schema, https://github.com/jacebrowning/datafiles/pull/269 should at least fix the TypeError and treat the attribute as a Dict[str, Any], similar to how Dict annotations are handled: https://datafiles.readthedocs.io/en/latest/types/containers/#dictionaries