prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Undefined variable issue in 0.77.6
  With the latest release of #1213, this [line](https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/nodes.py#L40) raises a KeyError when a variable is used in a query, but not defined in the payload under the key `variables`.
  
  This used to work, and, I guess, is ok when the variable/argument is optional.
  
  I do not know what the specs are, meaning if variable definition should be enforced all the way. If it's the way to go, maybe there should be a validation step/graceful way to handle such a case.
  
  If not, `return info.variable_values.get(name)` should do the trick.
  
  --- 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/types/nodes.py`
  Content:
  ```
  1 """
  2 Abstraction layer for graphql-core field nodes.
  3 
  4 Call `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.
  5 
  6 If a node has only one useful value, it's value is inlined.
  7 
  8 If a list of nodes have unique names, it's transformed into a mapping.
  9 Note Python dicts maintain ordering (for all supported versions).
  10 """
  11 
  12 import dataclasses
  13 from typing import Any, Dict, Iterable, List, Optional, Union
  14 
  15 from graphql import GraphQLResolveInfo
  16 from graphql.language import (
  17     ArgumentNode as GQLArgumentNode,
  18     DirectiveNode as GQLDirectiveNode,
  19     FieldNode as GQLFieldNode,
  20     FragmentSpreadNode as GQLFragmentSpreadNode,
  21     InlineFragmentNode as GQLInlineFragment,
  22     InlineFragmentNode as GQLInlineFragmentNode,
  23     ListValueNode as GQLListValueNode,
  24     ObjectValueNode as GQLObjectValueNode,
  25     ValueNode as GQLValueNode,
  26     VariableNode as GQLVariableNode,
  27 )
  28 
  29 
  30 Arguments = Dict[str, Any]
  31 Directives = Dict[str, Arguments]
  32 Selection = Union["SelectedField", "FragmentSpread", "InlineFragment"]
  33 
  34 
  35 def convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:
  36     """Return useful value from any node."""
  37     if isinstance(node, GQLVariableNode):
  38         # Look up variable
  39         name = node.name.value
  40         return info.variable_values[name]
  41     if isinstance(node, GQLListValueNode):
  42         return [convert_value(info, value) for value in node.values]
  43     if isinstance(node, GQLObjectValueNode):
  44         return {
  45             field.name.value: convert_value(info, field.value) for field in node.fields
  46         }
  47     return getattr(node, "value", None)
  48 
  49 
  50 def convert_arguments(
  51     info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]
  52 ) -> Arguments:
  53     """Return mapping of arguments."""
  54     return {node.name.value: convert_value(info, node.value) for node in nodes}
  55 
  56 
  57 def convert_directives(
  58     info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]
  59 ) -> Directives:
  60     """Return mapping of directives."""
  61     return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}
  62 
  63 
  64 def convert_selections(
  65     info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]
  66 ) -> List[Selection]:
  67     """Return typed `Selection` based on node type."""
  68     selections: List[Selection] = []
  69     for node in field_nodes:
  70         if isinstance(node, GQLFieldNode):
  71             selections.append(SelectedField.from_node(info, node))
  72         elif isinstance(node, GQLInlineFragment):
  73             selections.append(InlineFragment.from_node(info, node))
  74         elif isinstance(node, GQLFragmentSpreadNode):
  75             selections.append(FragmentSpread.from_node(info, node))
  76         else:
  77             raise TypeError(f"Unknown node type: {node}")
  78 
  79     return selections
  80 
  81 
  82 @dataclasses.dataclass
  83 class FragmentSpread:
  84     """Wrapper for a FragmentSpreadNode."""
  85 
  86     name: str
  87     type_condition: str
  88     directives: Directives
  89     selections: List[Selection]
  90 
  91     @classmethod
  92     def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):
  93         # Look up fragment
  94         name = node.name.value
  95         fragment = info.fragments[name]
  96         return cls(
  97             name=name,
  98             directives=convert_directives(info, node.directives),
  99             type_condition=fragment.type_condition.name.value,
  100             selections=convert_selections(
  101                 info, getattr(fragment.selection_set, "selections", [])
  102             ),
  103         )
  104 
  105 
  106 @dataclasses.dataclass
  107 class InlineFragment:
  108     """Wrapper for a InlineFragmentNode."""
  109 
  110     type_condition: str
  111     selections: List[Selection]
  112     directives: Directives
  113 
  114     @classmethod
  115     def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):
  116         return cls(
  117             type_condition=node.type_condition.name.value,
  118             selections=convert_selections(
  119                 info, getattr(node.selection_set, "selections", [])
  120             ),
  121             directives=convert_directives(info, node.directives),
  122         )
  123 
  124 
  125 @dataclasses.dataclass
  126 class SelectedField:
  127     """Wrapper for a FieldNode."""
  128 
  129     name: str
  130     directives: Directives
  131     arguments: Arguments
  132     selections: List[Selection]
  133     alias: Optional[str] = None
  134 
  135     @classmethod
  136     def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):
  137         return cls(
  138             name=node.name.value,
  139             directives=convert_directives(info, node.directives),
  140             alias=getattr(node.alias, "value", None),
  141             arguments=convert_arguments(info, node.arguments),
  142             selections=convert_selections(
  143                 info, getattr(node.selection_set, "selections", [])
  144             ),
  145         )
  146
  ```
  --- 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/types/nodes.py b/strawberry/types/nodes.py
  --- a/strawberry/types/nodes.py
  +++ b/strawberry/types/nodes.py
  @@ -37,7 +37,7 @@
       if isinstance(node, GQLVariableNode):
           # Look up variable
           name = node.name.value
  -        return info.variable_values[name]
  +        return info.variable_values.get(name)
       if isinstance(node, GQLListValueNode):
           return [convert_value(info, value) for value in node.values]
       if isinstance(node, GQLObjectValueNode):
  
verification_info: {"golden_diff": "diff --git a/strawberry/types/nodes.py b/strawberry/types/nodes.py\n--- a/strawberry/types/nodes.py\n+++ b/strawberry/types/nodes.py\n@@ -37,7 +37,7 @@\n     if isinstance(node, GQLVariableNode):\n         # Look up variable\n         name = node.name.value\n-        return info.variable_values[name]\n+        return info.variable_values.get(name)\n     if isinstance(node, GQLListValueNode):\n         return [convert_value(info, value) for value in node.values]\n     if isinstance(node, GQLObjectValueNode):\n", "issue": "Undefined variable issue in 0.77.6\nWith the latest release of #1213, this [line](https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/nodes.py#L40) raises a KeyError when a variable is used in a query, but not defined in the payload under the key `variables`.\r\n\r\nThis used to work, and, I guess, is ok when the variable/argument is optional.\r\n\r\nI do not know what the specs are, meaning if variable definition should be enforced all the way. If it's the way to go, maybe there should be a validation step/graceful way to handle such a case.\r\n\r\nIf not, `return info.variable_values.get(name)` should do the trick.\n", "before_files": [{"content": "\"\"\"\nAbstraction layer for graphql-core field nodes.\n\nCall `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.\n\nIf a node has only one useful value, it's value is inlined.\n\nIf a list of nodes have unique names, it's transformed into a mapping.\nNote Python dicts maintain ordering (for all supported versions).\n\"\"\"\n\nimport dataclasses\nfrom typing import Any, Dict, Iterable, List, Optional, Union\n\nfrom graphql import GraphQLResolveInfo\nfrom graphql.language import (\n    ArgumentNode as GQLArgumentNode,\n    DirectiveNode as GQLDirectiveNode,\n    FieldNode as GQLFieldNode,\n    FragmentSpreadNode as GQLFragmentSpreadNode,\n    InlineFragmentNode as GQLInlineFragment,\n    InlineFragmentNode as GQLInlineFragmentNode,\n    ListValueNode as GQLListValueNode,\n    ObjectValueNode as GQLObjectValueNode,\n    ValueNode as GQLValueNode,\n    VariableNode as GQLVariableNode,\n)\n\n\nArguments = Dict[str, Any]\nDirectives = Dict[str, Arguments]\nSelection = Union[\"SelectedField\", \"FragmentSpread\", \"InlineFragment\"]\n\n\ndef convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:\n    \"\"\"Return useful value from any node.\"\"\"\n    if isinstance(node, GQLVariableNode):\n        # Look up variable\n        name = node.name.value\n        return info.variable_values[name]\n    if isinstance(node, GQLListValueNode):\n        return [convert_value(info, value) for value in node.values]\n    if isinstance(node, GQLObjectValueNode):\n        return {\n            field.name.value: convert_value(info, field.value) for field in node.fields\n        }\n    return getattr(node, \"value\", None)\n\n\ndef convert_arguments(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]\n) -> Arguments:\n    \"\"\"Return mapping of arguments.\"\"\"\n    return {node.name.value: convert_value(info, node.value) for node in nodes}\n\n\ndef convert_directives(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]\n) -> Directives:\n    \"\"\"Return mapping of directives.\"\"\"\n    return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}\n\n\ndef convert_selections(\n    info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]\n) -> List[Selection]:\n    \"\"\"Return typed `Selection` based on node type.\"\"\"\n    selections: List[Selection] = []\n    for node in field_nodes:\n        if isinstance(node, GQLFieldNode):\n            selections.append(SelectedField.from_node(info, node))\n        elif isinstance(node, GQLInlineFragment):\n            selections.append(InlineFragment.from_node(info, node))\n        elif isinstance(node, GQLFragmentSpreadNode):\n            selections.append(FragmentSpread.from_node(info, node))\n        else:\n            raise TypeError(f\"Unknown node type: {node}\")\n\n    return selections\n\n\n@dataclasses.dataclass\nclass FragmentSpread:\n    \"\"\"Wrapper for a FragmentSpreadNode.\"\"\"\n\n    name: str\n    type_condition: str\n    directives: Directives\n    selections: List[Selection]\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):\n        # Look up fragment\n        name = node.name.value\n        fragment = info.fragments[name]\n        return cls(\n            name=name,\n            directives=convert_directives(info, node.directives),\n            type_condition=fragment.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(fragment.selection_set, \"selections\", [])\n            ),\n        )\n\n\n@dataclasses.dataclass\nclass InlineFragment:\n    \"\"\"Wrapper for a InlineFragmentNode.\"\"\"\n\n    type_condition: str\n    selections: List[Selection]\n    directives: Directives\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):\n        return cls(\n            type_condition=node.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n            directives=convert_directives(info, node.directives),\n        )\n\n\n@dataclasses.dataclass\nclass SelectedField:\n    \"\"\"Wrapper for a FieldNode.\"\"\"\n\n    name: str\n    directives: Directives\n    arguments: Arguments\n    selections: List[Selection]\n    alias: Optional[str] = None\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):\n        return cls(\n            name=node.name.value,\n            directives=convert_directives(info, node.directives),\n            alias=getattr(node.alias, \"value\", None),\n            arguments=convert_arguments(info, node.arguments),\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n        )\n", "path": "strawberry/types/nodes.py"}], "after_files": [{"content": "\"\"\"\nAbstraction layer for graphql-core field nodes.\n\nCall `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.\n\nIf a node has only one useful value, it's value is inlined.\n\nIf a list of nodes have unique names, it's transformed into a mapping.\nNote Python dicts maintain ordering (for all supported versions).\n\"\"\"\n\nimport dataclasses\nfrom typing import Any, Dict, Iterable, List, Optional, Union\n\nfrom graphql import GraphQLResolveInfo\nfrom graphql.language import (\n    ArgumentNode as GQLArgumentNode,\n    DirectiveNode as GQLDirectiveNode,\n    FieldNode as GQLFieldNode,\n    FragmentSpreadNode as GQLFragmentSpreadNode,\n    InlineFragmentNode as GQLInlineFragment,\n    InlineFragmentNode as GQLInlineFragmentNode,\n    ListValueNode as GQLListValueNode,\n    ObjectValueNode as GQLObjectValueNode,\n    ValueNode as GQLValueNode,\n    VariableNode as GQLVariableNode,\n)\n\n\nArguments = Dict[str, Any]\nDirectives = Dict[str, Arguments]\nSelection = Union[\"SelectedField\", \"FragmentSpread\", \"InlineFragment\"]\n\n\ndef convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:\n    \"\"\"Return useful value from any node.\"\"\"\n    if isinstance(node, GQLVariableNode):\n        # Look up variable\n        name = node.name.value\n        return info.variable_values.get(name)\n    if isinstance(node, GQLListValueNode):\n        return [convert_value(info, value) for value in node.values]\n    if isinstance(node, GQLObjectValueNode):\n        return {\n            field.name.value: convert_value(info, field.value) for field in node.fields\n        }\n    return getattr(node, \"value\", None)\n\n\ndef convert_arguments(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]\n) -> Arguments:\n    \"\"\"Return mapping of arguments.\"\"\"\n    return {node.name.value: convert_value(info, node.value) for node in nodes}\n\n\ndef convert_directives(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]\n) -> Directives:\n    \"\"\"Return mapping of directives.\"\"\"\n    return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}\n\n\ndef convert_selections(\n    info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]\n) -> List[Selection]:\n    \"\"\"Return typed `Selection` based on node type.\"\"\"\n    selections: List[Selection] = []\n    for node in field_nodes:\n        if isinstance(node, GQLFieldNode):\n            selections.append(SelectedField.from_node(info, node))\n        elif isinstance(node, GQLInlineFragment):\n            selections.append(InlineFragment.from_node(info, node))\n        elif isinstance(node, GQLFragmentSpreadNode):\n            selections.append(FragmentSpread.from_node(info, node))\n        else:\n            raise TypeError(f\"Unknown node type: {node}\")\n\n    return selections\n\n\n@dataclasses.dataclass\nclass FragmentSpread:\n    \"\"\"Wrapper for a FragmentSpreadNode.\"\"\"\n\n    name: str\n    type_condition: str\n    directives: Directives\n    selections: List[Selection]\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):\n        # Look up fragment\n        name = node.name.value\n        fragment = info.fragments[name]\n        return cls(\n            name=name,\n            directives=convert_directives(info, node.directives),\n            type_condition=fragment.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(fragment.selection_set, \"selections\", [])\n            ),\n        )\n\n\n@dataclasses.dataclass\nclass InlineFragment:\n    \"\"\"Wrapper for a InlineFragmentNode.\"\"\"\n\n    type_condition: str\n    selections: List[Selection]\n    directives: Directives\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):\n        return cls(\n            type_condition=node.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n            directives=convert_directives(info, node.directives),\n        )\n\n\n@dataclasses.dataclass\nclass SelectedField:\n    \"\"\"Wrapper for a FieldNode.\"\"\"\n\n    name: str\n    directives: Directives\n    arguments: Arguments\n    selections: List[Selection]\n    alias: Optional[str] = None\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):\n        return cls(\n            name=node.name.value,\n            directives=convert_directives(info, node.directives),\n            alias=getattr(node.alias, \"value\", None),\n            arguments=convert_arguments(info, node.arguments),\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n        )\n", "path": "strawberry/types/nodes.py"}]}