Skip to content

Commit

Permalink
Added excution_context_class for custom ExecutionContext
Browse files Browse the repository at this point in the history
  • Loading branch information
syrusakbary committed Sep 6, 2018
1 parent d3bdab4 commit 238f0a1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 73 deletions.
87 changes: 44 additions & 43 deletions graphql/execution/execute.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from inspect import isawaitable
from typing import (
Any, Awaitable, Dict, Iterable, List, NamedTuple, Optional, Set, Union,
Tuple, cast)
Tuple, Type, cast)

from ..error import GraphQLError, INVALID, located_error
from ..language import (
Expand Down Expand Up @@ -60,48 +60,6 @@ class ExecutionResult(NamedTuple):
ExecutionResult.__new__.__defaults__ = (None, None) # type: ignore


def execute(
schema: GraphQLSchema, document: DocumentNode,
root_value: Any=None, context_value: Any=None,
variable_values: Dict[str, Any]=None,
operation_name: str=None, field_resolver: GraphQLFieldResolver=None
) -> MaybeAwaitable[ExecutionResult]:
"""Execute a GraphQL operation.
Implements the "Evaluating requests" section of the GraphQL specification.
Returns an ExecutionResult (if all encountered resolvers are synchronous),
or a coroutine object eventually yielding an ExecutionResult.
If the arguments to this function do not result in a legal execution
context, a GraphQLError will be thrown immediately explaining the invalid
input.
"""
# If arguments are missing or incorrect, throw an error.
assert_valid_execution_arguments(schema, document, variable_values)

# If a valid execution context cannot be created due to incorrect
# arguments, a "Response" with only errors is returned.
exe_context = ExecutionContext.build(
schema, document, root_value, context_value,
variable_values, operation_name, field_resolver)

# Return early errors if execution context failed.
if isinstance(exe_context, list):
return ExecutionResult(data=None, errors=exe_context)

# Return a possible coroutine object that will eventually yield the data
# described by the "Response" section of the GraphQL specification.
#
# If errors are encountered while executing a GraphQL field, only that
# field and its descendants will be omitted, and sibling fields will still
# be executed. An execution which encounters errors will still result in a
# coroutine object that can be executed without errors.

data = exe_context.execute_operation(exe_context.operation, root_value)
return exe_context.build_response(data)


class ExecutionContext:
"""Data that must be available at all points during query execution.
Expand Down Expand Up @@ -794,6 +752,49 @@ def collect_subfields(
return sub_field_nodes


def execute(
schema: GraphQLSchema, document: DocumentNode,
root_value: Any=None, context_value: Any=None,
variable_values: Dict[str, Any]=None,
operation_name: str=None, field_resolver: GraphQLFieldResolver=None,
execution_context_class: Type[ExecutionContext]=ExecutionContext,
) -> MaybeAwaitable[ExecutionResult]:
"""Execute a GraphQL operation.
Implements the "Evaluating requests" section of the GraphQL specification.
Returns an ExecutionResult (if all encountered resolvers are synchronous),
or a coroutine object eventually yielding an ExecutionResult.
If the arguments to this function do not result in a legal execution
context, a GraphQLError will be thrown immediately explaining the invalid
input.
"""
# If arguments are missing or incorrect, throw an error.
assert_valid_execution_arguments(schema, document, variable_values)

# If a valid execution context cannot be created due to incorrect
# arguments, a "Response" with only errors is returned.
exe_context = execution_context_class.build(
schema, document, root_value, context_value,
variable_values, operation_name, field_resolver)

# Return early errors if execution context failed.
if isinstance(exe_context, list):
return ExecutionResult(data=None, errors=exe_context)

# Return a possible coroutine object that will eventually yield the data
# described by the "Response" section of the GraphQL specification.
#
# If errors are encountered while executing a GraphQL field, only that
# field and its descendants will be omitted, and sibling fields will still
# be executed. An execution which encounters errors will still result in a
# coroutine object that can be executed without errors.

data = exe_context.execute_operation(exe_context.operation, root_value)
return exe_context.build_response(data)


def assert_valid_execution_arguments(
schema: GraphQLSchema, document: DocumentNode,
raw_variable_values: Dict[str, Any]=None) -> None:
Expand Down
71 changes: 41 additions & 30 deletions graphql/graphql.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
from asyncio import ensure_future
from inspect import isawaitable
from typing import Any, Awaitable, Callable, Dict, Union, cast
from typing import Any, Awaitable, Callable, Dict, Union, Type, cast

from .error import GraphQLError
from .execution import execute
from .language import parse, Source
from .pyutils import MaybeAwaitable
from .type import GraphQLSchema, validate_schema
from .execution.execute import ExecutionResult
from .execution.execute import ExecutionResult, ExecutionContext

__all__ = ['graphql', 'graphql_sync']
__all__ = ["graphql", "graphql_sync"]


async def graphql(
schema: GraphQLSchema,
source: Union[str, Source],
root_value: Any=None,
context_value: Any=None,
variable_values: Dict[str, Any]=None,
operation_name: str=None,
field_resolver: Callable=None) -> ExecutionResult:
schema: GraphQLSchema,
source: Union[str, Source],
root_value: Any = None,
context_value: Any = None,
variable_values: Dict[str, Any] = None,
operation_name: str = None,
field_resolver: Callable = None,
execution_context_class: Type[ExecutionContext] = ExecutionContext,
) -> ExecutionResult:
"""Execute a GraphQL operation asynchronously.
This is the primary entry point function for fulfilling GraphQL operations
Expand Down Expand Up @@ -65,7 +67,9 @@ async def graphql(
context_value,
variable_values,
operation_name,
field_resolver)
field_resolver,
execution_context_class,
)

