Skip to content

Commit 3500817

Browse files
authored
Merge branch 'master' into move-tests-require-to-extras-require
2 parents 238e100 + 7b275a4 commit 3500817

File tree

82 files changed

+3052
-1158
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+3052
-1158
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Byte-compiled / optimized / DLL files
55
__pycache__/
66
*.py[cod]
7+
venv
78

89
# C extensions
910
*.so
@@ -61,6 +62,7 @@ target/
6162

6263
# IntelliJ
6364
.idea
65+
*.iml
6466

6567
# OS X
6668
.DS_Store

.travis.yml

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,43 @@ python:
66
- 3.4
77
- 3.5
88
- pypy
9-
cache: pip
109
before_install:
11-
- |
12-
if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
13-
export PYENV_ROOT="$HOME/.pyenv"
14-
if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
15-
cd "$PYENV_ROOT" && git pull
16-
else
17-
rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
18-
fi
19-
export PYPY_VERSION="4.0.1"
20-
"$PYENV_ROOT/bin/pyenv" install "pypy-$PYPY_VERSION"
21-
virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
22-
source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
23-
fi
10+
- |
11+
if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
12+
export PYENV_ROOT="$HOME/.pyenv"
13+
if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
14+
cd "$PYENV_ROOT" && git pull
15+
else
16+
rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
17+
fi
18+
export PYPY_VERSION="4.0.1"
19+
"$PYENV_ROOT/bin/pyenv" install "pypy-$PYPY_VERSION"
20+
virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
21+
source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
22+
fi
2423
install:
25-
- pip install --cache-dir $HOME/.cache/pip pytest-cov pytest-mock coveralls flake8 isort==3.9.6 gevent==1.1b5 six>=1.10.0 promise>=0.4.2
26-
- pip install --cache-dir $HOME/.cache/pip pytest>=2.7.3 --upgrade
24+
- pip install pytest-cov pytest-mock coveralls flake8 gevent==1.1b5 six>=1.10.0 promise>=0.4.2
25+
pytest-benchmark
26+
- pip install pytest==2.9.2
2727
- pip install -e .
2828
script:
29-
- flake8
3029
- py.test --cov=graphql graphql tests
3130
after_success:
3231
- coveralls
3332
matrix:
3433
include:
35-
- python: "3.5"
36-
after_install:
37-
- pip install pytest-asyncio
38-
script:
39-
- flake8
40-
- isort --check-only graphql/ -rc
41-
- py.test --cov=graphql graphql tests tests_py35
34+
- python: '3.5'
35+
after_install:
36+
- pip install pytest-asyncio
37+
script:
38+
- py.test --cov=graphql graphql tests tests_py35
39+
- python: '2.7'
40+
script:
41+
- flake8
42+
deploy:
43+
provider: pypi
44+
user: syrusakbary
45+
on:
46+
tags: true
47+
password:
48+
secure: q7kMxnJQ5LWr8fxVbQPm3pAXKRfYa1d2defM1UXKTQ+Gi6ZQ+QEOAOSbX1SKzYH62+hNRY2JGTeLkTQBeEYn05GJRh+WOkFzIFV1EnsgFbimSb6B83EmM57099GjJnO2nRUU4jyuNGU1joTeaD/g08ede072Es1I7DTuholNbYIq+brL/LQMJycuqZMoWUW4+pP8dE9SmjThMNYHlqNhzdXSE3BlZU0xcw7F2Ea384DNcekIIcapZuPjL167VouuSH/oMQMxBJo+ExEHdbqn5zsA9xcoF931XCgz4ag8U3jHhE48ZXM/xwdQt+S8JnOZcuv3MoAAioMbh+bYXUt2lmENWXCKK1kMDz2bJymwEUeZLA6lFxJQwvlVShowdi7xeyDYLIbeF7yG90Hd+5BqCZn5imzlcQxpjanaQq6xLwAzo6AHssWtd5bBOjDydknPxd1t3QGDoDvtfRdqrfOhlVX5813Hmd/vAopBAba7msKPMLxhsqDZKkwsVrLJLJDjGdpHNl/bbVaMsYcPrsFxa2W8PuddQFviHbL4HDNqHn5SpRwJcQ18YL1X5StQnUz1J+4E0W4mLrU3YW1k8RGlKTes/GeTH4sU+Sh3I9vrDv7849A8U9sSFyB2PT4Jyy8O2R5UyjoqnZDrkYYbLdn/caVo3ThrubTpwdPBmNwcDLA=

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
GraphQL for Python.
44

