prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add mypy plugin for strawberry.union
  We have a function to create union types and also add name and description that are used when generating the GraphQL schema[1].
  
  Unfortunately MyPy complains when using the annotation, since it doesn't recognise it as a type, here's an example:
  
  ```python
  ExampleUnion = strawberry.union(
      "ExampleUnion",
      types=(
          ABC,
          CDE,
      ),
  )
  
  @strawberry.mutation
  def abc(phone_number: str) -> ExampleUnion:
      # ...
  ```
  
  Error:
  
  ```
  Variable "api.mutations.abc.ExampleUnion" is not valid as a type
  ```
  
  [1] This is why we can't use typing.Union directly, as we can't pass arguments to it
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `strawberry/ext/mypy_plugin.py`
  Content:
  ```
  1 from typing import Callable, Optional
  2 
  3 from mypy.plugin import AnalyzeTypeContext, ClassDefContext, Plugin
  4 from mypy.plugins import dataclasses
  5 from mypy.types import Type
  6 
  7 
  8 def lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:
  9     type_name = ctx.type.args[0]
  10     type_ = ctx.api.analyze_type(type_name)
  11 
  12     return type_
  13 
  14 
  15 def private_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:
  16     type_name = ctx.type.args[0]
  17     type_ = ctx.api.analyze_type(type_name)
  18 
  19     return type_
  20 
  21 
  22 class StrawberryPlugin(Plugin):
  23     def get_type_analyze_hook(self, fullname: str):
  24         if fullname == "strawberry.lazy_type.LazyType":
  25             return lazy_type_analyze_callback
  26 
  27         if any(
  28             name in fullname
  29             for name in {"strawberry.private.Private", "strawberry.Private"}
  30         ):
  31             return private_type_analyze_callback
  32 
  33         return None
  34 
  35     def get_class_decorator_hook(
  36         self, fullname: str
  37     ) -> Optional[Callable[[ClassDefContext], None]]:
  38         if any(
  39             strawberry_decorator in fullname
  40             for strawberry_decorator in {
  41                 "strawberry.type",
  42                 "strawberry.federation.type",
  43                 "strawberry.input",
  44                 "strawberry.interface",
  45             }
  46         ):
  47             return dataclasses.dataclass_class_maker_callback
  48         return None
  49 
  50 
  51 def plugin(version: str):
  52     return StrawberryPlugin
  53
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/strawberry/ext/mypy_plugin.py b/strawberry/ext/mypy_plugin.py
  --- a/strawberry/ext/mypy_plugin.py
  +++ b/strawberry/ext/mypy_plugin.py
  @@ -1,8 +1,23 @@
   from typing import Callable, Optional
   
  -from mypy.plugin import AnalyzeTypeContext, ClassDefContext, Plugin
  +from mypy.nodes import (
  +    GDEF,
  +    Expression,
  +    IndexExpr,
  +    NameExpr,
  +    SymbolTableNode,
  +    TupleExpr,
  +    TypeAlias,
  +)
  +from mypy.plugin import (
  +    AnalyzeTypeContext,
  +    ClassDefContext,
  +    DynamicClassDefContext,
  +    Plugin,
  +    SemanticAnalyzerPluginInterface,
  +)
   from mypy.plugins import dataclasses
  -from mypy.types import Type
  +from mypy.types import Type, UnionType
   
   
   def lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:
  @@ -19,7 +34,48 @@
       return type_
   
   
  +def _get_type_for_expr(expr: Expression, api: SemanticAnalyzerPluginInterface):
  +    if isinstance(expr, NameExpr):
  +        return api.named_type(expr.name)
  +
  +    if isinstance(expr, IndexExpr):
  +        type_ = _get_type_for_expr(expr.base, api)
  +        type_.args = [_get_type_for_expr(expr.index, api)]
  +
  +        return type_
  +
  +    raise ValueError(f"Unsupported expression f{type(expr)}")
  +
  +
  +def union_hook(ctx: DynamicClassDefContext) -> None:
  +    types = ctx.call.args[1]
  +
  +    if isinstance(types, TupleExpr):
  +        type_ = UnionType(tuple(_get_type_for_expr(x, ctx.api) for x in types.items))
  +
  +        type_alias = TypeAlias(
  +            type_,
  +            fullname=ctx.api.qualified_name(ctx.name),
  +            line=ctx.call.line,
  +            column=ctx.call.column,
  +        )
  +
  +        ctx.api.add_symbol_table_node(
  +            ctx.name, SymbolTableNode(GDEF, type_alias, plugin_generated=False)
  +        )
  +
  +
   class StrawberryPlugin(Plugin):
  +    def get_dynamic_class_hook(
  +        self, fullname: str
  +    ) -> Optional[Callable[[DynamicClassDefContext], None]]:
  +        # TODO: investigate why we need this instead of `strawberry.union.union` on CI
  +        # we have the same issue in the other hooks
  +        if "strawberry.union" in fullname:
  +            return union_hook
  +
  +        return None
  +
       def get_type_analyze_hook(self, fullname: str):
           if fullname == "strawberry.lazy_type.LazyType":
               return lazy_type_analyze_callback
  
verification_info: {"golden_diff": "diff --git a/strawberry/ext/mypy_plugin.py b/strawberry/ext/mypy_plugin.py\n--- a/strawberry/ext/mypy_plugin.py\n+++ b/strawberry/ext/mypy_plugin.py\n@@ -1,8 +1,23 @@\n from typing import Callable, Optional\n \n-from mypy.plugin import AnalyzeTypeContext, ClassDefContext, Plugin\n+from mypy.nodes import (\n+    GDEF,\n+    Expression,\n+    IndexExpr,\n+    NameExpr,\n+    SymbolTableNode,\n+    TupleExpr,\n+    TypeAlias,\n+)\n+from mypy.plugin import (\n+    AnalyzeTypeContext,\n+    ClassDefContext,\n+    DynamicClassDefContext,\n+    Plugin,\n+    SemanticAnalyzerPluginInterface,\n+)\n from mypy.plugins import dataclasses\n-from mypy.types import Type\n+from mypy.types import Type, UnionType\n \n \n def lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:\n@@ -19,7 +34,48 @@\n     return type_\n \n \n+def _get_type_for_expr(expr: Expression, api: SemanticAnalyzerPluginInterface):\n+    if isinstance(expr, NameExpr):\n+        return api.named_type(expr.name)\n+\n+    if isinstance(expr, IndexExpr):\n+        type_ = _get_type_for_expr(expr.base, api)\n+        type_.args = [_get_type_for_expr(expr.index, api)]\n+\n+        return type_\n+\n+    raise ValueError(f\"Unsupported expression f{type(expr)}\")\n+\n+\n+def union_hook(ctx: DynamicClassDefContext) -> None:\n+    types = ctx.call.args[1]\n+\n+    if isinstance(types, TupleExpr):\n+        type_ = UnionType(tuple(_get_type_for_expr(x, ctx.api) for x in types.items))\n+\n+        type_alias = TypeAlias(\n+            type_,\n+            fullname=ctx.api.qualified_name(ctx.name),\n+            line=ctx.call.line,\n+            column=ctx.call.column,\n+        )\n+\n+        ctx.api.add_symbol_table_node(\n+            ctx.name, SymbolTableNode(GDEF, type_alias, plugin_generated=False)\n+        )\n+\n+\n class StrawberryPlugin(Plugin):\n+    def get_dynamic_class_hook(\n+        self, fullname: str\n+    ) -> Optional[Callable[[DynamicClassDefContext], None]]:\n+        # TODO: investigate why we need this instead of `strawberry.union.union` on CI\n+        # we have the same issue in the other hooks\n+        if \"strawberry.union\" in fullname:\n+            return union_hook\n+\n+        return None\n+\n     def get_type_analyze_hook(self, fullname: str):\n         if fullname == \"strawberry.lazy_type.LazyType\":\n             return lazy_type_analyze_callback\n", "issue": "Add mypy plugin for strawberry.union\nWe have a function to create union types and also add name and description that are used when generating the GraphQL schema[1].\r\n\r\nUnfortunately MyPy complains when using the annotation, since it doesn't recognise it as a type, here's an example:\r\n\r\n```python\r\nExampleUnion = strawberry.union(\r\n    \"ExampleUnion\",\r\n    types=(\r\n        ABC,\r\n        CDE,\r\n    ),\r\n)\r\n\r\n@strawberry.mutation\r\ndef abc(phone_number: str) -> ExampleUnion:\r\n    # ...\r\n```\r\n\r\nError:\r\n\r\n```\r\nVariable \"api.mutations.abc.ExampleUnion\" is not valid as a type\r\n```\r\n\r\n[1] This is why we can't use typing.Union directly, as we can't pass arguments to it\n", "before_files": [{"content": "from typing import Callable, Optional\n\nfrom mypy.plugin import AnalyzeTypeContext, ClassDefContext, Plugin\nfrom mypy.plugins import dataclasses\nfrom mypy.types import Type\n\n\ndef lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:\n    type_name = ctx.type.args[0]\n    type_ = ctx.api.analyze_type(type_name)\n\n    return type_\n\n\ndef private_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:\n    type_name = ctx.type.args[0]\n    type_ = ctx.api.analyze_type(type_name)\n\n    return type_\n\n\nclass StrawberryPlugin(Plugin):\n    def get_type_analyze_hook(self, fullname: str):\n        if fullname == \"strawberry.lazy_type.LazyType\":\n            return lazy_type_analyze_callback\n\n        if any(\n            name in fullname\n            for name in {\"strawberry.private.Private\", \"strawberry.Private\"}\n        ):\n            return private_type_analyze_callback\n\n        return None\n\n    def get_class_decorator_hook(\n        self, fullname: str\n    ) -> Optional[Callable[[ClassDefContext], None]]:\n        if any(\n            strawberry_decorator in fullname\n            for strawberry_decorator in {\n                \"strawberry.type\",\n                \"strawberry.federation.type\",\n                \"strawberry.input\",\n                \"strawberry.interface\",\n            }\n        ):\n            return dataclasses.dataclass_class_maker_callback\n        return None\n\n\ndef plugin(version: str):\n    return StrawberryPlugin\n", "path": "strawberry/ext/mypy_plugin.py"}], "after_files": [{"content": "from typing import Callable, Optional\n\nfrom mypy.nodes import (\n    GDEF,\n    Expression,\n    IndexExpr,\n    NameExpr,\n    SymbolTableNode,\n    TupleExpr,\n    TypeAlias,\n)\nfrom mypy.plugin import (\n    AnalyzeTypeContext,\n    ClassDefContext,\n    DynamicClassDefContext,\n    Plugin,\n    SemanticAnalyzerPluginInterface,\n)\nfrom mypy.plugins import dataclasses\nfrom mypy.types import Type, UnionType\n\n\ndef lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:\n    type_name = ctx.type.args[0]\n    type_ = ctx.api.analyze_type(type_name)\n\n    return type_\n\n\ndef private_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:\n    type_name = ctx.type.args[0]\n    type_ = ctx.api.analyze_type(type_name)\n\n    return type_\n\n\ndef _get_type_for_expr(expr: Expression, api: SemanticAnalyzerPluginInterface):\n    if isinstance(expr, NameExpr):\n        return api.named_type(expr.name)\n\n    if isinstance(expr, IndexExpr):\n        type_ = _get_type_for_expr(expr.base, api)\n        type_.args = [_get_type_for_expr(expr.index, api)]\n\n        return type_\n\n    raise ValueError(f\"Unsupported expression f{type(expr)}\")\n\n\ndef union_hook(ctx: DynamicClassDefContext) -> None:\n    types = ctx.call.args[1]\n\n    if isinstance(types, TupleExpr):\n        type_ = UnionType(tuple(_get_type_for_expr(x, ctx.api) for x in types.items))\n\n        type_alias = TypeAlias(\n            type_,\n            fullname=ctx.api.qualified_name(ctx.name),\n            line=ctx.call.line,\n            column=ctx.call.column,\n        )\n\n        ctx.api.add_symbol_table_node(\n            ctx.name, SymbolTableNode(GDEF, type_alias, plugin_generated=False)\n        )\n\n\nclass StrawberryPlugin(Plugin):\n    def get_dynamic_class_hook(\n        self, fullname: str\n    ) -> Optional[Callable[[DynamicClassDefContext], None]]:\n        # TODO: investigate why we need this instead of `strawberry.union.union` on CI\n        # we have the same issue in the other hooks\n        if \"strawberry.union\" in fullname:\n            return union_hook\n\n        return None\n\n    def get_type_analyze_hook(self, fullname: str):\n        if fullname == \"strawberry.lazy_type.LazyType\":\n            return lazy_type_analyze_callback\n\n        if any(\n            name in fullname\n            for name in {\"strawberry.private.Private\", \"strawberry.Private\"}\n        ):\n            return private_type_analyze_callback\n\n        return None\n\n    def get_class_decorator_hook(\n        self, fullname: str\n    ) -> Optional[Callable[[ClassDefContext], None]]:\n        if any(\n            strawberry_decorator in fullname\n            for strawberry_decorator in {\n                \"strawberry.type\",\n                \"strawberry.federation.type\",\n                \"strawberry.input\",\n                \"strawberry.interface\",\n            }\n        ):\n            return dataclasses.dataclass_class_maker_callback\n        return None\n\n\ndef plugin(version: str):\n    return StrawberryPlugin\n", "path": "strawberry/ext/mypy_plugin.py"}]}