How to evaluate Forward/Strings Refs

Hi folks, I'm working on a library that makes extensive of use of type hints at runtime and I found this use case where I need to evaluate a forward reference ```python def test_forward_references(): @dataclasses.dataclass class User: username: str @dataclasses.dataclass class Product: reviews: List["Review"] @dataclasses.dataclass class Review: text: str ``` as youy can see List["Review"] has a reference to the not yet declared class Review. My use case is to generate class based on the type hints of these 3 classes, unfortunately when I try to inspect the field type of Product.reviews, I get List[ForwardRef("Review)] and I'm not sure how I could get the actual type at run time. I've made a repl that shows what I've tried so far: https://repl.it/@patrick91/dcth-2 I've tried getting the global namespace for the module, but that won't work in this case, since the classes are defined inside a function. I have another option, which is to store all the decorated classes in a global dict (I'm using a different decorator that the dataclasses one), but I don't want to do that as my users could register multiple types with the same name. Any ideas on how I could solve this?

Perhaps you can pass the locals dict to get_type_hints()? https://docs.python.org/3/library/typing.html#typing.get_type_hints On Thu, Jul 16, 2020 at 06:52 Patrick Arminio <patrick.arminio@gmail.com> wrote:
-- --Guido (mobile)

On Thu, 16 Jul 2020 at 15:17, Guido van Rossum <guido@python.org> wrote:
Perhaps you can pass the locals dict to get_type_hints()?
locals would probably the right options, but unfortunately I don't have access to the locals where the type was defined, as I'd need to call get_type_hints from another function. Maybe I'm missing something obvious?

On Thu, 16 Jul 2020 at 15:24, Guido van Rossum <guido@python.org> wrote:
Mainly because I'm writing them in tests. I guess I could "force" users of the library to not do something like that, as it is a very specific use case (I can't think of anything else other than tests for something like this). So, I'm guess the only solution would be to move the classes outside the tests and import the module dynamically, when dealing with ForwardRefs, like I did here? [...snip] def get_first_field_type(type: Type) -> Type: field = dataclasses.fields(type)[0] field_type = field.type # one option would be to use something like this, but it would # only work when the type was declared in the module namespace # and also might introduce side effects if the imported module # runs code at import time # def resolve_hint(hint): # def f(x: field_type): pass # namespace = importlib.import_module(type.__module__).__dict__ # return get_type_hints(f, None, namespace)['x'] -- Patrick Arminio

Perhaps you can pass the locals dict to get_type_hints()? https://docs.python.org/3/library/typing.html#typing.get_type_hints On Thu, Jul 16, 2020 at 06:52 Patrick Arminio <patrick.arminio@gmail.com> wrote:
-- --Guido (mobile)

On Thu, 16 Jul 2020 at 15:17, Guido van Rossum <guido@python.org> wrote:
Perhaps you can pass the locals dict to get_type_hints()?
locals would probably the right options, but unfortunately I don't have access to the locals where the type was defined, as I'd need to call get_type_hints from another function. Maybe I'm missing something obvious?

On Thu, 16 Jul 2020 at 15:24, Guido van Rossum <guido@python.org> wrote:
Mainly because I'm writing them in tests. I guess I could "force" users of the library to not do something like that, as it is a very specific use case (I can't think of anything else other than tests for something like this). So, I'm guess the only solution would be to move the classes outside the tests and import the module dynamically, when dealing with ForwardRefs, like I did here? [...snip] def get_first_field_type(type: Type) -> Type: field = dataclasses.fields(type)[0] field_type = field.type # one option would be to use something like this, but it would # only work when the type was declared in the module namespace # and also might introduce side effects if the imported module # runs code at import time # def resolve_hint(hint): # def f(x: field_type): pass # namespace = importlib.import_module(type.__module__).__dict__ # return get_type_hints(f, None, namespace)['x'] -- Patrick Arminio
participants (3)
-
Guido van Rossum
-
Jakub Stasiak
-
Patrick Arminio