5-
*This library is a port of [graphql-js](https://github.com/graphql/graphql-js) to Python.*
5+
*This library is a port of [graphql-js](https://github.com/graphql/graphql-js) to Python and currently is up-to-date with release [0.6.0](https://github.com/graphql/graphql-js/releases/tag/v0.6.0).*
66

77

88
[![PyPI version](https://badge.fury.io/py/graphql-core.svg)](https://badge.fury.io/py/graphql-core)
@@ -20,7 +20,8 @@ For questions, ask [Stack Overflow](http://stackoverflow.com/questions/tagged/gr
2020
An overview of the GraphQL language is available in the
2121
[README](https://github.com/facebook/graphql/blob/master/README.md) for the
2222
[Specification for GraphQL](https://github.com/facebook/graphql).
23-
The overview describes a simple set of GraphQL examples that exist as [tests](tests/starwars)
23+
24+
The overview describes a simple set of GraphQL examples that exist as [tests](https://github.com/graphql-python/graphql-core/tree/master/tests/)
2425
in this repository. A good way to get started is to walk through that README and the corresponding tests
2526
in parallel.
2627

@@ -62,7 +63,7 @@ schema = GraphQLSchema(
6263
This defines a simple schema with one type and one field, that resolves
6364
to a fixed value. The `resolve` function can return a value, a promise,
6465
or an array of promises. A more complex example is included in the top
65-
level [tests](tests) directory.
66+
level [tests](https://github.com/graphql-python/graphql-core/tree/master/tests/) directory.
6667

6768
Then, serve the result of a query against that type schema.
6869

graphql/__init__.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
__GRAPHQL_SETUP__ = False
3434

3535

36-
VERSION = (0, 5, 3, 'final', 0)
36+
VERSION = (1, 0, 1, 'final', 0)
3737

3838
__version__ = get_version(VERSION)
3939

@@ -58,15 +58,49 @@
5858
GraphQLList,
5959
GraphQLNonNull,
6060
GraphQLField,
61+
GraphQLInputObjectField,
6162
GraphQLArgument,
6263

64+
# "Enum" of Type Kinds
65+
TypeKind,
66+
67+
# "Enum" of Directive locations
68+
DirectiveLocation,
69+
6370
# Scalars
6471
GraphQLInt,
6572
GraphQLFloat,
6673
GraphQLString,
6774
GraphQLBoolean,
6875
GraphQLID,
6976

77+
# Directive definition
78+
GraphQLDirective,
79+
80+
# Built-in directives defined by the Spec
81+
specified_directives,
82+
GraphQLSkipDirective,
83+
GraphQLIncludeDirective,
84+
GraphQLDeprecatedDirective,
85+
86+
# Constant Deprecation Reason
87+
DEFAULT_DEPRECATION_REASON,
88+
89+
# GraphQL Types for introspection.
90+
__Schema,
91+
__Directive,
92+
__DirectiveLocation,
93+
__Type,
94+
__Field,
95+
__InputValue,
96+
__EnumValue,
97+
__TypeKind,
98+
99+
# Meta-field definitions.
100+
SchemaMetaFieldDef,
101+
TypeMetaFieldDef,
102+
TypeNameMetaFieldDef,
103+
70104
# Predicates
71105
is_type,
72106
is_input_type,
@@ -102,6 +136,8 @@
102136
# Execute GraphQL queries.
103137
from .execution import ( # no import order
104138
execute,
139+
MiddlewareManager,
140+
middlewares
105141
)
106142

107143
# Validate GraphQL queries.
@@ -180,12 +216,32 @@
180216
'GraphQLList',
181217
'GraphQLNonNull',
182218
'GraphQLField',
219+
'GraphQLInputObjectField',
183220
'GraphQLArgument',
184221
'GraphQLObjectType',
185222
'GraphQLScalarType',
186223
'GraphQLSchema',
187224
'GraphQLString',
188225
'GraphQLUnionType',
226+
'GraphQLDirective',
227+
'specified_directives',
228+
'GraphQLSkipDirective',
229+
'GraphQLIncludeDirective',
230+
'GraphQLDeprecatedDirective',
231+
'DEFAULT_DEPRECATION_REASON',
232+
'TypeKind',
233+
'DirectiveLocation',
234+
'__Schema',
235+
'__Directive',
236+
'__DirectiveLocation',
237+
'__Type',
238+
'__Field',
239+
'__InputValue',
240+
'__EnumValue',
241+
'__TypeKind',
242+
'SchemaMetaFieldDef',
243+
'TypeMetaFieldDef',
244+
'TypeNameMetaFieldDef',
189245
'get_named_type',
190246
'get_nullable_type',
191247
'is_abstract_type',
@@ -204,6 +260,8 @@
204260
'print_ast',
205261
'visit',
206262
'execute',
263+
'MiddlewareManager',
264+
'middlewares',
207265
'specified_rules',
208266
'validate',
209267
'GraphQLError',

graphql/error/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import six
12
from ..language.location import get_location
23

34

@@ -29,6 +30,12 @@ def positions(self):
2930
if any(node_positions):
3031
return node_positions
3132

33+
def reraise(self):
34+
if self.stack:
35+
six.reraise(type(self), self, self.stack)
36+
else:
37+
raise self
38+
3239
@property
3340
def locations(self):
3441
source = self.source

graphql/error/located_error.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ class GraphQLLocatedError(GraphQLError):
99

1010
def __init__(self, nodes, original_error=None):
1111
if original_error:
12-
message = str(original_error)
12+
try:
13+
message = str(original_error)
14+
except UnicodeEncodeError:
15+
message = original_error.message.encode('utf-8')
1316
else:
1417
message = 'An unknown error occurred.'
1518

graphql/error/tests/__init__.py

Whitespace-only changes.

graphql/error/tests/test_base.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import pytest
2+
import traceback
3+
4+
from graphql.execution import execute
5+
from graphql.language.parser import parse
6+
from graphql.type import (GraphQLField, GraphQLObjectType, GraphQLSchema,
7+
GraphQLString)
8+
9+
10+
def test_raise():
11+
ast = parse('query Example { a }')
12+
13+
def resolver(context, *_):
14+
raise Exception('Failed')
15+
16+
Type = GraphQLObjectType('Type', {
17+
'a': GraphQLField(GraphQLString, resolver=resolver),
18+
})
19+
20+
result = execute(GraphQLSchema(Type), ast)
21+
assert str(result.errors[0]) == 'Failed'
22+
23+
24+
def test_reraise():
25+
ast = parse('query Example { a }')
26+
27+
def resolver(context, *_):
28+
raise Exception('Failed')
29+
30+
Type = GraphQLObjectType('Type', {
31+
'a': GraphQLField(GraphQLString, resolver=resolver),
32+
})
33+
34+
result = execute(GraphQLSchema(Type), ast)
35+
with pytest.raises(Exception) as exc_info:
36+
result.errors[0].reraise()
37+
38+
extracted = traceback.extract_tb(exc_info.tb)
39+
formatted_tb = [row[2:] for row in extracted]
40+
if formatted_tb[2][0] == 'reraise':
41+
formatted_tb[2:] = formatted_tb[3:]
42+
43+
assert formatted_tb == [
44+
('test_reraise', 'result.errors[0].reraise()'),
45+
('reraise', 'six.reraise(type(self), self, self.stack)'),
46+
# ('reraise', 'raise value.with_traceback(tb)'),
47+
('resolve_or_error', 'return executor.execute(resolve_fn, source, args, context, info)'),
48+
('execute', 'return fn(*args, **kwargs)'), ('resolver', "raise Exception('Failed')")
49+
]
50+
assert str(exc_info.value) == 'Failed'

graphql/execution/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121
from .executor import execute
2222
from .base import ExecutionResult
23+
from .middleware import middlewares, MiddlewareManager
2324

2425

25-
__all__ = ['execute', 'ExecutionResult']
26+
__all__ = ['execute', 'ExecutionResult', 'MiddlewareManager', 'middlewares']

graphql/execution/base.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from ..error import GraphQLError
33
from ..language import ast
4+
from ..pyutils.default_ordered_dict import DefaultOrderedDict
45
from ..type.definition import GraphQLInterfaceType, GraphQLUnionType
56
from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
67
from ..type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef,
@@ -18,9 +19,9 @@ class ExecutionContext(object):
1819
and the fragments defined in the query document"""
1920

2021
__slots__ = 'schema', 'fragments', 'root_value', 'operation', 'variable_values', 'errors', 'context_value', \
21-
'argument_values_cache', 'executor'
22+
'argument_values_cache', 'executor', 'middleware', '_subfields_cache'
2223

23-
def __init__(self, schema, document_ast, root_value, context_value, variable_values, operation_name, executor):
24+
def __init__(self, schema, document_ast, root_value, context_value, variable_values, operation_name, executor, middleware):
2425
"""Constructs a ExecutionContext object from the arguments passed
2526
to execute, which we will pass throughout the other execution
2627
methods."""
@@ -63,6 +64,13 @@ def __init__(self, schema, document_ast, root_value, context_value, variable_val
6364
self.context_value = context_value
6465
self.argument_values_cache = {}
6566
self.executor = executor
67+
self.middleware = middleware
68+
self._subfields_cache = {}
69+
70+
def get_field_resolver(self, field_resolver):
71+
if not self.middleware:
72+
return field_resolver
73+
return self.middleware.get_field_resolver(field_resolver)
6674

6775
def get_argument_values(self, field_def, field_ast):
6876
k = field_def, field_ast
@@ -74,6 +82,21 @@ def get_argument_values(self, field_def, field_ast):
7482

7583
return result
7684

85+
def get_sub_fields(self, return_type, field_asts):
86+
k = return_type, tuple(field_asts)
87+
if k not in self._subfields_cache:
88+
subfield_asts = DefaultOrderedDict(list)
89+
visited_fragment_names = set()
90+
for field_ast in field_asts:
91+
selection_set = field_ast.selection_set
92+
if selection_set:
93+
subfield_asts = collect_fields(
94+
self, return_type, selection_set,
95+
subfield_asts, visited_fragment_names
96+
)
97+
self._subfields_cache[k] = subfield_asts
98+
return self._subfields_cache[k]
99+
77100

78101
class ExecutionResult(object):
79102
"""The result of execution. `data` is the result of executing the
@@ -245,6 +268,8 @@ def get_field_entry_key(node):
245268

246269

247270
class ResolveInfo(object):
271+
__slots__ = ('field_name', 'field_asts', 'return_type', 'parent_type',
272+
'schema', 'fragments', 'root_value', 'operation', 'variable_values')
248273

249274
def __init__(self, field_name, field_asts, return_type, parent_type,
250275
schema, fragments, root_value, operation, variable_values):
@@ -277,10 +302,10 @@ def get_field_def(schema, parent_type, field_name):
277302
are allowed, like on a Union. __schema could get automatically
278303
added to the query type, but that would require mutating type
279304
definitions, which would cause issues."""
280-
if field_name == SchemaMetaFieldDef.name and schema.get_query_type() == parent_type:
305+
if field_name == '__schema' and schema.get_query_type() == parent_type:
281306
return SchemaMetaFieldDef
282-
elif field_name == TypeMetaFieldDef.name and schema.get_query_type() == parent_type:
307+
elif field_name == '__type' and schema.get_query_type() == parent_type:
283308
return TypeMetaFieldDef
284-
elif field_name == TypeNameMetaFieldDef.name:
309+
elif field_name == '__typename':
285310
return TypeNameMetaFieldDef
286-
return parent_type.get_fields().get(field_name)
311+
return parent_type.fields.get(field_name)

0 commit comments

Comments
 (0)