Skip to content

Query Builder + Promise 2.0 + DataLoader #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 58 commits into from
Apr 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
90799df
Added first proof of concept of query builder
syrusakbary Sep 7, 2016
c9a7f72
Added promise check resolvers
syrusakbary Sep 8, 2016
01f1160
Isolated benchmark code
syrusakbary Sep 8, 2016
3c5696a
Cleaned Fragment logic
syrusakbary Sep 8, 2016
48e5d1e
Fixed test
syrusakbary Sep 8, 2016
c65bb2c
Improved resolver completion naming
syrusakbary Sep 8, 2016
99641f0
Added fragment function arguments
syrusakbary Sep 8, 2016
6c6f808
Isolated fragment tests
syrusakbary Sep 8, 2016
7c1a5f6
Added nested field fragments
syrusakbary Sep 8, 2016
b6a81df
Added more complete benchmark case
syrusakbary Sep 8, 2016
c5f6e9f
Added equality check to fragments
syrusakbary Sep 8, 2016
06a2472
Added QueryBuilder
syrusakbary Sep 8, 2016
07458cd
Merge branch 'features/next' into features/next-query-builder
syrusakbary Sep 8, 2016
14e4ee6
Make all benchmarks cases similar so is easier to compare
syrusakbary Sep 8, 2016
04b63e6
Improved query builder
syrusakbary Oct 4, 2016
fd0fbf6
Improved fragment abstractions
syrusakbary Oct 5, 2016
f67f813
Improved testing cases in executor
syrusakbary Oct 5, 2016
67fad4a
Improved resolvers with exe_context and info
syrusakbary Oct 5, 2016
162dea3
Improved nonnull checker
syrusakbary Oct 5, 2016
8ca8428
Improved Fragment logic and added more tests
syrusakbary Oct 5, 2016
ea4463e
Improved resolver catch logic
syrusakbary Oct 6, 2016
b48b374
First phase of list working
syrusakbary Oct 7, 2016
e8dafc5
Fixed all tests
syrusakbary Oct 7, 2016
09ff102
Fixed executor test
syrusakbary Oct 7, 2016
dd50783
Improved tests
syrusakbary Oct 7, 2016
61ffcc3
Improved use cases and tests
syrusakbary Oct 7, 2016
45e0a14
Improved naming
syrusakbary Oct 7, 2016
7d4bc99
Improved serial resolver. Added mutations and variables tests
syrusakbary Oct 7, 2016
d4de4a2
Changed querybuilder executor to experimental
syrusakbary Oct 7, 2016
2ab068c
Improved experimental executor code
syrusakbary Oct 7, 2016
a6efe00
Improve experimental imports and install cyordereddict for travis tests
syrusakbary Oct 7, 2016
e4f8e8c
Fixed tests
syrusakbary Oct 7, 2016
ae18289
Fixed python3 issues
syrusakbary Oct 7, 2016
ba8cd06
Fixed pypy tests
syrusakbary Oct 7, 2016
d02a4fe
Fixed imports
syrusakbary Oct 7, 2016
bf2daa3
Fixed imports and PEP8 syntax
syrusakbary Oct 7, 2016
329b6a9
Improved `wrap_in_promise` optional argument
syrusakbary Mar 1, 2017
e16c91a
Merge branch 'master' into features/next-query-builder
syrusakbary Mar 1, 2017
754adb9
Revert "Improved `wrap_in_promise` optional argument"
syrusakbary Mar 1, 2017
656eb7d
Added (temporal) simple way to use the experimental executor
syrusakbary Mar 1, 2017
403d61e
Improved basic benchmark
syrusakbary Mar 1, 2017
8bc7003
Improved experimental resolution
syrusakbary Mar 1, 2017
54724ec
Fixed fragment retreival
syrusakbary Mar 1, 2017
b356f66
Fixed tests
syrusakbary Mar 1, 2017
42a5ac3
Make experimental executor fully compatible with the spec
syrusakbary Mar 7, 2017
dd38759
Fixed tests
syrusakbary Mar 8, 2017
d22798f
Simplified field_ast collection in experimental executor
syrusakbary Mar 8, 2017
a1ecaf4
Refactored imap tools
syrusakbary Mar 8, 2017
197d3ee
Fixed NonNull ordered errors in tests
syrusakbary Mar 11, 2017
81bcf8c
Improved experimental execution speed with simulated Ordered dicts
syrusakbary Mar 13, 2017
ce85533
Improved package tests and updated promise req to 2.0.dev
syrusakbary Mar 13, 2017
16c7613
Fixed fragment container in Python 3
syrusakbary Mar 13, 2017
23f4b48
Fixed Flake8 issues :D
syrusakbary Mar 13, 2017
acfb2c0
Added experimental dataloader
syrusakbary Mar 14, 2017
fdb3032
Accept custom middleware on experimental executor
femesq Mar 16, 2017
6ec9b66
Update executor.py
femesq Mar 16, 2017
b10adc4
Merge pull request #110 from femesq/patch-1
syrusakbary Mar 17, 2017
8878e5c
Fixed promisify dependency
syrusakbary Apr 5, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ before_install:
source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
fi
install:
- pip install pytest-cov pytest-mock coveralls flake8 gevent==1.1b5 six>=1.10.0 promise>=0.4.2
pytest-benchmark
- pip install pytest==2.9.2
- pip install -e .
- pip install -e .[test]
script:
- py.test --cov=graphql graphql tests
after_success:
Expand All @@ -37,6 +34,7 @@ matrix:
script:
- py.test --cov=graphql graphql tests tests_py35
- python: '2.7'
install: pip install flake8
script:
- flake8
deploy:
Expand Down
10 changes: 10 additions & 0 deletions graphql/error/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,14 @@ def resolver(context, *_):
('resolve_or_error', 'return executor.execute(resolve_fn, source, args, context, info)'),
('execute', 'return fn(*args, **kwargs)'), ('resolver', "raise Exception('Failed')")
]
# assert formatted_tb == [
# ('test_reraise', 'result.errors[0].reraise()'),
# ('reraise', 'six.reraise(type(self), self, self.stack)'),
# ('on_complete_resolver', 'result = __resolver(*args, **kwargs)'),
# # ('reraise', 'raise value.with_traceback(tb)'),
# # ('resolve_or_error', 'return executor.execute(resolve_fn, source, args, context, info)'),
# # ('execute', 'return fn(*args, **kwargs)'),
# ('resolver', "raise Exception('Failed')")
# ]

