Skip to content

Instantly share code, notes, and snippets.

@1st1
Last active September 23, 2018 16:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1st1/37fdd3cc84cd65b9af3471b935b722df to your computer and use it in GitHub Desktop.
Save 1st1/37fdd3cc84cd65b9af3471b935b722df to your computer and use it in GitHub Desktop.
A hack-ish way to fix dataclasses & get_type_hints()
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 28e9f75127..ad3dc46b67 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -868,7 +868,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
# Include InitVars and regular fields (so, not ClassVars).
flds = [f for f in fields.values()
if f._field_type in (_FIELD, _FIELD_INITVAR)]
- _set_new_attribute(cls, '__init__',
+ had_own_init = _set_new_attribute(cls, '__init__',
_init_fn(flds,
frozen,
has_post_init,
@@ -878,6 +878,9 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
'__dataclass_self__' if 'self' in fields
else 'self',
))
+ if not had_own_init:
+ cls.__init__.__module__ = cls.__module__
+ cls.__init__.__generated__ = True
# Get the fields as a list, and include only real fields. This is
# used in all of the following methods.
diff --git a/Lib/typing.py b/Lib/typing.py
index 445a42492b..3f6a916b6c 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -978,7 +978,10 @@ def get_type_hints(obj, globalns=None, localns=None):
if isinstance(obj, types.ModuleType):
globalns = obj.__dict__
else:
- globalns = getattr(obj, '__globals__', {})
+ if hasattr(obj, '__generated__'):
+ globalns = sys.modules[obj.__module__].__dict__
+ else:
+ globalns = getattr(obj, '__globals__', {})
if localns is None:
localns = globalns
elif localns is None:
@1st1
Copy link
Author

1st1 commented Sep 22, 2018

Another way to fix this:

  • Fix exec() to accept arbitrary Mappings for globals;
  • Fix dataclasses to set __globals__ attribute on functions it generates to a ModuleProxy mapping like the following:
class ModuleProxy(collections.abc.Mapping):

    def __init__(self, modname):
        self.__modname = modname

    @property
    def _mod(self):
        return sys.modules[self.__modname].__dict__

    def __getitem__(self, key):
        return self._mod[key]

    def __len__(self):
        return len(self._mod)

    def __contains__(self, x):
        return x in self._mod

    def __iter__(self):
        return iter(self._mod)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment