Пример #1
0
async def introspection_directives_executor(
    element: Any,
    ctx: Optional[Any],
    info: "ResolveInfo",
    context_coercer: Optional[Any] = None,
) -> Any:
    """
    Applies introspection directives on the element or a list of elements.
    :param element: element to treat
    :param ctx: context passed to the query execution
    :param info: information related to the execution and the resolved field
    :param context_coercer: context passed to the query execution to use on
    argument coercion process
    :type element: Any
    :type ctx: Optional[Any]
    :type info: ResolveInfo
    :type context_coercer: Optional[Any]
    :return: the coerced element
    :rtype: Any
    """
    if not isinstance(element, list):
        computed_element = await execute_introspection_directive(
            element, ctx, info, context_coercer=context_coercer)
        if not is_invalid_value(computed_element):
            return computed_element
        return None

    results = await asyncio.gather(*[
        execute_introspection_directive(
            item, ctx, info, context_coercer=context_coercer)
        for item in element
    ])
    return [result for result in results if not is_invalid_value(result)]
Пример #2
0
async def scalar_coercer(
    result: Any,
    info: "ResolveInfo",
    execution_context: "ExecutionContext",
    field_nodes: List["FieldNode"],
    path: "Path",
    scalar_type: "GraphQLScalar",
) -> Any:
    """
    Computes the value of a scalar type.
    :param result: resolved value
    :param info: information related to the execution and the resolved field
    :param execution_context: instance of the query execution context
    :param field_nodes: AST nodes related to the resolved field
    :param path: the path traveled until this resolver
    :param scalar_type: the GraphQLType instance of the scalar
    :type result: Any
    :type info: ResolveInfo
    :type execution_context: ExecutionContext
    :type field_nodes: List[FieldNode]
    :type path: Path
    :type scalar_type: GraphQLScalar
    :return: the computed value
    :rtype: Any
    """
    # pylint: disable=unused-argument
    coerced_result = scalar_type.coerce_output(result)
    if is_invalid_value(coerced_result):
        raise ValueError(
            f"Expected value of type {scalar_type} but received {type(result)}."
        )
    return coerced_result
Пример #3
0
async def scalar_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: Union["ValueNode", "VariableNode"],
    ctx: Optional[Any],
    scalar_type: "GraphQLScalarType",
    variables: Optional[Dict[str, Any]] = None,
    path: Optional["Path"] = None,
) -> "CoercionResult":
    """
    Computes the value of a scalar.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param ctx: context passed to the query execution
    :param scalar_type: the GraphQLScalarType instance of the scalar
    :param variables: the variables provided in the GraphQL request
    :param path: the path traveled until this coercer
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Union[ValueNode, VariableNode]
    :type ctx: Optional[Any]
    :type scalar_type: GraphQLScalarType
    :type variables: Optional[Dict[str, Any]]
    :type path: Optional[Path]
    :return: the computed value
    :rtype: CoercionResult
    """
    # pylint: disable=unused-argument
    try:
        value = scalar_type.parse_literal(node)
        if not is_invalid_value(value):
            return CoercionResult(value=value)
    except Exception:  # pylint: disable=broad-except
        pass
    return CoercionResult(value=UNDEFINED_VALUE)
Пример #4
0
async def execute_fields_serially(
    execution_context: "ExecutionContext",
    parent_type: "GraphQLObjectType",
    source_value: Any,
    path: Optional["Path"],
    fields: Dict[str, List["FieldNode"]],
) -> Dict[str, Any]:
    """
    Implements the "Evaluating selection sets" section of the spec for "write"
    mode.
    :param execution_context: instance of the query execution context
    :param parent_type: GraphQLObjectType of the field's parent
    :param source_value: default root value or field parent value
    :param path: the path traveled until this resolver
    :param fields: dictionary of collected fields
    :type execution_context: ExecutionContext
    :type parent_type: GraphQLObjectType
    :type source_value: Any
    :type path: Optional[Path]
    :type fields: Dict[str, List[FieldNode]]
    :return: the computed fields value
    :rtype: Dict[str, Any]
    """
    results = {}
    for entry_key, field_nodes in fields.items():
        result = await resolve_field(
            execution_context,
            parent_type,
            source_value,
            field_nodes,
            Path(path, entry_key),
        )
        if not is_invalid_value(result):
            results[entry_key] = result
    return results
Пример #5
0
async def execute_fields(
    execution_context: "ExecutionContext",
    parent_type: "GraphQLObjectType",
    source_value: Any,
    path: Optional["Path"],
    fields: Dict[str, List["FieldNode"]],
    is_introspection_context: bool = False,
) -> Dict[str, Any]:
    """
    Implements the "Evaluating selection sets" section of the spec for "read"
    mode.
    :param execution_context: instance of the query execution context
    :param parent_type: GraphQLObjectType of the field's parent
    :param source_value: default root value or field parent value
    :param path: the path traveled until this resolver
    :param fields: dictionary of collected fields
    :param is_introspection_context: determines whether or not the resolved
    field is in a context of an introspection query
    :type execution_context: ExecutionContext
    :type parent_type: GraphQLObjectType
    :type source_value: Any
    :type path: Optional[Path]
    :type fields: Dict[str, List[FieldNode]]
    :type is_introspection_context: bool
    :return: the computed fields value
    :rtype: Dict[str, Any]
    """
    results = await asyncio.gather(
        *[
            resolve_field(
                execution_context,
                parent_type,
                source_value,
                field_nodes,
                Path(path, entry_key),
                is_introspection_context,
            ) for entry_key, field_nodes in fields.items()
        ],
        return_exceptions=True,
    )

    exceptions = extract_exceptions_from_results(results)
    if exceptions:
        raise exceptions

    return {
        entry_key: result
        for entry_key, result in zip(fields, results)
        if not is_invalid_value(result)
    }
Пример #6
0
async def input_field_value_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: "Node",
    value: Any,
    ctx: Optional[Any],
    input_field: "GraphQLInputField",
    path: "Path",
) -> Union["CoercionResult", "UNDEFINED_VALUE"]:
    """
    Computes the value of an input field object.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param value: the raw value to compute
    :param ctx: context passed to the query execution
    :param input_field: the input field to compute
    :param path: the path traveled until this coercer
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Node
    :type value: Any
    :type ctx: Optional[Any]
    :type input_field: GraphQLInputField
    :type path: Path
    :return: the coercion result
    :rtype: Union[CoercionResult, UNDEFINED_VALUE]
    """
    if is_invalid_value(value):
        if input_field.default_value is not None:
            return await input_field.literal_coercer(parent_node,
                                                     input_field.default_value,
                                                     ctx)
        if input_field.graphql_type.is_non_null_type:
            return CoercionResult(errors=[
                coercion_error(
                    f"Field < {path} > of required type "
                    f"< {input_field.gql_type} > was not provided",
                    node,
                )
            ])
        return UNDEFINED_VALUE

    return await input_field.input_coercer(parent_node,
                                           node,
                                           value,
                                           ctx,
                                           path=path)
    async def wrapper(
        parent_node: Union["VariableDefinitionNode",
                           "InputValueDefinitionNode"],
        node: "Node",
        ctx: Optional[Any],
        variables: Optional[Dict[str, Any]] = None,
        is_non_null_type: bool = False,
        **kwargs,
    ) -> "CoercionResult":
        """
        Computes the value if null or variable.
        :param parent_node: the root parent AST node
        :param node: the AST node to treat
        :param ctx: context passed to the query execution
        :param variables: the variables provided in the GraphQL request
        :param is_non_null_type: determines whether or not the value is
        nullable
        :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
        :type node: Union[ValueNode, VariableNode]
        :type ctx: Optional[Any]
        :type variables: Optional[Dict[str, Any]]
        :type is_non_null_type: bool
        :return: the computed value
        :rtype: CoercionResult
        """
        if not node:
            return CoercionResult(value=UNDEFINED_VALUE)

        if isinstance(node, NullValueNode):
            return CoercionResult(value=None)

        if isinstance(node, VariableNode):
            if not variables:
                return CoercionResult(value=UNDEFINED_VALUE)

            value = variables.get(node.name.value, UNDEFINED_VALUE)
            if is_invalid_value(value) or (value is None and is_non_null_type):
                return CoercionResult(value=UNDEFINED_VALUE)
            return CoercionResult(value=value)

        return await coercer(parent_node,
                             node,
                             ctx,
                             variables=variables,
                             **kwargs)
Пример #8
0
async def scalar_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: "Node",
    value: Any,
    ctx: Optional[Any],
    scalar_type: "GraphQLScalarType",
    path: Optional["Path"] = None,
) -> "CoercionResult":
    """
    Computes the value of a scalar.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param value: the raw value to compute
    :param ctx: context passed to the query execution
    :param scalar_type: the GraphQLScalarType instance of the scalar
    :param path: the path traveled until this coercer
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Node
    :type value: Any
    :type ctx: Optional[Any]
    :type scalar_type: GraphQLScalarType
    :type path: Optional[Path]
    :return: the coercion result
    :rtype: CoercionResult
    """
    # pylint: disable=unused-argument
    try:
        coerced_value = scalar_type.coerce_input(value)
        if is_invalid_value(coerced_value):
            return CoercionResult(errors=[
                coercion_error(f"Expected type < {scalar_type.name} >", node,
                               path)
            ])
    except Exception as e:  # pylint: disable=broad-except
        return CoercionResult(errors=[
            coercion_error(
                f"Expected type < {scalar_type.name} >",
                node,
                path,
                sub_message=str(e),
                original_error=e,
            )
        ])
    return CoercionResult(value=coerced_value)
Пример #9
0
def is_missing_variable(
    value_node: Union["ValueNode", "VariableNode"], variables: Dict[str, Any]
) -> bool:
    """
    Determines whether or not the value node is a VariableNode without defined
    value.
    :param value_node: the AST node to treat
    :param variables: the variables provided in the GraphQL request
    :type value_node: Union[ValueNode, VariableNode]
    :type variables: Dict[str, Any]
    :return: whether or not the value node is a VariableNode without defined
    value
    :rtype: bool
    """
    return isinstance(value_node, VariableNode) and (
        not variables
        or value_node.name.value not in variables
        or is_invalid_value(variables[value_node.name.value])
    )
Пример #10
0
async def enum_coercer(
    result: Any,
    info: "ResolveInfo",
    execution_context: "ExecutionContext",
    field_nodes: List["FieldNode"],
    path: "Path",
    enum_type: "GraphQLEnumType",
) -> Any:
    """
    Computes the value of an enum type.
    :param result: resolved value
    :param info: information related to the execution and the resolved field
    :param execution_context: instance of the query execution context
    :param field_nodes: AST nodes related to the resolved field
    :param path: the path traveled until this resolver
    :param enum_type: the GraphQLType instance of the enum
    :type result: Any
    :type info: ResolveInfo
    :type execution_context: ExecutionContext
    :type field_nodes: List[FieldNode]
    :type path: Path
    :type enum_type: GraphQLEnumType
    :return: the computed value
    :rtype: Any
    """
    # pylint: disable=unused-argument
    try:
        enum_value = enum_type.get_value(result)
        coerced_result = await enum_value.output_coercer(
            result,
            execution_context.context,
            info,
            context_coercer=execution_context.context,
        )
    except KeyError:
        coerced_result = UNDEFINED_VALUE

    if is_invalid_value(coerced_result):
        raise ValueError(
            f"Expected value of type {enum_type} but received {type(result)}.")
    return coerced_result
async def input_field_value_coercer(
    input_field: "GraphQLInputField",
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    value_node: Union["ValueNode", "VariableNode", "UNDEFINED_VALUE"],
    ctx: Optional[Any],
    variables: Optional[Dict[str, Any]],
    path: Optional["Path"],
) -> Union["CoercionResult", "UNDEFINED_VALUE", "SKIP_FIELD"]:
    """
    Computes the value of an input field.
    :param input_field: the input field to compute
    :param parent_node: the root parent AST node
    :param value_node: the value node to compute
    :param ctx: context passed to the query execution
    :param variables: the variables provided in the GraphQL request
    :param path: the path traveled until this coercer
    :type input_field: GraphQLInputField
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type value_node: Union[ValueNode, VariableNode, UNDEFINED_VALUE]
    :type ctx: Optional[Any]
    :type variables: Optional[Dict[str, Any]]
    :type path: Optional[Path]
    :return: the computed value
    :rtype: Union[CoercionResult, UNDEFINED_VALUE, SKIP_FIELD]
    """
    if is_invalid_value(value_node) or is_missing_variable(
        value_node.value, variables
    ):
        if input_field.default_value is not None:
            input_field_node = input_field.default_value
        elif input_field.graphql_type.is_non_null_type:
            return UNDEFINED_VALUE
        else:
            return SKIP_FIELD
    else:
        input_field_node = value_node.value

    return await input_field.literal_coercer(
        parent_node, input_field_node, ctx, variables=variables, path=path
    )
async def input_object_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: Union["ValueNode", "VariableNode"],
    ctx: Optional[Any],
    input_object_type: "GraphQLInputObjectType",
    variables: Optional[Dict[str, Any]] = None,
    path: Optional["Path"] = None,
) -> "CoercionResult":
    """
    Computes the value of an input object.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param ctx: context passed to the query execution
    :param input_object_type: the GraphQLInputObjectType instance of the input
    object
    :param variables: the variables provided in the GraphQL request
    :param path: the path traveled until this coercer
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Union[ValueNode, VariableNode]
    :type ctx: Optional[Any]
    :type input_object_type: GraphQLInputObjectType
    :type variables: Optional[Dict[str, Any]]
    :type path: Optional[Path]
    :return: the computed value
    :rtype: CoercionResult
    """
    # pylint: disable=too-many-locals
    if not isinstance(node, ObjectValueNode):
        return CoercionResult(value=UNDEFINED_VALUE)

    field_nodes = {
        field_node.name.value: field_node for field_node in node.fields
    }

    input_fields = input_object_type.input_fields

    results = await asyncio.gather(
        *[
            input_field_value_coercer(
                input_field,
                parent_node,
                field_nodes.get(input_field_name, UNDEFINED_VALUE),
                ctx,
                variables,
                path=Path(path, input_field_name),
            )
            for input_field_name, input_field in input_fields.items()
        ]
    )

    errors = []
    coerced_values = {}
    for input_field_name, input_field_result in zip(input_fields, results):
        if input_field_result is SKIP_FIELD:
            continue

        if is_invalid_value(input_field_result):
            return CoercionResult(value=UNDEFINED_VALUE)

        input_field_value, input_field_errors = input_field_result
        if is_invalid_value(input_field_value):
            return CoercionResult(value=UNDEFINED_VALUE)
        if input_field_errors:
            errors.extend(input_field_errors)
        elif not errors:
            coerced_values[input_field_name] = input_field_value

    return CoercionResult(value=coerced_values, errors=errors)
Пример #13
0
async def coerce_arguments(
    argument_definitions: Dict[str, "GraphQLArgument"],
    node: Union["FieldNode", "DirectiveNode"],
    variable_values: Dict[str, Any],
    ctx: Optional[Any],
) -> Dict[str, Any]:
    """
    Returns the computed values of the arguments.
    :param argument_definitions: the argument definitions to treat
    :param node: the parent AST node of the arguments
    :param variable_values: the variables provided in the GraphQL request
    :param ctx: context passed to the query execution
    :type argument_definitions: Dict[str, GraphQLArgument]
    :type node: Union[FieldNode, DirectiveNode]
    :type variable_values: Dict[str, Any]
    :type ctx: Optional[Any]
    :return: the computed values of the arguments
    :rtype: Dict[str, Any]
    """
    # pylint: disable=too-many-locals
    argument_nodes = node.arguments
    if not argument_definitions or argument_nodes is None:
        return {}

    argument_nodes_map = {
        argument_node.name.value: argument_node
        for argument_node in argument_nodes
    }

    results = await asyncio.gather(
        *[
            argument_definition.coercer(
                argument_definition,
                node,
                argument_nodes_map.get(argument_definition.name),
                variable_values,
                ctx,
            ) for argument_definition in argument_definitions.values()
        ],
        return_exceptions=True,
    )

    coercion_errors: List["TartifletteError"] = []
    coerced_values: Dict[str, Any] = {}

    for argument_name, result in zip(argument_definitions, results):
        if isinstance(result, Exception):
            coercion_errors.extend(
                located_error(
                    result,
                    nodes=argument_nodes_map.get(argument_name)).exceptions)
            continue

        if is_invalid_value(result):
            continue

        if not isinstance(result, CoercionResult):
            coerced_values[argument_name] = result
            continue

        value, errors = result
        if errors:
            coercion_errors.extend(errors)
        elif not is_invalid_value(result):
            coerced_values[argument_name] = value

    if coercion_errors:
        raise MultipleException(coercion_errors)

    return coerced_values