assert str(exc_info.value) == 'Failed'
21 changes: 16 additions & 5 deletions graphql/execution/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys

from six import string_types
from promise import Promise, promise_for_dict, promisify, is_thenable
from promise import Promise, promise_for_dict, is_thenable

from ..error import GraphQLError, GraphQLLocatedError
from ..pyutils.default_ordered_dict import DefaultOrderedDict
Expand All @@ -16,6 +16,7 @@
collect_fields, default_resolve_fn, get_field_def,
get_operation_root_type)
from .executors.sync import SyncExecutor
from .experimental.executor import execute as experimental_execute
from .middleware import MiddlewareManager

logger = logging.getLogger(__name__)
Expand All @@ -25,9 +26,19 @@ def is_promise(obj):
return type(obj) == Promise


use_experimental_executor = False


def execute(schema, document_ast, root_value=None, context_value=None,
variable_values=None, operation_name=None, executor=None,
return_promise=False, middleware=None):
if use_experimental_executor:
return experimental_execute(
schema, document_ast, root_value, context_value,
variable_values, operation_name, executor,
return_promise, middleware
)

assert schema, 'Must provide schema'
assert isinstance(schema, GraphQLSchema), (
'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
Expand Down Expand Up @@ -106,7 +117,7 @@ def collect_result(resolved_result):
results[response_name] = resolved_result
return results

return promisify(result).then(collect_result, None)
return result.then(collect_result, None)

results[response_name] = result
return results
Expand Down Expand Up @@ -210,9 +221,9 @@ def complete_value_catching_error(exe_context, return_type, field_asts, info, re
if is_thenable(completed):
def handle_error(error):
exe_context.errors.append(error)
return Promise.fulfilled(None)
return None

return promisify(completed).then(None, handle_error)
return completed.catch(handle_error)

return completed
except Exception as e:
Expand Down Expand Up @@ -242,7 +253,7 @@ def complete_value(exe_context, return_type, field_asts, info, result):
# If field type is NonNull, complete for inner type, and throw field error if result is null.

if is_thenable(result):
return promisify(result).then(
return Promise.resolve(result).then(
lambda resolved: complete_value(
exe_context,
return_type,
Expand Down
4 changes: 2 additions & 2 deletions graphql/execution/executors/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from asyncio import Future, get_event_loop, iscoroutine, wait

from promise import promisify
from promise import Promise

try:
from asyncio import ensure_future
Expand Down Expand Up @@ -49,5 +49,5 @@ def execute(self, fn, *args, **kwargs):
if isinstance(result, Future) or iscoroutine(result):
future = ensure_future(result, loop=self.loop)
self.futures.append(future)
return promisify(future)
return Promise.resolve(future)
return result
4 changes: 2 additions & 2 deletions graphql/execution/executors/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def process(p, f, args, kwargs):
try:
val = f(*args, **kwargs)
p.fulfill(val)
p.do_resolve(val)
except Exception as e:
p.reject(e)
p.do_reject(e)
Empty file.
64 changes: 64 additions & 0 deletions graphql/execution/experimental/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from promise import Promise

from ...type import GraphQLSchema
from ..base import ExecutionContext, ExecutionResult, get_operation_root_type
from ..executors.sync import SyncExecutor
from ..middleware import MiddlewareManager
from .fragment import Fragment


def execute(schema, document_ast, root_value=None, context_value=None,
variable_values=None, operation_name=None, executor=None,
return_promise=False, middleware=None):
assert schema, 'Must provide schema'
assert isinstance(schema, GraphQLSchema), (
'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
'not multiple versions of GraphQL installed in your node_modules directory.'
)
if middleware:
if not isinstance(middleware, MiddlewareManager):
middleware = MiddlewareManager(*middleware)
assert isinstance(middleware, MiddlewareManager), (
'middlewares have to be an instance'
' of MiddlewareManager. Received "{}".'.format(middleware)
)

if executor is None:
executor = SyncExecutor()

context = ExecutionContext(
schema,
document_ast,
root_value,
context_value,
variable_values,
operation_name,
executor,
middleware
)

def executor(resolve, reject):
return resolve(execute_operation(context, context.operation, root_value))

def on_rejected(error):
context.errors.append(error)
return None

def on_resolve(data):
return ExecutionResult(data=data, errors=context.errors)

promise = Promise(executor).catch(on_rejected).then(on_resolve)
if return_promise:
return promise
context.executor.wait_until_finished()
return promise.get()


def execute_operation(exe_context, operation, root_value):
type = get_operation_root_type(exe_context.schema, operation)
execute_serially = operation.operation == 'mutation'

fragment = Fragment(type=type, field_asts=[operation], context=exe_context)
if execute_serially:
return fragment.resolve_serially(root_value)
return fragment.resolve(root_value)
Loading