if isawaitable(result):
return await cast(Awaitable[ExecutionResult], result)
Expand All @@ -74,13 +78,15 @@ async def graphql(


def graphql_sync(
schema: GraphQLSchema,
source: Union[str, Source],
root_value: Any = None,
context_value: Any = None,
variable_values: Dict[str, Any] = None,
operation_name: str = None,
field_resolver: Callable = None) -> ExecutionResult:
schema: GraphQLSchema,
source: Union[str, Source],
root_value: Any = None,
context_value: Any = None,
variable_values: Dict[str, Any] = None,
operation_name: str = None,
field_resolver: Callable = None,
execution_context_class: Type[ExecutionContext] = ExecutionContext,
) -> ExecutionResult:
"""Execute a GraphQL operation synchronously.
The graphql_sync function also fulfills GraphQL operations by parsing,
Expand All @@ -95,26 +101,28 @@ def graphql_sync(
context_value,
variable_values,
operation_name,
field_resolver)
field_resolver,
execution_context_class,
)

# Assert that the execution was synchronous.
if isawaitable(result):
ensure_future(cast(Awaitable[ExecutionResult], result)).cancel()
raise RuntimeError(
'GraphQL execution failed to complete synchronously.')
raise RuntimeError("GraphQL execution failed to complete synchronously.")

return cast(ExecutionResult, result)


def graphql_impl(
schema,
source,
root_value,
context_value,
variable_values,
operation_name,
field_resolver
) -> MaybeAwaitable[ExecutionResult]:
schema,
source,
root_value,
context_value,
variable_values,
operation_name,
field_resolver,
execution_context_class,
) -> MaybeAwaitable[ExecutionResult]:
"""Execute a query, return asynchronously only if necessary."""
# Validate Schema
schema_validation_errors = validate_schema(schema)
Expand All @@ -132,6 +140,7 @@ def graphql_impl(

# Validate
from .validation import validate

validation_errors = validate(schema, document)
if validation_errors:
return ExecutionResult(data=None, errors=validation_errors)
Expand All @@ -144,4 +153,6 @@ def graphql_impl(
context_value,
variable_values,
operation_name,
field_resolver)
field_resolver,
execution_context_class,
)

0 comments on commit 238f0a1

Please sign in to comment.