Skip to content

Commit 54df11f

Browse files
committed
Added initial version of static typing
1 parent 4d2f7e7 commit 54df11f

File tree

124 files changed

+8027
-4532
lines changed

Some content is hidden

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

124 files changed

+8027
-4532
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ target/
6767

6868
# OS X
6969
.DS_Store
70+
/.mypy_cache
71+
.pyre
72+
/.vscode
73+
/type_info.json

conftest.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Configuration for pytest to automatically collect types.
2+
# Thanks to Guilherme Salgado.
3+
import pytest
4+
5+
try:
6+
import pyannotate_runtime
7+
PYANOTATE_PRESENT = True
8+
except ImportError:
9+
PYANOTATE_PRESENT = False
10+
11+
if PYANOTATE_PRESENT:
12+
def pytest_collection_finish(session):
13+
"""Handle the pytest collection finish hook: configure pyannotate.
14+
Explicitly delay importing `collect_types` until all tests have
15+
been collected. This gives gevent a chance to monkey patch the
16+
world before importing pyannotate.
17+
"""
18+
from pyannotate_runtime import collect_types
19+
collect_types.init_types_collection()
20+
21+
@pytest.fixture(autouse=True)
22+
def collect_types_fixture():
23+
from pyannotate_runtime import collect_types
24+
collect_types.resume()
25+
yield
26+
collect_types.pause()
27+
28+
def pytest_sessionfinish(session, exitstatus):
29+
from pyannotate_runtime import collect_types
30+
collect_types.dump_stats("type_info.json")

graphql/backend/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99
from .decider import GraphQLDeciderBackend
1010
from .cache import GraphQLCachedBackend
1111

12+
if False:
13+
from typing import Union
14+
1215
_default_backend = None
1316

1417

1518
def get_default_backend():
19+
# type: () -> GraphQLCoreBackend
1620
global _default_backend
1721
if _default_backend is None:
1822
_default_backend = GraphQLCoreBackend()
1923
return _default_backend
2024

2125