async def literal_directives_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: Union["ValueNode", "VariableNode"],
    ctx: Optional[Any],
    coercer: Callable,
    directives: Optional[Callable],
    variables: Optional[Dict[str, Any]] = None,
    path: Optional["Path"] = None,
    is_input_field: bool = False,
    is_non_null_type: bool = False,
) -> Any:
    """
    Executes the directives on the coerced value.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param ctx: context passed to the query execution
    :param coercer: pre-computed coercer to use on the value
    :param directives: the directives to execute
    :param variables: the variables provided in the GraphQL request
    :param path: the path traveled until this coercer
    :param is_input_field: determines whether or not the node is an InputField
    :param is_non_null_type: determines whether or not the value is nullable
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Union[ValueNode, VariableNode]
    :type ctx: Optional[Any]
    :type coercer: Callable
    :type directives: Optional[Callable]
    :type variables: Optional[Dict[str, Any]]
    :type path: Optional[Path]
    :type is_input_field: bool
    :type is_non_null_type: bool
    :return: the computed value
    :rtype: Any
    """
    # pylint: disable=too-many-locals,too-many-arguments
    coercion_result = await coercer(
        parent_node,
        node,
        ctx,
        variables=variables,
        path=path,
        is_non_null_type=is_non_null_type,
    )

    if not directives or (isinstance(node, VariableNode)
                          and not is_input_field):
        return coercion_result

    value, errors = coercion_result
    if is_invalid_value(value) or errors:
        return coercion_result

    try:
        return CoercionResult(value=await directives(
            parent_node, value, ctx, context_coercer=ctx))
    except Exception as raw_exception:  # pylint: disable=broad-except
        return CoercionResult(errors=[
            graphql_error_from_nodes(
                str(raw_exception),
                node,
                original_error=(raw_exception if not is_coercible_exception(
                    raw_exception) else None),
            ) for raw_exception in (raw_exception.exceptions if isinstance(
                raw_exception, MultipleException) else [raw_exception])
        ])
Пример #15
0
async def list_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: Union["ValueNode", "VariableNode"],
    ctx: Optional[Any],
    is_non_null_item_type: bool,
    inner_coercer: Callable,
    variables: Optional[Dict[str, Any]] = None,
    path: Optional["Path"] = None,
) -> "CoercionResult":
    """
    Computes the value of a list.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param ctx: context passed to the query execution
    :param is_non_null_item_type: determines whether or not the inner value is
    nullable
    :param inner_coercer: the pre-computed coercer to use on each value in the
    list
    :param variables: the variables provided in the GraphQL request
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Union[ValueNode, VariableNode]
    :type ctx: Optional[Any]
    :type is_non_null_item_type: bool
    :type inner_coercer: Callable
    :type variables: Optional[Dict[str, Any]]
    :return: the computed value
    :rtype: CoercionResult
    """
    # pylint: disable=too-many-locals
    if isinstance(node, ListValueNode):
        results = await asyncio.gather(
            *[
                list_item_coercer(
                    parent_node,
                    item_node,
                    ctx,
                    is_non_null_item_type,
                    inner_coercer,
                    variables,
                    path=Path(path, index),
                )
                for index, item_node in enumerate(node.values)
            ]
        )

        errors = []
        coerced_values = []
        for coerced_result in results:
            if is_invalid_value(coerced_result):
                return CoercionResult(value=UNDEFINED_VALUE)

            coerced_value, coerced_errors = coerced_result
            if is_invalid_value(coerced_value):
                return CoercionResult(value=UNDEFINED_VALUE)
            if coerced_errors:
                errors.extend(coerced_errors)
            elif not errors:
                coerced_values.append(coerced_value)

        return CoercionResult(value=coerced_values, errors=errors)

    coerced_item_value, coerced_item_errors = await inner_coercer(
        parent_node, node, ctx, variables=variables, path=path
    )
    if is_invalid_value(coerced_item_value):
        return CoercionResult(value=UNDEFINED_VALUE)
    return CoercionResult(
        value=[coerced_item_value], errors=coerced_item_errors
    )
Пример #16
0
async def input_object_coercer(
    parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
    node: "Node",
    value: Any,
    ctx: Optional[Any],
    input_object_type: "GraphQLInputObjectType",
    path: Optional["Path"] = None,
) -> "CoercionResult":
    """
    Computes the value of an input object.
    :param parent_node: the root parent AST node
    :param node: the AST node to treat
    :param value: the raw value to compute
    :param ctx: context passed to the query execution
    :param input_object_type: the GraphQLInputObjectType instance of the input
    object
    :param path: the path traveled until this coercer
    :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode]
    :type node: Node
    :type value: Any
    :type ctx: Optional[Any]
    :type input_object_type: GraphQLInputObjectType
    :type path: Optional[Path]
    :return: the coercion result
    :rtype: CoercionResult
    """
    # pylint: disable=too-many-locals
    if not isinstance(value, dict):
        return CoercionResult(errors=[
            coercion_error(
                f"Expected type < {input_object_type.name} > to be an object",
                node,
                path,
            )
        ])

    input_fields = input_object_type.input_fields

    results = await asyncio.gather(*[
        input_field_value_coercer(
            parent_node,
            node,
            value.get(input_field_name, UNDEFINED_VALUE),
            ctx,
            input_field,
            path=Path(path, input_field_name),
        ) for input_field_name, input_field in input_fields.items()
    ])

    errors = []
    coerced_values = {}
    for input_field_name, input_field_result in zip(input_fields, results):
        if is_invalid_value(input_field_result):
            continue

        input_field_value, input_field_errors = input_field_result
        if input_field_errors:
            errors.extend(input_field_errors)
        elif not errors:
            coerced_values[input_field_name] = input_field_value

    for input_field_name in value:
        if input_field_name not in input_fields:
            errors.append(
                coercion_error(
                    f"Field < {input_field_name} > is not defined by type "
                    f"< {input_object_type.name} >",
                    node,
                    path,
                    did_you_mean(
                        get_close_matches(input_field_name,
                                          input_fields.keys(),
                                          n=5)),
                ))

    return CoercionResult(value=coerced_values, errors=errors)
