konradhalas / dacite

Simple creation of data classes from dictionaries.
MIT License
1.76k stars 106 forks source link

dacite.Config's forward_references overrides get_type_hints()'s default globalns. #129

Closed yerihyo closed 1 year ago

yerihyo commented 3 years ago

Problem

Example Code

aa.py

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, List

from dacite import from_dict, Config

if TYPE_CHECKING:
    from foxylib.tools.dataclass.tests.bb import BB

@dataclass
class AA:
    bb_list: List[BB]

    @classmethod
    def shared_function(cls):
        return "X"

    @classmethod
    def config(cls, h_aa):
        from .bb import BB

        config = Config(forward_references={"BB":BB, })
        aa: AA = from_dict(cls, h_aa,config=config)
        return aa

bb.py

from dataclasses import dataclass

from .aa import AA  # would like to keep AA import as global

@dataclass
class BB:
    name: str

    @classmethod
    def shared_function(cls):
        AA.shared_function()  # need to use AA in multiple places of bb.py

    @classmethod
    def config(cls, ):
        return {"x":"y"}

error

Traceback (most recent call last):
  File ".../venv/lib/python3.8/site-packages/dacite/core.py", line 46, in from_dict
    data_class_hints = get_type_hints(data_class, globalns=config.forward_references)
  File ".../.pyenv/versions/3.8.8/lib/python3.8/typing.py", line 1232, in get_type_hints
    value = _eval_type(value, base_globals, localns)
  File ".../.pyenv/versions/3.8.8/lib/python3.8/typing.py", line 270, in _eval_type
    return t._evaluate(globalns, localns)
  File ".../.pyenv/versions/3.8.8/lib/python3.8/typing.py", line 518, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'List' is not defined

settings

Python 3.8.8

Analysis & Suggestion

https://github.com/konradhalas/dacite/blob/d2206b2e4711859da0ea5862c395940f33693e80/dacite/core.py#L46

Is there any advantage in passing dacite.Config as get_type_hints()'s globalns instead of localns? The above problem happens because dacite passes its Config's forwards_references as get_type_hints()'s globalns and therefore get_type_hint() loses all the information available in its default globalns. I changed the dacite's from_dict function to use localns instead and with that change localns seems working fine.

        try:
            data_class_hints = get_type_hints(data_class, localns=config.forward_references)
        except NameError as error:
            raise ForwardReferenceError(str(error))

I am very new to python dataclass and typing in general, and would like to learn if there is some reason behind using globalns instead of localns.

Pull Request

https://github.com/konradhalas/dacite/pull/128