2226
def set_default_backend(backend):
27+
# type: (GraphQLCoreBackend) -> None
2328
global _default_backend
2429
assert isinstance(
2530
backend, GraphQLBackend

graphql/backend/base.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from abc import ABCMeta, abstractmethod
55
import six
66

7+
if False:
8+
from typing import Dict, Optional, Union, Callable
9+
from ..language.ast import Document
10+
from ..type.schema import GraphQLSchema
11+
712

813
class GraphQLBackend(six.with_metaclass(ABCMeta)):
914
@abstractmethod
@@ -15,35 +20,38 @@ def document_from_string(self, schema, request_string):
1520

1621
class GraphQLDocument(object):
1722
def __init__(self, schema, document_string, document_ast, execute):
23+
# type: (GraphQLSchema, str, Document, Callable) -> None
1824
self.schema = schema
1925
self.document_string = document_string
2026
self.document_ast = document_ast
2127
self.execute = execute
2228

2329
@cached_property
2430
def operations_map(self):
25-
'''
31+
# type: () -> Dict[Union[str, None], str]
32+
"""
2633
returns a Mapping of operation names and it's associated types.
2734
E.g. {'myQuery': 'query', 'myMutation': 'mutation'}
28-
'''
35+
"""
2936
document_ast = self.document_ast
30-
operations = {}
37+
operations = {} # type: Dict[Union[str, None], str]
3138
for definition in document_ast.definitions:
3239
if isinstance(definition, ast.OperationDefinition):
3340
if definition.name:
34-
operation_name = definition.name.value
41+
operations[definition.name.value] = definition.operation
3542
else:
36-
operation_name = None
37-
operations[operation_name] = definition.operation
43+
operations[None] = definition.operation
44+
3845
return operations
3946

4047
def get_operation_type(self, operation_name):
41-
'''
48+
# type: (Optional[str]) -> Optional[str]
49+
"""
4250
Returns the operation type ('query', 'mutation', 'subscription' or None)
4351
for the given operation_name.
4452
If no operation_name is provided (and only one operation exists) it will return the
4553
operation type for that operation
46-
'''
54+
"""
4755
operations_map = self.operations_map
4856
if not operation_name and len(operations_map) == 1:
4957
return next(iter(operations_map.values()))

graphql/backend/cache.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
from .base import GraphQLBackend
66

7-
_cached_schemas = {}
7+
if False:
8+
from typing import Any, Dict, Optional, Union, Tuple, Hashable
9+
from .base import GraphQLDocument
810

9-
_cached_queries = {}
11+
_cached_schemas = {} # type: Dict[GraphQLSchema, str]
12+
13+
_cached_queries = {} # type: Dict[str, str]
1014

1115

1216
def get_unique_schema_id(schema):
17+
# type: (GraphQLSchema) -> str
1318
"""Get a unique id given a GraphQLSchema"""
1419
assert isinstance(schema, GraphQLSchema), (
1520
"Must receive a GraphQLSchema as schema. Received {}"
@@ -21,6 +26,7 @@ def get_unique_schema_id(schema):
2126

2227

2328
def get_unique_document_id(query_str):
29+
# type: (str) -> str
2430
"""Get a unique id given a query_string"""
2531
assert isinstance(query_str, string_types), (
2632
"Must receive a string as query_str. Received {}"
@@ -32,7 +38,13 @@ def get_unique_document_id(query_str):
3238

3339

3440
class GraphQLCachedBackend(GraphQLBackend):
35-
def __init__(self, backend, cache_map=None, use_consistent_hash=False):
41+
def __init__(
42+
self,
43+
backend, # type: GraphQLBackend
44+
cache_map=None, # type: Optional[Dict[Hashable, GraphQLDocument]]
45+
use_consistent_hash=False, # type: bool
46+
):
47+
# type: (...) -> None
3648
assert isinstance(
3749
backend, GraphQLBackend
3850
), "Provided backend must be an instance of GraphQLBackend"
@@ -43,14 +55,16 @@ def __init__(self, backend, cache_map=None, use_consistent_hash=False):
4355
self.use_consistent_hash = use_consistent_hash
4456

4557
def get_key_for_schema_and_document_string(self, schema, request_string):
58+
# type: (GraphQLSchema, str) -> int
4659
"""This method returns a unique key given a schema and a request_string"""
4760
if self.use_consistent_hash:
4861
schema_id = get_unique_schema_id(schema)
4962
document_id = get_unique_document_id(request_string)
50-
return (schema_id, document_id)
63+
return hash((schema_id, document_id))
5164
return hash((schema, request_string))
5265

5366
def document_from_string(self, schema, request_string):
67+
# type: (GraphQLSchema, str) -> Optional[GraphQLDocument]
5468
"""This method returns a GraphQLQuery (from cache if present)"""
5569
key = self.get_key_for_schema_and_document_string(schema, request_string)
5670
if key not in self.cache_map:

graphql/backend/compiled.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,52 @@
11
from six import string_types
22
from .base import GraphQLDocument
33

4+
if False:
5+
from ..type.schema import GraphQLSchema
6+
from typing import Any, Optional, Dict, Callable, Union
7+
48

59
class GraphQLCompiledDocument(GraphQLDocument):
610
@classmethod
7-
def from_code(cls, schema, code, uptodate=None, extra_namespace=None):
11+
def from_code(
12+
cls,
13+
schema, # type: GraphQLSchema
14+
code, # type: Union[str, Any]
15+
uptodate=None, # type: Optional[bool]
16+
extra_namespace=None, # type: Optional[Dict[str, Any]]
17+
):
18+
# type: (...) -> GraphQLCompiledDocument
819
"""Creates a GraphQLDocument object from compiled code and the globals. This
920
is used by the loaders and schema to create a document object.
1021
"""
1122
if isinstance(code, string_types):
12-
filename = '<document>'
13-
code = compile(code, filename, 'exec')
23+
filename = "<document>"
24+
code = compile(code, filename, "exec")
1425
namespace = {"__file__": code.co_filename}
1526
exec(code, namespace)
1627
if extra_namespace:
1728
namespace.update(extra_namespace)
1829
rv = cls._from_namespace(schema, namespace)
19-
rv._uptodate = uptodate
30+
# rv._uptodate = uptodate
2031
return rv
2132

2233
@classmethod
2334
def from_module_dict(cls, schema, module_dict):
35+
# type: (GraphQLSchema, Dict[str, Any]) -> GraphQLCompiledDocument
2436
"""Creates a template object from a module. This is used by the
2537
module loader to create a document object.
2638
"""
2739
return cls._from_namespace(schema, module_dict)
2840

2941
@classmethod
3042
def _from_namespace(cls, schema, namespace):
31-
document_string = namespace.get("document_string", "")
32-
document_ast = namespace.get("document_ast")
33-
execute = namespace["execute"]
43+
# type: (GraphQLSchema, Dict[str, Any]) -> GraphQLCompiledDocument
44+
document_string = namespace.get("document_string", "") # type: str
45+
document_ast = namespace.get("document_ast") # type: ignore
46+
execute = namespace["execute"] # type: Callable
3447

3548
namespace["schema"] = schema
36-
return cls(
49+
return cls( # type: ignore
3750
schema=schema,
3851
document_string=document_string,
3952
document_ast=document_ast,

graphql/backend/core.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,51 @@
88

99
from .base import GraphQLBackend, GraphQLDocument
1010

11-
12-
def execute_and_validate(schema, document_ast, *args, **kwargs):
13-
do_validation = kwargs.get('validate', True)
11+
if False:
12+
from typing import Any, Optional, Union
13+
from .base import GraphQLDocument
14+
from ..language.ast import Document
15+
from ..type.schema import GraphQLSchema
16+
from ..execution.base import ExecutionResult
17+
from rx.core.anonymousobservable import AnonymousObservable
18+
19+
20+
def execute_and_validate(
21+
schema, # type: GraphQLSchema
22+
document_ast, # type: Document
23+
*args, # type: Any
24+
**kwargs # type: Any
25+
):
26+
# type: (...) -> Union[ExecutionResult, AnonymousObservable]
27+
do_validation = kwargs.get("validate", True)
1428
if do_validation:
1529
validation_errors = validate(schema, document_ast)
1630
if validation_errors:
17-
return ExecutionResult(
18-
errors=validation_errors,
19-
invalid=True,
20-
)
31+
return ExecutionResult(errors=validation_errors, invalid=True)
2132

2233
return execute(schema, document_ast, *args, **kwargs)
2334

2435

2536
class GraphQLCoreBackend(GraphQLBackend):
26-
def __init__(self, executor=None, **kwargs):
27-
super(GraphQLCoreBackend, self).__init__(**kwargs)
37+
def __init__(self, executor=None):
38+
# type: (Optional[Any]) -> None
2839
self.execute_params = {"executor": executor}
2940

3041
def document_from_string(self, schema, document_string):
42+
# type: (GraphQLSchema, Union[Document, str]) -> GraphQLDocument
3143
if isinstance(document_string, ast.Document):
3244
document_ast = document_string
3345
document_string = print_ast(document_ast)
3446
else:
35-
assert isinstance(document_string, string_types), "The query must be a string"
47+
assert isinstance(
48+
document_string, string_types
49+
), "The query must be a string"
3650
document_ast = parse(document_string)
3751
return GraphQLDocument(
3852
schema=schema,
3953
document_string=document_string,
4054
document_ast=document_ast,
41-
execute=partial(execute_and_validate, schema, document_ast, **self.execute_params),
55+
execute=partial(
56+
execute_and_validate, schema, document_ast, **self.execute_params
57+
),
4258
)

graphql/backend/decider.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
from .base import GraphQLBackend
1+
from .base import GraphQLBackend, GraphQLDocument
2+
3+
if False:
4+
from typing import List, Union, Any, Optional
5+
from ..type.schema import GraphQLSchema
26

37

48
class GraphQLDeciderBackend(GraphQLBackend):
5-
def __init__(self, backends=None):
9+
def __init__(self, backends):
10+
# type: (List[GraphQLBackend], ) -> None
611
if not backends:
712
raise Exception("Need to provide backends to decide into.")
813
if not isinstance(backends, (list, tuple)):
@@ -11,6 +16,7 @@ def __init__(self, backends=None):
1116
super(GraphQLDeciderBackend, self).__init__()
1217

1318
def document_from_string(self, schema, request_string):
19+
# type: (GraphQLSchema, str) -> GraphQLDocument
1420
for backend in self.backends:
1521
try:
1622
return backend.document_from_string(schema, request_string)

graphql/backend/tests/test_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33

44

55
def test_get_default_backend_returns_core_by_default():
6+
# type: () -> None
67
backend = get_default_backend()
78
assert isinstance(backend, GraphQLCoreBackend)
89

910

1011
def test_set_default_backend():
12+
# type: () -> None
1113
default_backend = get_default_backend()
1214
new_backend = GraphQLCoreBackend()
1315
assert new_backend != default_backend
@@ -16,6 +18,7 @@ def test_set_default_backend():
1618

1719

1820
def test_set_default_backend_fails_if_invalid_backend():
21+
# type: () -> None
1922
default_backend = get_default_backend()
2023
with pytest.raises(Exception) as exc_info:
2124
set_default_backend(object())

graphql/backend/tests/test_cache.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111

1212

1313
def test_cached_backend():
14+
# type: () -> None
1415
cached_backend = GraphQLCachedBackend(GraphQLCoreBackend())
1516
document1 = cached_backend.document_from_string(schema, "{ hello }")
1617
document2 = cached_backend.document_from_string(schema, "{ hello }")
1718
assert document1 == document2
1819

1920

2021
def test_cached_backend_with_use_consistent_hash():
22+
# type: () -> None
2123
cached_backend = GraphQLCachedBackend(GraphQLCoreBackend(), use_consistent_hash=True)
2224
document1 = cached_backend.document_from_string(schema, "{ hello }")
2325
document2 = cached_backend.document_from_string(schema, "{ hello }")

0 commit comments

Comments
 (0)