diff --git a/.gitignore b/.gitignore index 97c2e1a7..77f41503 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ nosetests.xml coverage.xml *.cover .pytest_cache/ +.python-version # PyBuilder target/ diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index e77050e2..e6150442 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -529,7 +529,7 @@ def complete_value( lambda resolved: complete_value( exe_context, return_type, field_asts, info, path, resolved ), - lambda error: Promise.rejected( + lambda error: Promise.rejected( # type: ignore GraphQLLocatedError(field_asts, original_error=error, path=path) ), ) @@ -602,7 +602,9 @@ def complete_list_value( completed_results.append(completed_item) index += 1 - return Promise.all(completed_results) if contains_promise else completed_results + return ( # type: ignore + Promise.all(completed_results) if contains_promise else completed_results + ) def complete_leaf_value( @@ -711,7 +713,9 @@ def complete_object_value( # Collect sub-fields to execute to complete this value. subfield_asts = exe_context.get_sub_fields(return_type, field_asts) - return execute_fields(exe_context, return_type, result, subfield_asts, path, info) + return execute_fields( # type: ignore + exe_context, return_type, result, subfield_asts, path, info + ) def complete_nonnull_value( diff --git a/graphql/execution/executors/thread.py b/graphql/execution/executors/thread.py index f540a1a0..ebd5cf16 100644 --- a/graphql/execution/executors/thread.py +++ b/graphql/execution/executors/thread.py @@ -35,7 +35,7 @@ def clean(self): def execute_in_thread(self, fn, *args, **kwargs): # type: (Callable, *Any, **Any) -> Promise - promise = Promise() + promise = Promise() # type: ignore thread = Thread(target=process, args=(promise, fn, args, kwargs)) thread.start() self.threads.append(thread) diff --git a/graphql/execution/tests/test_executor_thread.py b/graphql/execution/tests/test_executor_thread.py index ceaf8ce8..0c06bbe9 100644 --- a/graphql/execution/tests/test_executor_thread.py +++ b/graphql/execution/tests/test_executor_thread.py @@ -11,12 +11,21 @@ GraphQLSchema, GraphQLString, ) +from pytest import mark from ..executors.thread import ThreadExecutor from .test_mutations import assert_evaluate_mutations_serially from .utils import rejected, resolved +### +# Disabled because all these tests are flaky +# The culprit is that the `ThreadExecutor` incorrectly assumes that +# the `Promise` class is thread-safe. +# See https://git.io/JeA3s +### + +@mark.xfail def test_executes_arbitary_code(): # type: () -> None class Data(object): @@ -157,6 +166,7 @@ def handle_result(result): ) +@mark.xfail def test_synchronous_error_nulls_out_error_subtrees(): # type: () -> None ast = parse( @@ -289,6 +299,7 @@ def handle_results(result): handle_results(execute(schema, ast, Data(), executor=ThreadExecutor())) +@mark.xfail def test_evaluates_mutations_serially(): # type: () -> None assert_evaluate_mutations_serially(executor=ThreadExecutor()) diff --git a/graphql/type/definition.py b/graphql/type/definition.py index 1d7b8a0c..3f8d2ba0 100644 --- a/graphql/type/definition.py +++ b/graphql/type/definition.py @@ -649,7 +649,7 @@ def __init__( self.name = name self.description = description if container_type is None: - container_type = dict # type: ignore + container_type = OrderedDict # type: ignore assert callable(container_type), "container_type must be callable" self.container_type = container_type self._fields = fields diff --git a/graphql/utils/value_from_ast.py b/graphql/utils/value_from_ast.py index 7ad52bca..07bb4d00 100644 --- a/graphql/utils/value_from_ast.py +++ b/graphql/utils/value_from_ast.py @@ -1,4 +1,5 @@ from ..language import ast +from ..pyutils.ordereddict import OrderedDict from ..type import ( GraphQLEnumType, GraphQLInputObjectType, @@ -56,13 +57,15 @@ def value_from_ast(value_ast, type, variables=None): for field_ast in value_ast.fields: field_asts[field_ast.name.value] = field_ast - obj = {} + obj_items = [] for field_name, field in fields.items(): if field_name not in field_asts: if field.default_value is not None: # We use out_name as the output name for the # dict if exists - obj[field.out_name or field_name] = field.default_value + obj_items.append( + (field.out_name or field_name, field.default_value) + ) continue @@ -72,7 +75,9 @@ def value_from_ast(value_ast, type, variables=None): # We use out_name as the output name for the # dict if exists - obj[field.out_name or field_name] = field_value + obj_items.append((field.out_name or field_name, field_value)) + + obj = OrderedDict(obj_items) return type.create_container(obj) diff --git a/setup.py b/setup.py index 64bd95ec..54801c05 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ sys.path[:] = path_copy -install_requires = ["six>=1.10.0", "promise>=2.1", "rx>=1.6,<3"] +install_requires = ["six>=1.10.0", "promise>=2.3", "rx>=1.6,<3"] tests_requires = [ "pytest>=3.3,<4.0", diff --git a/tox.ini b/tox.ini index 3889aa4c..6b3cb9d4 100644 --- a/tox.ini +++ b/tox.ini @@ -3,12 +3,7 @@ envlist = py27,py34,py35,py36,py37,pre-commit,pypy,mypy,docs [testenv] deps = - pytest>=3.3,<4.0 - gevent>=1.1 - promise>=2.0 - six>=1.10.0 - pytest-mock - pytest-benchmark + .[test] commands = py{27,34,py}: py.test graphql tests {posargs} py{35,36,37}: py.test graphql tests tests_py35 {posargs}