trustfall 0.3.1__cp315-cp315-win_amd64.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.
trustfall/__init__.py ADDED
@@ -0,0 +1,41 @@
1
+ # Error types:
2
+ # - ParseError, when the provided input doesn't even parse as valid syntax
3
+ # - ValidationError, when the input is syntactically valid but doesn't match the schema
4
+ # - FrontendError, when the input's use of the schema is valid but the operations it attempts
5
+ # are not supported or not valid
6
+ # - InvalidIRQueryError should in principle never be seen in Python. It indicates that
7
+ # the internal representation could not be converted to its "indexed" (i.e. execution-ready) form.
8
+ # - InvalidSchemaError, when the provided schema cannot be used due to violating some necessary
9
+ # precondition as explained in the error message.
10
+ # - QueryArgumentsError, if the query by itself is fine but cannot be executed together with
11
+ # the provided arguments.
12
+ from ._internals import (
13
+ FrontendError,
14
+ InvalidIRQueryError,
15
+ InvalidSchemaError,
16
+ ParseError,
17
+ QueryArgumentsError,
18
+ Schema,
19
+ ValidationError,
20
+ )
21
+ from .adapter import Adapter, Context, FieldValue
22
+ from .execution import execute_query
23
+
24
+ __all__ = [
25
+ # from .adapter
26
+ "Adapter",
27
+ "Context",
28
+ "FieldValue",
29
+ #
30
+ # from .execution
31
+ "execute_query",
32
+ #
33
+ # from ._internals (defined in Rust)
34
+ "FrontendError",
35
+ "InvalidIRQueryError",
36
+ "InvalidSchemaError",
37
+ "ParseError",
38
+ "QueryArgumentsError",
39
+ "Schema",
40
+ "ValidationError",
41
+ ]
@@ -0,0 +1,27 @@
1
+ from . import trustfall
2
+
3
+ # Workaround around issue preventing direct `from ._trustfall_internal import X` imports:
4
+ # https://github.com/PyO3/pyo3/issues/759
5
+ # https://github.com/PyO3/pyo3/issues/1517
6
+ _trustfall_internal = trustfall._trustfall_internal
7
+ AdapterShim = _trustfall_internal.AdapterShim
8
+ Schema = _trustfall_internal.Schema
9
+ FrontendError = _trustfall_internal.FrontendError
10
+ InvalidIRQueryError = _trustfall_internal.InvalidIRQueryError
11
+ InvalidSchemaError = _trustfall_internal.InvalidSchemaError
12
+ ParseError = _trustfall_internal.ParseError
13
+ QueryArgumentsError = _trustfall_internal.QueryArgumentsError
14
+ ValidationError = _trustfall_internal.ValidationError
15
+ interpret_query = _trustfall_internal.interpret_query
16
+
17
+ __all__ = [
18
+ "AdapterShim",
19
+ "FrontendError",
20
+ "InvalidIRQueryError",
21
+ "InvalidSchemaError",
22
+ "ParseError",
23
+ "QueryArgumentsError",
24
+ "Schema",
25
+ "ValidationError",
26
+ "interpret_query",
27
+ ]
@@ -0,0 +1,20 @@
1
+ from typing import Any, Dict, Iterator, Mapping
2
+
3
+ from .adapter import Adapter
4
+
5
+ class AdapterShim:
6
+ def __init__(self, adapter: Adapter[Any]) -> None: ...
7
+
8
+ class FrontendError(Exception): ...
9
+ class InvalidIRQueryError(Exception): ...
10
+ class InvalidSchemaError(Exception): ...
11
+ class ParseError(Exception): ...
12
+ class QueryArgumentsError(Exception): ...
13
+ class ValidationError(Exception): ...
14
+
15
+ class Schema:
16
+ def __init__(self, schema_txt: str) -> None: ...
17
+
18
+ def interpret_query(
19
+ adapter: AdapterShim, schema: Schema, query: str, arguments: Mapping[str, Any]
20
+ ) -> Iterator[Dict[str, Any]]: ...
trustfall/adapter.py ADDED
@@ -0,0 +1,178 @@
1
+ from abc import ABCMeta, abstractmethod
2
+ from typing import (
3
+ Any,
4
+ Generic,
5
+ Iterable,
6
+ Iterator,
7
+ Mapping,
8
+ Optional,
9
+ Tuple,
10
+ TypeVar,
11
+ Union,
12
+ )
13
+
14
+ FieldValue = Union[None, str, int, float, bool, list["FieldValue"]]
15
+ """
16
+ The type of property values, query arguments, and columns in the result.
17
+ """
18
+
19
+
20
+ Vertex = TypeVar("Vertex")
21
+ """
22
+ The type of vertices in the dataset implemented by an adapter.
23
+ """
24
+
25
+
26
+ class Context(Generic[Vertex]):
27
+ """Adapter helper type. Its active_vertex property indicates vertices whose data is needed."""
28
+
29
+ __slots__ = ("_active_vertex",)
30
+
31
+ _active_vertex: Optional[Vertex]
32
+
33
+ @property
34
+ def active_vertex(self) -> Optional[Vertex]:
35
+ """The vertex whose information (properties, edges, etc.) needs to be resolved."""
36
+ return self._active_vertex
37
+
38
+
39
+ class Adapter(Generic[Vertex], metaclass=ABCMeta):
40
+ @abstractmethod
41
+ def resolve_starting_vertices(
42
+ self,
43
+ edge_name: str,
44
+ parameters: Mapping[str, FieldValue],
45
+ /,
46
+ *args: Any,
47
+ **kwargs: Any,
48
+ ) -> Iterable[Vertex]:
49
+ """Produce an iterable of vertices for the specified starting edge.
50
+
51
+ Starting edges are ones where queries are allowed to begin.
52
+ They are defined directly on the root query type of the schema.
53
+ For example, `Foo` is the starting edge of the following query:
54
+ ```graphql
55
+ query {
56
+ Foo {
57
+ bar @output
58
+ }
59
+ }
60
+ ```
61
+
62
+ The caller guarantees that:
63
+ - The specified edge is a starting edge in the schema being queried.
64
+ - Any parameters the edge requires per the schema have values provided.
65
+ """
66
+
67
+ @abstractmethod
68
+ def resolve_property(
69
+ self,
70
+ contexts: Iterator[Context[Vertex]],
71
+ type_name: str,
72
+ property_name: str,
73
+ /,
74
+ *args: Any,
75
+ **kwargs: Any,
76
+ ) -> Iterable[Tuple[Context[Vertex], FieldValue]]:
77
+ """Resolve the value of a vertex property, over an iterator of query contexts.
78
+
79
+ Each context in the `contexts` argument has an active vertex of type Optional[Vertex],
80
+ which is either None or represents a vertex of type `type_name` defined in the schema.
81
+
82
+ This function resolves the property value on that active vertex.
83
+
84
+ The caller guarantees that:
85
+ - `type_name` is a type or interface defined in the schema.
86
+ - `property_name` is either a property field on `type_name` defined in the schema,
87
+ or the special value `"__typename"` requesting the name of the vertex's type.
88
+ - When the active vertex is not None, then it's a vertex of type `type_name`:
89
+ either its type is exactly `type_name`, or `type_name` is an interface that
90
+ the vertex's type implements.
91
+
92
+ The returned iterable must satisfy these properties:
93
+ - Produce `(context, property_value)` tuples with the property's value for that context.
94
+ - Produce contexts in the same order as the input `contexts` iterator produced them.
95
+ - Produce property values whose type matches the property's type defined in the schema.
96
+ - When a context's active vertex is `None`, its property value is `None`.
97
+ """
98
+
99
+ @abstractmethod
100
+ def resolve_neighbors(
101
+ self,
102
+ contexts: Iterator[Context[Vertex]],
103
+ type_name: str,
104
+ edge_name: str,
105
+ parameters: Mapping[str, FieldValue],
106
+ /,
107
+ *args: Any,
108
+ **kwargs: Any,
109
+ ) -> Iterable[Tuple[Context[Vertex], Iterable[Vertex]]]:
110
+ """Resolve the neighboring vertices across an edge, over an iterator of query contexts.
111
+
112
+ Each context in the `contexts` argument has an active vertex of type Optional[Vertex],
113
+ which is either None or represents a vertex of type `type_name` defined in the schema.
114
+
115
+ This function resolves the neighboring vertices for that active vertex.
116
+
117
+ If the schema this adapter covers has no edges aside from starting edges, then
118
+ this method will never be called and may be implemented as `raise NotImplementedError()`.
119
+
120
+ The caller guarantees that:
121
+ - `type_name` is a type or interface defined in the schema.
122
+ - `edge_name` is an edge field on `type_name` defined in the schema.
123
+ - Any parameters the edge requires per the schema have values provided.
124
+ - When the active vertex is not None, then it's a vertex of type `type_name`:
125
+ either its type is exactly `type_name`, or `type_name` is an interface that
126
+ the vertex's type implements.
127
+
128
+ The returned iterator must satisfy these properties:
129
+ - Produce `(context, neighbors)` tuples with an iterator of neighbor vertices for that edge.
130
+ - Produce contexts in the same order as the input `contexts` iterator produced them.
131
+ - Each neighboring vertex is of the type specified for that edge in the schema.
132
+ - When a context's active vertex is None, it has an empty neighbors iterator.
133
+ """
134
+
135
+ @abstractmethod
136
+ def resolve_coercion(
137
+ self,
138
+ contexts: Iterator[Context[Vertex]],
139
+ type_name: str,
140
+ coerce_to_type: str,
141
+ /,
142
+ *args: Any,
143
+ **kwargs: Any,
144
+ ) -> Iterable[Tuple[Context[Vertex], bool]]:
145
+ """Attempt to coerce vertices to a subtype, over an iterator of query contexts.
146
+
147
+ In this example query, the starting vertices of type `Foo` are coerced to `Bar`:
148
+ ```graphql
149
+ query {
150
+ Foo {
151
+ ... on Bar {
152
+ abc @output
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ Each context in the `contexts` argument has an active vertex of type `Optional[Vertex]`,
159
+ which is either None or represents a vertex of type `type_name` defined in the schema.
160
+
161
+ This function checks whether the active vertex is of the specified subtype.
162
+
163
+ If this adapter's schema contains no subtyping, then no type coercions are possible:
164
+ this method will never be called and may be implemented as `raise NotImplementedError()`.
165
+
166
+ The caller guarantees that:
167
+ - `type_name` is an interface defined in the schema.
168
+ - `coerce_to_type` is a type or interface that implements `type_name` in the schema.
169
+ - When the active vertex is not None, then it's a vertex of type `type_name`:
170
+ either its type is exactly `type_name`, or `type_name` is an interface that
171
+ the vertex's type implements.
172
+
173
+ The returned iterator must satisfy these properties:
174
+ - Produce `(context, can_coerce)` tuples showing if the coercion succeded for that context.
175
+ - Produce contexts in the same order as the input `contexts` iterator produced them.
176
+ - Each neighboring vertex is of the type specified for that edge in the schema.
177
+ - When a context's active vertex is `None`, its coercion outcome is `False`.
178
+ """
trustfall/execution.py ADDED
@@ -0,0 +1,20 @@
1
+ from typing import Any, Dict, Iterator, Mapping
2
+
3
+ from ._internals import AdapterShim, Schema, interpret_query
4
+ from .adapter import Adapter, Vertex
5
+
6
+
7
+ def execute_query(
8
+ adapter: Adapter[Vertex],
9
+ schema: Schema,
10
+ query: str,
11
+ arguments: Mapping[str, Any],
12
+ /,
13
+ ) -> Iterator[Dict[str, Any]]:
14
+ """Execute the given query using the adapter, returning an iterator of result dicts."""
15
+ if not isinstance(adapter, Adapter):
16
+ raise TypeError(
17
+ f"Expected 'adapter' input to be a subclass of Adapter, but instead got: {adapter}"
18
+ )
19
+
20
+ return interpret_query(AdapterShim(adapter), schema, query, arguments)
trustfall/py.typed ADDED
File without changes
File without changes
@@ -0,0 +1,105 @@
1
+ from typing import Any, Iterable, Iterator, Mapping, Tuple, Union, cast
2
+
3
+ from .. import Adapter, Context, FieldValue
4
+
5
+ _NUMBER_NAMES = [
6
+ "zero",
7
+ "one",
8
+ "two",
9
+ "three",
10
+ "four",
11
+ "five",
12
+ "six",
13
+ "seven",
14
+ "eight",
15
+ "nine",
16
+ "ten",
17
+ ]
18
+
19
+ Vertex = int
20
+
21
+
22
+ class NumbersAdapter(Adapter[Vertex]):
23
+ def resolve_starting_vertices(
24
+ self,
25
+ edge_name: str,
26
+ parameters: Mapping[str, FieldValue],
27
+ /,
28
+ *args: Any,
29
+ **kwargs: Any,
30
+ ) -> Iterable[Vertex]:
31
+ max_value = cast(int, parameters["max"]) # type known from schema and enforced by Trustfall
32
+
33
+ # We could just `yield from range(0, max_value)` here.
34
+ # But that returns an `Iterator` which is an easier type to deal with than `Iterable`,
35
+ # and we've had a bug with correct `Iterable` handling already.
36
+ # So let's return an `Iterable` instead by wrapping it in `list()`.
37
+ return list(range(0, max_value))
38
+
39
+ def resolve_property(
40
+ self,
41
+ contexts: Iterator[Context[Vertex]],
42
+ type_name: str,
43
+ property_name: str,
44
+ /,
45
+ *args: Any,
46
+ **kwargs: Any,
47
+ ) -> Iterable[Tuple[Context[Vertex], Any]]:
48
+ for context in contexts:
49
+ active_vertex = context.active_vertex
50
+ value: Union[int, str, None] = None
51
+ if active_vertex is not None:
52
+ if property_name == "value":
53
+ value = active_vertex
54
+ elif property_name == "name":
55
+ if 0 <= active_vertex < len(_NUMBER_NAMES):
56
+ value = _NUMBER_NAMES[active_vertex]
57
+ else:
58
+ raise NotImplementedError()
59
+
60
+ yield (context, value)
61
+
62
+ def resolve_neighbors(
63
+ self,
64
+ contexts: Iterator[Context[Vertex]],
65
+ type_name: str,
66
+ edge_name: str,
67
+ parameters: Mapping[str, FieldValue],
68
+ /,
69
+ *args: Any,
70
+ **kwargs: Any,
71
+ ) -> Iterable[Tuple[Context[Vertex], Iterable[Vertex]]]:
72
+ for context in contexts:
73
+ active_vertex = context.active_vertex
74
+ neighbors: Iterable[Vertex] = []
75
+ if active_vertex is not None:
76
+ if edge_name == "multiple":
77
+ if active_vertex > 0:
78
+ max_value = cast(
79
+ int, parameters["max"]
80
+ ) # type known from schema and enforced by Trustfall
81
+ neighbors = range(
82
+ 2 * active_vertex,
83
+ max_value * active_vertex + 1,
84
+ active_vertex,
85
+ )
86
+ elif edge_name == "predecessor":
87
+ if active_vertex > 0:
88
+ neighbors = [active_vertex - 1]
89
+ elif edge_name == "successor":
90
+ neighbors = [active_vertex + 1]
91
+ else:
92
+ raise NotImplementedError()
93
+
94
+ yield (context, neighbors)
95
+
96
+ def resolve_coercion(
97
+ self,
98
+ contexts: Iterator[Context[Vertex]],
99
+ type_name: str,
100
+ coerce_to_type: str,
101
+ /,
102
+ *args: Any,
103
+ **kwargs: Any,
104
+ ) -> Iterable[Tuple[Context[Vertex], bool]]:
105
+ raise NotImplementedError()