Skip to content

Commit b4e17f0

Browse files
authored
Docs subscriptions and other housekeeping work (#65)
1 parent fce1de3 commit b4e17f0

26 files changed

+687
-542
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ coverage.xml
5656
# Django stuff:
5757
*.log
5858

59+
# Type checking
60+
/.mypy_cache
61+
.pyre
62+
/type_info.json
63+
5964
# Sphinx documentation
6065
docs/_build/
6166

@@ -81,3 +86,6 @@ target/
8186
# Databases
8287
*.sqlite3
8388
.DS_Store
89+
90+
### VisualStudioCode ###
91+
.vscode/*

.travis.yml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@ python:
99
- 3.9-dev
1010
- pypy
1111
- pypy3
12+
matrix:
13+
include:
14+
- python: 3.6
15+
env: TOXENV=flake8
16+
- python: 3.6
17+
env: TOXENV=black
18+
- python: 3.6
19+
env: TOXENV=import-order
20+
- python: 3.6
21+
env: TOXENV=mypy
22+
- python: 3.6
23+
env: TOXENV=manifest
1224
cache: pip
13-
install:
14-
- pip install -e .[dev]
15-
script:
16-
- flake8 gql tests
17-
- pytest --cov-report=term-missing --cov=gql tests
25+
install: pip install tox-travis
26+
script: tox
1827
after_success:
28+
- pip install coveralls
1929
- coveralls
2030
deploy:
2131
provider: pypi

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ virtualenv gql-dev
3131
Activate the virtualenv and install dependencies by running:
3232

3333
```console
34-
python pip install -e ".[test]"
34+
python pip install -e[dev]
3535
```
3636

3737
If you are using Linux or MacOS, you can make use of Makefile command
@@ -50,7 +50,7 @@ Then activate the environment with `conda activate gql-dev`.
5050
Proceed to install all dependencies by running:
5151

5252
```console
53-
python pip install -e ".[test]"
53+
pip install -e.[dev]
5454
```
5555

5656
And you ready to start development!

MANIFEST.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ include Makefile
1010

1111
include tox.ini
1212

13-
recursive-include tests *.py *.yaml
13+
recursive-include tests *.py *.yaml *.graphql
14+
recursive-include tests_py36 *.py
1415

1516
prune gql-checker
1617

gql/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .gql import gql
22
from .client import Client
33

4-
__all__ = ['gql', 'Client']
4+
__all__ = ["gql", "Client"]

gql/client.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22

3-
from graphql import parse, introspection_query, build_ast_schema, build_client_schema
3+
from graphql import build_ast_schema, build_client_schema, introspection_query, parse
44
from graphql.validation import validate
55

66
from .transport.local_schema import LocalSchemaTransport
@@ -10,24 +10,38 @@
1010

1111
class RetryError(Exception):
1212
"""Custom exception thrown when retry logic fails"""
13+
1314
def __init__(self, retries_count, last_exception):
1415
message = "Failed %s retries: %s" % (retries_count, last_exception)
1516
super(RetryError, self).__init__(message)
1617
self.last_exception = last_exception
1718

1819

1920
class Client(object):
20-
def __init__(self, schema=None, introspection=None, type_def=None, transport=None,
21-
fetch_schema_from_transport=False, retries=0):
22-
assert not(type_def and introspection), 'Cant provide introspection type definition at the same time'
21+
def __init__(
22+
self,
23+
schema=None,
24+
introspection=None,
25+
type_def=None,
26+
transport=None,
27+
fetch_schema_from_transport=False,
28+
retries=0,
29+
):
30+
assert not (
31+
type_def and introspection
32+
), "Cant provide introspection type definition at the same time"
2333
if transport and fetch_schema_from_transport:
24-
assert not schema, 'Cant fetch the schema from transport if is already provided'
34+
assert (
35+
not schema
36+
), "Cant fetch the schema from transport if is already provided"
2537
introspection = transport.execute(parse(introspection_query)).data
2638
if introspection:
27-
assert not schema, 'Cant provide introspection and schema at the same time'
39+
assert not schema, "Cant provide introspection and schema at the same time"
2840
schema = build_client_schema(introspection)
2941
elif type_def:
30-
assert not schema, 'Cant provide Type definition and schema at the same time'
42+
assert (
43+
not schema
44+
), "Cant provide Type definition and schema at the same time"
3145
type_def_ast = parse(type_def)
3246
schema = build_ast_schema(type_def_ast)
3347
elif schema and not transport:
@@ -40,7 +54,9 @@ def __init__(self, schema=None, introspection=None, type_def=None, transport=Non
4054

4155
def validate(self, document):
4256
if not self.schema:
43-
raise Exception("Cannot validate locally the document, you need to pass a schema.")
57+
raise Exception(
58+
"Cannot validate locally the document, you need to pass a schema."
59+
)
4460
validation_errors = validate(self.schema, document)
4561
if validation_errors:
4662
raise validation_errors[0]
@@ -67,8 +83,12 @@ def _get_result(self, document, *args, **kwargs):
6783
return result
6884
except Exception as e:
6985
last_exception = e
70-
log.warning("Request failed with exception %s. Retrying for the %s time...",
71-
e, retries_count + 1, exc_info=True)
86+
log.warning(
87+
"Request failed with exception %s. Retrying for the %s time...",
88+
e,
89+
retries_count + 1,
90+
exc_info=True,
91+
)
7292
finally:
7393
retries_count += 1
7494

gql/dsl.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import six
44
from graphql.language import ast
55
from graphql.language.printer import print_ast
6-
from graphql.type import (GraphQLEnumType, GraphQLList, GraphQLNonNull)
6+
from graphql.type import (
7+
GraphQLEnumType,
8+
GraphQLInputObjectField,
9+
GraphQLInputObjectType,
10+
GraphQLList,
11+
GraphQLNonNull,
12+
)
713
from graphql.utils.ast_from_value import ast_from_value
814

915
from .utils import to_camel_case
@@ -30,7 +36,7 @@ def query(self, *args, **kwargs):
3036
return self.execute(query(*args, **kwargs))
3137

3238
def mutate(self, *args, **kwargs):
33-
return self.query(*args, operation='mutation', **kwargs)
39+
return self.query(*args, operation="mutation", **kwargs)
3440

3541
def execute(self, document):
3642
return self.client.execute(document)
@@ -53,7 +59,9 @@ def get_field(self, name):
5359
if camel_cased_name in self.type.fields:
5460
return camel_cased_name, self.type.fields[camel_cased_name]
5561

56-
raise KeyError('Field {} does not exist in type {}.'.format(name, self.type.name))
62+
raise KeyError(
63+
"Field {} does not exist in type {}.".format(name, self.type.name)
64+
)
5765

5866

5967
def selections(*fields):
@@ -62,7 +70,6 @@ def selections(*fields):
6270

6371

6472
class DSLField(object):
65-
6673
def __init__(self, name, field):
6774
self.field = field
6875
self.ast_field = ast.Field(name=ast.Name(value=name), arguments=[])
@@ -87,10 +94,7 @@ def args(self, **kwargs):
8794
arg_type_serializer = get_arg_serializer(arg.type)
8895
serialized_value = arg_type_serializer(value)
8996
self.ast_field.arguments.append(
90-
ast.Argument(
91-
name=ast.Name(value=name),
92-
value=serialized_value
93-
)
97+
ast.Argument(name=ast.Name(value=name), value=serialized_value)
9498
)
9599
return self
96100

@@ -110,26 +114,38 @@ def selection_field(field):
110114

111115

112116
def query(*fields, **kwargs):
113-
if 'operation' not in kwargs:
114-
kwargs['operation'] = 'query'
117+
if "operation" not in kwargs:
118+
kwargs["operation"] = "query"
115119
return ast.Document(
116-
definitions=[ast.OperationDefinition(
117-
operation=kwargs['operation'],
118-
selection_set=ast.SelectionSet(
119-
selections=list(selections(*fields))
120+
definitions=[
121+
ast.OperationDefinition(
122+
operation=kwargs["operation"],
123+
selection_set=ast.SelectionSet(selections=list(selections(*fields))),
120124
)
121-
)]
125+
]
122126
)
123127

124128

125129
def serialize_list(serializer, list_values):
126-
assert isinstance(list_values, Iterable), 'Expected iterable, received "{}"'.format(repr(list_values))
130+
assert isinstance(list_values, Iterable), 'Expected iterable, received "{}"'.format(
131+
repr(list_values)
132+
)
127133
return ast.ListValue(values=[serializer(v) for v in list_values])
128134

129135

130136
def get_arg_serializer(arg_type):
131137
if isinstance(arg_type, GraphQLNonNull):
132138
return get_arg_serializer(arg_type.of_type)
139+
if isinstance(arg_type, GraphQLInputObjectField):
140+
return get_arg_serializer(arg_type.type)
141+
if isinstance(arg_type, GraphQLInputObjectType):
142+
serializers = {k: get_arg_serializer(v) for k, v in arg_type.fields.items()}
143+
return lambda value: ast.ObjectValue(
144+
fields=[
145+
ast.ObjectField(ast.Name(k), serializers[k](v))
146+
for k, v in value.items()
147+
]
148+
)
133149
if isinstance(arg_type, GraphQLList):
134150
inner_serializer = get_arg_serializer(arg_type.of_type)
135151
return partial(serialize_list, inner_serializer)

gql/gql.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
def gql(request_string):
77
if isinstance(request_string, six.string_types):
8-
source = Source(request_string, 'GraphQL request')
8+
source = Source(request_string, "GraphQL request")
99
return parse(source)
1010
else:
1111
raise Exception('Received incompatible request "{}".'.format(request_string))

gql/transport/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ def execute(self, document):
1717
:param document: GraphQL query as AST Node or Document object.
1818
:return: Either ExecutionResult or a Promise that resolves to ExecutionResult object.
1919
"""
20-
raise NotImplementedError("Any Transport subclass must implement execute method")
20+
raise NotImplementedError(
21+
"Any Transport subclass must implement execute method"
22+
)

gql/transport/local_schema.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
from typing import Union, Any
2-
31
from graphql import GraphQLSchema
4-
from graphql.execution import execute, ExecutionResult
2+
from graphql.execution import ExecutionResult, execute
53
from graphql.language.ast import Document
64
from promise import Promise
75

86
from gql.transport import Transport
7+
from typing import Any, Union
98

109

1110
class LocalSchemaTransport(Transport):
1211
"""A transport for executing GraphQL queries against a local schema."""
12+
1313
def __init__(
1414
self, # type: LocalSchemaTransport
15-
schema # type: GraphQLSchema
15+
schema, # type: GraphQLSchema
1616
):
1717
"""Initialize the transport with the given local schema.
1818
@@ -29,9 +29,4 @@ def execute(self, document, *args, **kwargs):
2929
:param kwargs: Keyword options passed to execute method from graphql-core library.
3030
:return: Either ExecutionResult or a Promise that resolves to ExecutionResult object.
3131
"""
32-
return execute(
33-
self.schema,
34-
document,
35-
*args,
36-
**kwargs
37-
)
32+
return execute(self.schema, document, *args, **kwargs)

0 commit comments

Comments
 (0)