prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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
  
  </issue>
  <code>
  [start of strawberry/ext/mypy_plugin.py]
  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 of strawberry/ext/mypy_plugin.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
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"}]}