graphql-codegen 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphql_codegen/__init__.py +7 -0
- graphql_codegen/__main__.py +5 -0
- graphql_codegen/_cli/__init__.py +41 -0
- graphql_codegen/_cli/_graphql_config.py +291 -0
- graphql_codegen/_cli/_introspection.py +87 -0
- graphql_codegen/_cli/_introspection_graphql.py +53 -0
- graphql_codegen/_cli/_parsing.py +67 -0
- graphql_codegen/_cli/_schema_pointer.py +108 -0
- graphql_codegen/_cli/_source.py +68 -0
- graphql_codegen/_cli/_write.py +22 -0
- graphql_codegen/_generator/__init__.py +0 -0
- graphql_codegen/_generator/_annotation.py +149 -0
- graphql_codegen/_generator/_ast_nodes.py +235 -0
- graphql_codegen/_generator/_data_type.py +532 -0
- graphql_codegen/_generator/_document.py +108 -0
- graphql_codegen/_generator/_document_module.py +54 -0
- graphql_codegen/_generator/_imports.py +166 -0
- graphql_codegen/_generator/_injector.py +267 -0
- graphql_codegen/_generator/_merge.py +149 -0
- graphql_codegen/_generator/_naming.py +80 -0
- graphql_codegen/_generator/_operation.py +223 -0
- graphql_codegen/_generator/_scalar.py +76 -0
- graphql_codegen/_generator/_schema.py +46 -0
- graphql_codegen/_generator/_schema_type.py +334 -0
- graphql_codegen/_generator/_selection.py +246 -0
- graphql_codegen/_generator/_structs.py +87 -0
- graphql_codegen/_generator/_typed_dict.py +154 -0
- graphql_codegen/_generator/dotted_name.py +48 -0
- graphql_codegen/_generator/package.py +695 -0
- graphql_codegen/_generator/spelling.py +168 -0
- graphql_codegen/_metadata.py +11 -0
- graphql_codegen/_note.py +12 -0
- graphql_codegen/config.py +42 -0
- graphql_codegen/document_sibling_module.py +58 -0
- graphql_codegen/generate.py +19 -0
- graphql_codegen/package_location.py +41 -0
- graphql_codegen/py.typed +0 -0
- graphql_codegen/runtime/__init__.py +20 -0
- graphql_codegen/runtime/_compat.py +27 -0
- graphql_codegen/runtime/_literal.py +18 -0
- graphql_codegen/runtime/_merge.py +153 -0
- graphql_codegen/runtime/_prepare.py +250 -0
- graphql_codegen/runtime/_reflection.py +376 -0
- graphql_codegen/runtime/_sigil.py +11 -0
- graphql_codegen/runtime/_transport.py +21 -0
- graphql_codegen/runtime/client.py +384 -0
- graphql_codegen/runtime/error.py +135 -0
- graphql_codegen/runtime/injection.py +107 -0
- graphql_codegen/runtime/operation.py +135 -0
- graphql_codegen/scalar.py +59 -0
- graphql_codegen-0.1.0.dist-info/METADATA +871 -0
- graphql_codegen-0.1.0.dist-info/RECORD +55 -0
- graphql_codegen-0.1.0.dist-info/WHEEL +4 -0
- graphql_codegen-0.1.0.dist-info/entry_points.txt +3 -0
- graphql_codegen-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Copied into each generated package, where this line says not to edit it.
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from json import dumps
|
|
6
|
+
from types import MappingProxyType
|
|
7
|
+
from typing import Any, Final, Generic, TypeVar, final, override
|
|
8
|
+
|
|
9
|
+
from ._literal import ExecutionErrorHandling, OperationType
|
|
10
|
+
from ._reflection import Convert, build_parser, build_serializer
|
|
11
|
+
from ._sigil import SIGIL
|
|
12
|
+
from .error import ExecutionError
|
|
13
|
+
|
|
14
|
+
_VARIABLES_KEY: Final = ',"variables":'
|
|
15
|
+
_NO_INJECTIONS: Final[Mapping[str, frozenset[tuple[str, ...]]]] = MappingProxyType(
|
|
16
|
+
dict[str, frozenset[tuple[str, ...]]]()
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Declared, since PEP 695 infers variance from every method.
|
|
20
|
+
# Covariant, so that an operation of one type, or its request, is also one of any type.
|
|
21
|
+
_Type_co = TypeVar("_Type_co", bound=OperationType, covariant=True)
|
|
22
|
+
# Both held by `variables_type` and taken by `__call__`.
|
|
23
|
+
_Variables = TypeVar("_Variables", bound=Mapping[str, object])
|
|
24
|
+
_Data_co = TypeVar("_Data_co", covariant=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@final
|
|
28
|
+
class Operation(Generic[_Type_co, _Variables, _Data_co]):
|
|
29
|
+
"""A generated operation, parameterized by its type, what it sends, and what it returns, in the order things happen.
|
|
30
|
+
|
|
31
|
+
Its type is a parameter, so that clients tell operation types apart statically: a subscription client takes subscriptions only, and a merge takes operations of one type.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
operation_type: _Type_co,
|
|
38
|
+
name: str,
|
|
39
|
+
document: str,
|
|
40
|
+
variables_type: type[_Variables],
|
|
41
|
+
injections: Mapping[str, frozenset[tuple[str, ...]]] = _NO_INJECTIONS,
|
|
42
|
+
data_type: type[_Data_co],
|
|
43
|
+
) -> None:
|
|
44
|
+
"""*document* has the merge sigil wherever its index in a merge goes, and *injections* the path of every object receiving each injector's value, by injector name, `()` being the variables themselves.
|
|
45
|
+
|
|
46
|
+
The key an injection writes is the injector's name, which the variables' type lacks, so that callers cannot pass it.
|
|
47
|
+
What crosses the wire is read off *variables_type* and *data_type* at runtime: they are the only description of it.
|
|
48
|
+
"""
|
|
49
|
+
self._operation_type: Final = operation_type
|
|
50
|
+
self._name: Final = name
|
|
51
|
+
self._document: Final = document
|
|
52
|
+
self._variables_type: Final = variables_type
|
|
53
|
+
self._injections: Final = injections
|
|
54
|
+
self._data_type: Final = data_type
|
|
55
|
+
|
|
56
|
+
def __call__(self, variables: _Variables, /) -> "Request[_Type_co, _Data_co]":
|
|
57
|
+
"""Return the request running this operation with *variables*, checked here, before anything runs.
|
|
58
|
+
|
|
59
|
+
Their type is already bound here, so every type checker checks them exactly, whereas from a function taking both an operation and its variables, some type checkers would infer a wider type.
|
|
60
|
+
"""
|
|
61
|
+
return Request(operation=self, variables=variables, on_execution_error="raise")
|
|
62
|
+
|
|
63
|
+
@override
|
|
64
|
+
def __repr__(self) -> str:
|
|
65
|
+
# The operation, rather than its document.
|
|
66
|
+
return (
|
|
67
|
+
f"Operation(operation_type={self._operation_type!r}, name={self._name!r})"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@cached_property
|
|
71
|
+
def _data_parser(self) -> Convert | None:
|
|
72
|
+
"""Only custom scalars with a codec and `@nonNull` fields cost anything at runtime."""
|
|
73
|
+
return build_parser(self._data_type, partial=False)
|
|
74
|
+
|
|
75
|
+
@cached_property
|
|
76
|
+
def _partial_data_parser(self) -> Convert | None:
|
|
77
|
+
return build_parser(self._data_type, partial=True)
|
|
78
|
+
|
|
79
|
+
@cached_property
|
|
80
|
+
def _variables_serializer(self) -> Convert | None:
|
|
81
|
+
return build_serializer(self._variables_type)
|
|
82
|
+
|
|
83
|
+
@cached_property
|
|
84
|
+
def _request_prefix(self) -> bytes:
|
|
85
|
+
"""Serialized once, since the document's string escaping costs as much as the document is long."""
|
|
86
|
+
envelope = dumps(
|
|
87
|
+
{"operationName": self._name, "query": self._document.replace(SIGIL, "")}
|
|
88
|
+
)
|
|
89
|
+
return (envelope[:-1] + _VARIABLES_KEY).encode()
|
|
90
|
+
|
|
91
|
+
def _request_body(self, variables: Mapping[str, object], /) -> bytes:
|
|
92
|
+
# `json.dumps()`'s default separators are what make the concatenation valid JSON.
|
|
93
|
+
return self._request_prefix + dumps(variables).encode() + b"}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@final
|
|
97
|
+
class Request(Generic[_Type_co, _Data_co]):
|
|
98
|
+
"""An operation with the variable values its caller gives, not yet those the client injects, as a :class:`requests.Request` does not yet hold the headers its session adds.
|
|
99
|
+
|
|
100
|
+
It runs nothing: `ListBooks({"first": 10})` is a value that a client runs, synchronously or not.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
__slots__ = ("_on_execution_error", "_operation", "_variables")
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
*,
|
|
108
|
+
# The variables' type has served its purpose once they are checked.
|
|
109
|
+
operation: Operation[_Type_co, Any, _Data_co],
|
|
110
|
+
variables: Mapping[str, object],
|
|
111
|
+
on_execution_error: ExecutionErrorHandling,
|
|
112
|
+
) -> None:
|
|
113
|
+
self._operation: Final = operation
|
|
114
|
+
self._variables: Final = variables
|
|
115
|
+
self._on_execution_error: Final = on_execution_error
|
|
116
|
+
|
|
117
|
+
@override
|
|
118
|
+
def __repr__(self) -> str:
|
|
119
|
+
return f"Request(operation={self._operation!r}, variables={self._variables!r}, on_execution_error={self._on_execution_error!r})"
|
|
120
|
+
|
|
121
|
+
def returning_error(
|
|
122
|
+
self,
|
|
123
|
+
) -> "Request[_Type_co, _Data_co | ExecutionError[_Data_co]]":
|
|
124
|
+
"""Return a copy of this request whose :class:`~.error.ExecutionError` a client returns in place of its data instead of raising it.
|
|
125
|
+
|
|
126
|
+
The returned error is typed by the request, and so is its :meth:`~.error.ExecutionError.parse_data`, which no `except` clause can express; a caller tells it apart from the data before use.
|
|
127
|
+
Every other error still raises, since it means that the request as a whole failed.
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
# A request's data type being covariant, one returning its data is one returning its data or its error.
|
|
131
|
+
return Request(
|
|
132
|
+
operation=self._operation,
|
|
133
|
+
variables=self._variables,
|
|
134
|
+
on_execution_error="return",
|
|
135
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from typing import final
|
|
4
|
+
|
|
5
|
+
from graphql_codegen._generator.dotted_name import Reference, parse_dotted_name
|
|
6
|
+
from graphql_codegen._note import error_note
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@final
|
|
10
|
+
@dataclass(frozen=True, kw_only=True)
|
|
11
|
+
class Codec:
|
|
12
|
+
"""The functions converting a scalar's value between JSON and Python, such as a :class:`~datetime.datetime` sent as an ISO string."""
|
|
13
|
+
|
|
14
|
+
decode: str
|
|
15
|
+
"""The dotted name of the function converting its JSON value to its Python one."""
|
|
16
|
+
|
|
17
|
+
encode: str
|
|
18
|
+
"""The dotted name of the function converting its Python value to its JSON one."""
|
|
19
|
+
|
|
20
|
+
def __post_init__(self) -> None:
|
|
21
|
+
# Parsed eagerly, so that no codec holds a dotted name it cannot use.
|
|
22
|
+
_ = self._decode_reference, self._encode_reference
|
|
23
|
+
|
|
24
|
+
@cached_property
|
|
25
|
+
def _decode_reference(self) -> Reference:
|
|
26
|
+
with error_note("In `decode`."):
|
|
27
|
+
return parse_dotted_name(self.decode)
|
|
28
|
+
|
|
29
|
+
@cached_property
|
|
30
|
+
def _encode_reference(self) -> Reference:
|
|
31
|
+
with error_note("In `encode`."):
|
|
32
|
+
return parse_dotted_name(self.encode)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@final
|
|
36
|
+
@dataclass(frozen=True, kw_only=True)
|
|
37
|
+
class Scalar:
|
|
38
|
+
"""How a scalar is typed, and converted if its Python value differs from its JSON one.
|
|
39
|
+
|
|
40
|
+
Not only a custom scalar: a built-in one can be given a type of its own too, such as `ID` typed `my_app.UserId`.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
type: str
|
|
44
|
+
"""The dotted name of its Python type.
|
|
45
|
+
|
|
46
|
+
When :attr:`codec` is ``None``, it must describe the decoded JSON value: a builtin, such as :class:`int`, or a :class:`typing.NewType` or type alias of one, such as `my_app.Isbn = NewType("Isbn", str)`.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
codec: Codec | None = None
|
|
50
|
+
"""When ``None``, the value stays as JSON decodes it."""
|
|
51
|
+
|
|
52
|
+
def __post_init__(self) -> None:
|
|
53
|
+
# Parsed eagerly, so that no scalar holds a dotted name it cannot use.
|
|
54
|
+
_ = self._type_reference
|
|
55
|
+
|
|
56
|
+
@cached_property
|
|
57
|
+
def _type_reference(self) -> Reference:
|
|
58
|
+
with error_note("In `type`."):
|
|
59
|
+
return parse_dotted_name(self.type)
|