graphql.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. from asyncio import ensure_future
  2. from inspect import isawaitable
  3. from typing import Any, Awaitable, Callable, Dict, Optional, Union, Type, cast
  4. from .error import GraphQLError
  5. from .execution import execute, ExecutionResult, ExecutionContext, Middleware
  6. from .language import parse, Source
  7. from .pyutils import AwaitableOrValue
  8. from .type import (
  9. GraphQLFieldResolver,
  10. GraphQLSchema,
  11. GraphQLTypeResolver,
  12. validate_schema,
  13. )
  14. __all__ = ["graphql", "graphql_sync"]
  15. async def graphql(
  16. schema: GraphQLSchema,
  17. source: Union[str, Source],
  18. root_value: Any = None,
  19. context_value: Any = None,
  20. variable_values: Optional[Dict[str, Any]] = None,
  21. operation_name: Optional[str] = None,
  22. field_resolver: Optional[GraphQLFieldResolver] = None,
  23. type_resolver: Optional[GraphQLTypeResolver] = None,
  24. middleware: Optional[Middleware] = None,
  25. execution_context_class: Optional[Type[ExecutionContext]] = None,
  26. is_awaitable: Optional[Callable[[Any], bool]] = None,
  27. ) -> ExecutionResult:
  28. """Execute a GraphQL operation asynchronously.
  29. This is the primary entry point function for fulfilling GraphQL operations by
  30. parsing, validating, and executing a GraphQL document along side a GraphQL schema.
  31. More sophisticated GraphQL servers, such as those which persist queries, may wish
  32. to separate the validation and execution phases to a static time tooling step,
  33. and a server runtime step.
  34. Accepts the following arguments:
  35. :arg schema:
  36. The GraphQL type system to use when validating and executing a query.
  37. :arg source:
  38. A GraphQL language formatted string representing the requested operation.
  39. :arg root_value:
  40. The value provided as the first argument to resolver functions on the top level
  41. type (e.g. the query object type).
  42. :arg context_value:
  43. The context value is provided as an attribute of the second argument
  44. (the resolve info) to resolver functions. It is used to pass shared information
  45. useful at any point during query execution, for example the currently logged in
  46. user and connections to databases or other services.
  47. :arg variable_values:
  48. A mapping of variable name to runtime value to use for all variables defined
  49. in the request string.
  50. :arg operation_name:
  51. The name of the operation to use if request string contains multiple possible
  52. operations. Can be omitted if request string contains only one operation.
  53. :arg field_resolver:
  54. A resolver function to use when one is not provided by the schema.
  55. If not provided, the default field resolver is used (which looks for a value
  56. or method on the source value with the field's name).
  57. :arg type_resolver:
  58. A type resolver function to use when none is provided by the schema.
  59. If not provided, the default type resolver is used (which looks for a
  60. ``__typename`` field or alternatively calls the
  61. :meth:`~graphql.type.GraphQLObjectType.is_type_of` method).
  62. :arg middleware:
  63. The middleware to wrap the resolvers with
  64. :arg execution_context_class:
  65. The execution context class to use to build the context
  66. :arg is_awaitable:
  67. The predicate to be used for checking whether values are awaitable
  68. """
  69. # Always return asynchronously for a consistent API.
  70. result = graphql_impl(
  71. schema,
  72. source,
  73. root_value,
  74. context_value,
  75. variable_values,
  76. operation_name,
  77. field_resolver,
  78. type_resolver,
  79. middleware,
  80. execution_context_class,
  81. is_awaitable,
  82. )
  83. if isawaitable(result):
  84. return await cast(Awaitable[ExecutionResult], result)
  85. return cast(ExecutionResult, result)
  86. def assume_not_awaitable(_value: Any) -> bool:
  87. """Replacement for isawaitable if everything is assumed to be synchronous."""
  88. return False
  89. def graphql_sync(
  90. schema: GraphQLSchema,
  91. source: Union[str, Source],
  92. root_value: Any = None,
  93. context_value: Any = None,
  94. variable_values: Optional[Dict[str, Any]] = None,
  95. operation_name: Optional[str] = None,
  96. field_resolver: Optional[GraphQLFieldResolver] = None,
  97. type_resolver: Optional[GraphQLTypeResolver] = None,
  98. middleware: Optional[Middleware] = None,
  99. execution_context_class: Optional[Type[ExecutionContext]] = None,
  100. check_sync: bool = False,
  101. ) -> ExecutionResult:
  102. """Execute a GraphQL operation synchronously.
  103. The graphql_sync function also fulfills GraphQL operations by parsing, validating,
  104. and executing a GraphQL document along side a GraphQL schema. However, it guarantees
  105. to complete synchronously (or throw an error) assuming that all field resolvers
  106. are also synchronous.
  107. Set check_sync to True to still run checks that no awaitable values are returned.
  108. """
  109. is_awaitable = (
  110. check_sync
  111. if callable(check_sync)
  112. else (None if check_sync else assume_not_awaitable)
  113. )
  114. result = graphql_impl(
  115. schema,
  116. source,
  117. root_value,
  118. context_value,
  119. variable_values,
  120. operation_name,
  121. field_resolver,
  122. type_resolver,
  123. middleware,
  124. execution_context_class,
  125. is_awaitable,
  126. )
  127. # Assert that the execution was synchronous.
  128. if isawaitable(result):
  129. ensure_future(cast(Awaitable[ExecutionResult], result)).cancel()
  130. raise RuntimeError("GraphQL execution failed to complete synchronously.")
  131. return cast(ExecutionResult, result)
  132. def graphql_impl(
  133. schema: GraphQLSchema,
  134. source: Union[str, Source],
  135. root_value: Any,
  136. context_value: Any,
  137. variable_values: Optional[Dict[str, Any]],
  138. operation_name: Optional[str],
  139. field_resolver: Optional[GraphQLFieldResolver],
  140. type_resolver: Optional[GraphQLTypeResolver],
  141. middleware: Optional[Middleware],
  142. execution_context_class: Optional[Type[ExecutionContext]],
  143. is_awaitable: Optional[Callable[[Any], bool]],
  144. ) -> AwaitableOrValue[ExecutionResult]:
  145. """Execute a query, return asynchronously only if necessary."""
  146. # Validate Schema
  147. schema_validation_errors = validate_schema(schema)
  148. if schema_validation_errors:
  149. return ExecutionResult(data=None, errors=schema_validation_errors)
  150. # Parse
  151. try:
  152. document = parse(source)
  153. except GraphQLError as error:
  154. return ExecutionResult(data=None, errors=[error])
  155. # Validate
  156. from .validation import validate
  157. validation_errors = validate(schema, document)
  158. if validation_errors:
  159. return ExecutionResult(data=None, errors=validation_errors)
  160. # Execute
  161. return execute(
  162. schema,
  163. document,
  164. root_value,
  165. context_value,
  166. variable_values,
  167. operation_name,
  168. field_resolver,
  169. type_resolver,
  170. None,
  171. middleware,
  172. execution_context_class,
  173. is_awaitable,
  174. )