Пример #17
0
async def argument_coercer(
    argument_definition: "GraphQLArgument",
    node: Union["FieldNode", "DirectiveNode"],
    argument_node: Optional["ArgumentNode"],
    variable_values: Dict[str, Any],
    ctx: Optional[Any],
    directives: Optional[Callable],
) -> Union["CoercionResult", "UNDEFINED_VALUE"]:
    """
    Computes the value of an argument.
    :param argument_definition: the argument definition to treat
    :param node: AST node linked to the argument
    :param argument_node: AST node representing the argument
    :param variable_values: the variables provided in the GraphQL request
    :param ctx: context passed to the query execution
    :param directives: the directives to execute
    :type argument_definition: GraphQLArgument
    :type node: Union[FieldNode, DirectiveNode]
    :type argument_node: Optional[ArgumentNode]
    :type variable_values: Dict[str, Any]
    :type ctx: Optional[Any]
    :type directives: Optional[Callable]
    :return: the computed value
    :rtype: Union["CoercionResult", "UNDEFINED_VALUE"]
    """
    # pylint: disable=too-many-locals,too-many-branches,too-complex
    name = argument_definition.name
    arg_type = argument_definition.graphql_type

    if argument_node and isinstance(argument_node.value, VariableNode):
        variable_name = argument_node.value.name.value
        has_value = variable_values and variable_name in variable_values
        is_null = has_value and variable_values[variable_name] is None
    else:
        has_value = argument_node is not None
        is_null = argument_node and isinstance(argument_node.value,
                                               NullValueNode)

    coercion_result = UNDEFINED_VALUE
    value_node = None
    if not has_value and argument_definition.default_value is not None:
        value_node = argument_definition.default_value
    elif (not has_value or is_null) and arg_type.is_non_null_type:
        if is_null:
            return CoercionResult(errors=[
                graphql_error_from_nodes(
                    f"Argument < {name} > of non-null type < {arg_type} > "
                    "must not be null.",
                    nodes=argument_node.value,
                )
            ])
        if argument_node and isinstance(argument_node.value, VariableNode):
            return CoercionResult(errors=[
                graphql_error_from_nodes(
                    f"Argument < {name} > of required type < {arg_type} > "
                    f"was provided the variable < ${variable_name} > "
                    "which was not provided a runtime value.",
                    nodes=argument_node.value,
                )
            ])
        return CoercionResult(errors=[
            graphql_error_from_nodes(
                f"Argument < {name} > of required type < {arg_type} > was "
                "not provided.",
                nodes=node,
            )
        ])
    elif has_value:
        if isinstance(argument_node.value, NullValueNode):
            coercion_result = CoercionResult(value=None)
        elif isinstance(argument_node.value, VariableNode):
            coercion_result = CoercionResult(
                value=variable_values[argument_node.value.name.value])
        else:
            value_node = argument_node.value

    if value_node:
        coercion_result = await argument_definition.literal_coercer(
            argument_definition.definition,
            value_node,
            ctx,
            variables=variable_values,
        )

    if is_invalid_value(coercion_result):
        return coercion_result

    value, errors = coercion_result
    if is_invalid_value(value):
        return CoercionResult(errors=[
            graphql_error_from_nodes(
                f"Argument < {name} > has invalid value < {value_node} >.",
                nodes=argument_node.value,
            )
        ])
    if not directives or errors:
        return coercion_result

    return await directives(node,
                            argument_node,
                            value,
                            ctx,
                            context_coercer=ctx)