On Fri, 24 Jul 2020 at 00:57, Guido van Rossum <guido@python.org> wrote:
You forgot to show us your definition for LazyType. And, honestly, a lot more context explaining what you're trying to do and why.
Sure, my bad, I thought the implementation of LazyType was quite simple and not needed. Here it is:

class LazyType:
    type_name: str
    module: str
    package: Optional[str]

    def __class_getitem__(cls, params):
        type_name, module = params

        package = None

        if module.startswith("."):
            current_frame = inspect.currentframe()
            package = current_frame.f_back.f_globals["__package__"]

        return cls(type_name, module, package)

    def resolve_type(self) -> Type:
        module = importlib.import_module(self.module, self.package)

        return module.__dict__[self.type_name]

The reason why I need to have this class is so that when I create the GraphQL type I can get access to the information of the type referenced by each field.
And since types can reference each other I need to have some way of importing the types "lazily", here's an example:

class TypeA:
    type_b: TypeB

class TypeB:
    type_a: TypeA

The example above works when the types are defined in the same file and using string annotations (I need to work on supporting from __future__ import annotations).
But this breaks when the types are defined in two different files, like here: https://github.com/strawberry-graphql/strawberry/blob/master/tests/test_cyclic/type_a.py

This could be solved by having a global type registry, but I don't want to have that since I want users to be able to define multiple types with the same name in the same
python application.

Hope this helps, happy to share more context if anything is unclear.
But passing ".type_a" to any generic type isn't going to work in mypy, it's always going to be treated as a syntax error.
Oh, that's interesting. I guess I can remove the functionality of finding the package name then, it's not a huge deal.
Maybe you can play some game with `if TYPE_CHECKING` inside strawberry so that mypy thinks that LazyType is an alias for Annotated, but at runtime it's something else?
I'll try this tomorrow :) Thank you

On Thu, Jul 23, 2020 at 3:14 PM Patrick Arminio <patrick.arminio@gmail.com> wrote:
Hello again, I'm working on a library that uses the return type of methods at runtime to generate GraphQL types and I'm trying to
deal with the case of circular imports and classes referencing each others.

I was thinking of create a LazyType where you can pass the type name and the module, so that we are able to resolve
the actual type at runtime without having to guess where it came from, like this:

import typing

import strawberry

if typing.TYPE_CHECKING:
    from .type_a import TypeA

class TypeB:
    def type_a(self, info) -> strawberry.LazyType["TypeA", ".type_a"]:
        from .type_a import TypeA

        return TypeA()

As previous post unfortunately I can't relay on sys.module, since the type is only imported when TYPE_CHECKING is True or inside the method.
Now, I managed to make it work with a custom LazyType class, so the annotation above works;

strawberry.LazyType["TypeA", ".type_a"]

I don't dislike this to be honest, but looks like mypy isn't really happy, as you can see from the errors here:

tests/test_cyclic/type_b.py:13: error: "LazyType" expects no type arguments, but 2 given
tests/test_cyclic/type_b.py:13: error: Invalid type comment or annotation
tests/test_cyclic/type_b.py:16: error: Incompatible return value type (got "TypeA", expected "LazyType")

The first one could be resolve by using generics, but I'm not sure that's a good idea. The last one could be solved by creating a plugin, hopefully it won't be too difficult.
But I have no clue about `Invalid type comment or annotation`. Why would that be the case?

I noticed that the error disappears when removing the dot from the module name, but I'd like to be able to pass a dot there, so
users of the library don't have to type the full module, since we can infer that.

Any ideas? Or do I need to change the approach for this? Maybe doing a less typehint-y way:

def x() -> strawberry.LazyType("TypeA", ".type_a"):

Let me know! :)
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
Member address: guido@python.org

--Guido van Rossum (python.org/~guido)

Patrick Arminio