text: """
  Abstraction layer for graphql-core field nodes.
  
  Call `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.
  
  If a node has only one useful value, it's value is inlined.
  
  If a list of nodes have unique names, it's transformed into a mapping.
  Note Python dicts maintain ordering (for all supported versions).
  """
  
  import dataclasses
  from typing import Any, Dict, Iterable, List, Optional, Union
  
  from graphql import GraphQLResolveInfo
  from graphql.language import (
      ArgumentNode as GQLArgumentNode,
      DirectiveNode as GQLDirectiveNode,
      FieldNode as GQLFieldNode,
      FragmentSpreadNode as GQLFragmentSpreadNode,
      InlineFragmentNode as GQLInlineFragment,
      InlineFragmentNode as GQLInlineFragmentNode,
      ListValueNode as GQLListValueNode,
      ObjectValueNode as GQLObjectValueNode,
      ValueNode as GQLValueNode,
      VariableNode as GQLVariableNode,
  )
  
  
  Arguments = Dict[str, Any]
  Directives = Dict[str, Arguments]
  Selection = Union["SelectedField", "FragmentSpread", "InlineFragment"]
  
  
  def convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:
      """Return useful value from any node."""
      if isinstance(node, GQLVariableNode):
          # Look up variable
          name = node.name.value
          return info.variable_values.get(name)
      if isinstance(node, GQLListValueNode):
          return [convert_value(info, value) for value in node.values]
      if isinstance(node, GQLObjectValueNode):
          return {
              field.name.value: convert_value(info, field.value) for field in node.fields
          }
      return getattr(node, "value", None)
  
  
  def convert_arguments(
      info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]
  ) -> Arguments:
      """Return mapping of arguments."""
      return {node.name.value: convert_value(info, node.value) for node in nodes}
  
  
  def convert_directives(
      info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]
  ) -> Directives:
      """Return mapping of directives."""
      return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}
  
  
  def convert_selections(
      info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]
  ) -> List[Selection]:
      """Return typed `Selection` based on node type."""
      selections: List[Selection] = []
      for node in field_nodes:
          if isinstance(node, GQLFieldNode):
              selections.append(SelectedField.from_node(info, node))
          elif isinstance(node, GQLInlineFragment):
              selections.append(InlineFragment.from_node(info, node))
          elif isinstance(node, GQLFragmentSpreadNode):
              selections.append(FragmentSpread.from_node(info, node))
          else:
              raise TypeError(f"Unknown node type: {node}")
  
      return selections
  
  
  @dataclasses.dataclass
  class FragmentSpread:
      """Wrapper for a FragmentSpreadNode."""
  
      name: str
      type_condition: str
      directives: Directives
      selections: List[Selection]
  
      @classmethod
      def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):
          # Look up fragment
          name = node.name.value
          fragment = info.fragments[name]
          return cls(
              name=name,
              directives=convert_directives(info, node.directives),
              type_condition=fragment.type_condition.name.value,
              selections=convert_selections(
                  info, getattr(fragment.selection_set, "selections", [])
              ),
          )
  
  
  @dataclasses.dataclass
  class InlineFragment:
      """Wrapper for a InlineFragmentNode."""
  
      type_condition: str
      selections: List[Selection]
      directives: Directives
  
      @classmethod
      def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):
          return cls(
              type_condition=node.type_condition.name.value,
              selections=convert_selections(
                  info, getattr(node.selection_set, "selections", [])
              ),
              directives=convert_directives(info, node.directives),
          )
  
  
  @dataclasses.dataclass
  class SelectedField:
      """Wrapper for a FieldNode."""
  
      name: str
      directives: Directives
      arguments: Arguments
      selections: List[Selection]
      alias: Optional[str] = None
  
      @classmethod
      def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):
          return cls(
              name=node.name.value,
              directives=convert_directives(info, node.directives),
              alias=getattr(node.alias, "value", None),
              arguments=convert_arguments(info, node.arguments),
              selections=convert_selections(
                  info, getattr(node.selection_set, "selections", [])
              ),
          )
  