99
1010
1111import json
12- from collections import MutableMapping , namedtuple
12+ from collections import namedtuple
1313
1414import six
1515from graphql import get_default_backend
1616from graphql .error import format_error as default_format_error
1717from graphql .execution import ExecutionResult
18+ from graphql .type import GraphQLSchema
1819
1920from .error import HttpQueryError
2021
22+ try : # pragma: no cover (Python >= 3.3)
23+ from collections .abc import MutableMapping
24+ except ImportError : # pragma: no cover (Python < 3.3)
25+ # noinspection PyUnresolvedReferences,PyProtectedMember
26+ from collections import MutableMapping
27+
2128# Necessary for static type checking
29+ # noinspection PyUnreachableCode
2230if False : # flake8: noqa
23- from typing import List , Dict , Optional , Tuple , Any , Union , Callable , Type
24- from graphql import GraphQLSchema , GraphQLBackend
31+ from typing import Any , Callable , Dict , List , Optional , Type , Union
32+ from graphql import GraphQLBackend
2533
2634
2735__all__ = [
2836 "run_http_query" ,
2937 "encode_execution_results" ,
3038 "load_json_body" ,
31- "HttpQueryError" ]
39+ "json_encode" ,
40+ "json_encode_pretty" ,
41+ "HttpQueryError" ,
42+ "RequestParams" ,
43+ "ServerResults" ,
44+ "ServerResponse" ,
45+ ]
46+
47+
48+ # The public data structures
49+
50+ RequestParams = namedtuple ("RequestParams" , "query variables operation_name" )
51+
52+ ServerResults = namedtuple ("ServerResults" , "results params" )
53+
54+ ServerResponse = namedtuple ("ServerResponse" , "body status_code" )
3255
3356
3457# The public helper functions
@@ -41,7 +64,7 @@ def run_http_query(
4164 query_data = None , # type: Optional[Dict]
4265 batch_enabled = False , # type: bool
4366 catch = False , # type: bool
44- ** execute_options # type: Dict
67+ ** execute_options # type: Any
4568):
4669 """Execute GraphQL coming from an HTTP query against a given schema.
4770
@@ -55,9 +78,11 @@ def run_http_query(
5578 All other keyword arguments are passed on to the GraphQL-Core function for
5679 executing GraphQL queries.
5780
58- This functions returns a tuple with the list of ExecutionResults as first item
81+ Returns a ServerResults tuple with the list of ExecutionResults as first item
5982 and the list of parameters that have been used for execution as second item.
6083 """
84+ if not isinstance (schema , GraphQLSchema ):
85+ raise TypeError ("Expected a GraphQL schema, but received {!r}." .format (schema ))
6186 if request_method not in ("get" , "post" ):
6287 raise HttpQueryError (
6388 405 ,
@@ -78,7 +103,7 @@ def run_http_query(
78103 if not is_batch :
79104 if not isinstance (data , (dict , MutableMapping )):
80105 raise HttpQueryError (
81- 400 , "GraphQL params should be a dict. Received {}." .format (data )
106+ 400 , "GraphQL params should be a dict. Received {!r }." .format (data )
82107 )
83108 data = [data ]
84109 elif not batch_enabled :
@@ -94,12 +119,12 @@ def run_http_query(
94119
95120 all_params = [get_graphql_params (entry , extra_data ) for entry in data ]
96121
97- responses = [
122+ results = [
98123 get_response (schema , params , catch_exc , allow_only_query , ** execute_options )
99124 for params in all_params
100125 ]
101126
102- return responses , all_params
127+ return ServerResults ( results , all_params )
103128
104129
105130def encode_execution_results (
@@ -108,7 +133,7 @@ def encode_execution_results(
108133 is_batch = False , # type: bool
109134 encode = None , # type: Callable[[Dict], Any]
110135):
111- # type: (...) -> Tuple[Any, int]
136+ # type: (...) -> ServerResponse
112137 """Serialize the ExecutionResults.
113138
114139 This function takes the ExecutionResults that are returned by run_http_query()
@@ -117,18 +142,21 @@ def encode_execution_results(
117142 the first one will be used. You can also pass a custom function that formats the
118143 errors in the ExecutionResults, expecting a dictionary as result and another custom
119144 function that is used to serialize the output.
145+
146+ Returns a ServerResponse tuple with the serialized response as the first item and
147+ a status code of 200 or 400 in case any result was invalid as the second item.
120148 """
121- responses = [
149+ results = [
122150 format_execution_result (execution_result , format_error or default_format_error )
123151 for execution_result in execution_results
124152 ]
125- result , status_codes = zip (* responses )
153+ result , status_codes = zip (* results )
126154 status_code = max (status_codes )
127155
128156 if not is_batch :
129157 result = result [0 ]
130158
131- return ( encode or json_encode )(result ), status_code
159+ return ServerResponse (( encode or json_encode )(result ), status_code )
132160
133161
134162def load_json_body (data ):
@@ -144,32 +172,46 @@ def load_json_body(data):
144172 raise HttpQueryError (400 , "POST body sent invalid JSON." )
145173
146174
147- # Some more private helpers
175+ def json_encode (data ):
176+ # type: (Union[Dict,List]) -> str
177+ """Serialize the given data(a dictionary or a list) using JSON.
178+
179+ The given data (a dictionary or a list) will be serialized using JSON
180+ and returned as a string that will be nicely formatted if you set pretty=True.
181+ """
182+ return json .dumps (data , separators = ("," , ":" ))
183+
148184
149- GraphQLParams = namedtuple ("GraphQLParams" , "query variables operation_name" )
185+ def json_encode_pretty (data ):
186+ # type: (Union[Dict,List]) -> str
187+ """Serialize the given data using JSON with nice formatting."""
188+ return json .dumps (data , indent = 2 , separators = ("," , ": " ))
189+
190+
191+ # Some more private helpers
150192
151- GraphQLResponse = namedtuple ("GraphQLResponse " , "result status_code" )
193+ FormattedResult = namedtuple ("FormattedResult " , "result status_code" )
152194
153195
154196class _NoException (Exception ):
155197 """Private exception used when we don't want to catch any real exception."""
156198
157199
158200def get_graphql_params (data , query_data ):
159- # type: (Dict, Dict) -> GraphQLParams
201+ # type: (Dict, Dict) -> RequestParams
160202 """Fetch GraphQL query, variables and operation name parameters from given data.
161203
162204 You need to pass both the data from the HTTP request body and the HTTP query string.
163205 Params from the request body will take precedence over those from the query string.
164206
165- You will get a GraphQLParams object with these parameters as attributes in return.
207+ You will get a RequestParams tuple with these parameters in return.
166208 """
167209 query = data .get ("query" ) or query_data .get ("query" )
168210 variables = data .get ("variables" ) or query_data .get ("variables" )
169211 # document_id = data.get('documentId')
170212 operation_name = data .get ("operationName" ) or query_data .get ("operationName" )
171213
172- return GraphQLParams (query , load_json_variables (variables ), operation_name )
214+ return RequestParams (query , load_json_variables (variables ), operation_name )
173215
174216
175217def load_json_variables (variables ):
@@ -190,12 +232,12 @@ def load_json_variables(variables):
190232
191233def execute_graphql_request (
192234 schema , # type: GraphQLSchema
193- params , # type: GraphQLParams
235+ params , # type: RequestParams
194236 allow_only_query = False , # type: bool
195237 backend = None , # type: GraphQLBackend
196- ** kwargs # type: Dict
238+ ** kwargs # type: Any
197239):
198- """Execute a GraphQL request and return a ExecutionResult.
240+ """Execute a GraphQL request and return an ExecutionResult.
199241
200242 You need to pass the GraphQL schema and the GraphQLParams that you can get
201243 with the get_graphql_params() function. If you only want to allow GraphQL query
@@ -235,18 +277,18 @@ def execute_graphql_request(
235277
236278def get_response (
237279 schema , # type: GraphQLSchema
238- params , # type: GraphQLParams
280+ params , # type: RequestParams
239281 catch_exc , # type: Type[BaseException]
240282 allow_only_query = False , # type: bool
241- ** kwargs # type: Dict
283+ ** kwargs # type: Any
242284):
243285 # type: (...) -> Optional[ExecutionResult]
244286 """Get an individual execution result as response, with option to catch errors.
245287
246288 This does the same as execute_graphql_request() except that you can catch errors
247289 that belong to an exception class that you need to pass as a parameter.
248290 """
249-
291+ # noinspection PyBroadException
250292 try :
251293 execution_result = execute_graphql_request (
252294 schema , params , allow_only_query , ** kwargs
@@ -261,10 +303,10 @@ def format_execution_result(
261303 execution_result , # type: Optional[ExecutionResult]
262304 format_error , # type: Optional[Callable[[Exception], Dict]]
263305):
264- # type: (...) -> GraphQLResponse
306+ # type: (...) -> FormattedResult
265307 """Format an execution result into a GraphQLResponse.
266308
267- This converts the given execution result into a GraphQLResponse that contains
309+ This converts the given execution result into a FormattedResult that contains
268310 the ExecutionResult converted to a dictionary and an appropriate status code.
269311 """
270312 status_code = 200
@@ -276,17 +318,4 @@ def format_execution_result(
276318 else :
277319 response = None
278320
279- return GraphQLResponse (response , status_code )
280-
281-
282- def json_encode (data , pretty = False ):
283- # type: (Union[Dict,List], bool) -> str
284- """Serialize the given data using JSON.
285-
286- The given data (a dictionary or a list) will be serialized using JSON
287- and returned as a string that will be nicely formatted if you set pretty=True.
288- """
289- if not pretty :
290- return json .dumps (data , separators = ("," , ":" ))
291-
292- return json .dumps (data , indent = 2 , separators = ("," , ": " ))
321+ return FormattedResult (response , status_code )
0 commit comments