pyjelly 0.7.1__cp311-cp311-macosx_11_0_x86_64.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.
Files changed (41) hide show
  1. cb523b6bada1c6eba8b4__mypyc.cpython-311-darwin.so +0 -0
  2. pyjelly/__init__.py +0 -0
  3. pyjelly/_proto/grpc.proto +33 -0
  4. pyjelly/_proto/patch.proto +165 -0
  5. pyjelly/_proto/rdf.proto +384 -0
  6. pyjelly/errors.py +10 -0
  7. pyjelly/integrations/__init__.py +0 -0
  8. pyjelly/integrations/generic/__init__.py +0 -0
  9. pyjelly/integrations/generic/generic_sink.py +202 -0
  10. pyjelly/integrations/generic/parse.py +412 -0
  11. pyjelly/integrations/generic/serialize.cpython-311-darwin.so +0 -0
  12. pyjelly/integrations/generic/serialize.py +402 -0
  13. pyjelly/integrations/rdflib/__init__.py +24 -0
  14. pyjelly/integrations/rdflib/parse.py +560 -0
  15. pyjelly/integrations/rdflib/serialize.py +408 -0
  16. pyjelly/jelly/__init__.py +5 -0
  17. pyjelly/jelly/rdf_pb2.py +70 -0
  18. pyjelly/jelly/rdf_pb2.pyi +231 -0
  19. pyjelly/options.py +141 -0
  20. pyjelly/parse/__init__.py +0 -0
  21. pyjelly/parse/decode.cpython-311-darwin.so +0 -0
  22. pyjelly/parse/decode.py +447 -0
  23. pyjelly/parse/ioutils.cpython-311-darwin.so +0 -0
  24. pyjelly/parse/ioutils.py +115 -0
  25. pyjelly/parse/lookup.cpython-311-darwin.so +0 -0
  26. pyjelly/parse/lookup.py +70 -0
  27. pyjelly/serialize/__init__.py +0 -0
  28. pyjelly/serialize/encode.cpython-311-darwin.so +0 -0
  29. pyjelly/serialize/encode.py +397 -0
  30. pyjelly/serialize/flows.py +196 -0
  31. pyjelly/serialize/ioutils.cpython-311-darwin.so +0 -0
  32. pyjelly/serialize/ioutils.py +13 -0
  33. pyjelly/serialize/lookup.cpython-311-darwin.so +0 -0
  34. pyjelly/serialize/lookup.py +137 -0
  35. pyjelly/serialize/streams.cpython-311-darwin.so +0 -0
  36. pyjelly/serialize/streams.py +281 -0
  37. pyjelly-0.7.1.dist-info/METADATA +114 -0
  38. pyjelly-0.7.1.dist-info/RECORD +41 -0
  39. pyjelly-0.7.1.dist-info/WHEEL +6 -0
  40. pyjelly-0.7.1.dist-info/entry_points.txt +7 -0
  41. pyjelly-0.7.1.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,397 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable, Iterator, Sequence
4
+ from enum import IntEnum
5
+ from typing import TypeAlias, TypeVar
6
+
7
+ from mypy_extensions import mypyc_attr
8
+
9
+ from pyjelly import jelly, options
10
+ from pyjelly.errors import JellyConformanceError
11
+ from pyjelly.serialize.lookup import LookupEncoder
12
+
13
+
14
+ def split_iri(iri_string: str) -> tuple[str, str]:
15
+ """
16
+ Split iri into prefix and name.
17
+
18
+ Args:
19
+ iri_string (str): full iri string.
20
+
21
+ Returns:
22
+ tuple[str, str]: iri's prefix and name.
23
+
24
+ """
25
+ name = iri_string
26
+ prefix = ""
27
+ for sep in "#", "/":
28
+ prefix, char, name = iri_string.rpartition(sep)
29
+ if char:
30
+ return prefix + char, name
31
+ return prefix, name
32
+
33
+
34
+ T = TypeVar("T")
35
+ Rows: TypeAlias = Sequence[jelly.RdfStreamRow]
36
+ Statement: TypeAlias = jelly.RdfQuad | jelly.RdfTriple
37
+ HasGraph: TypeAlias = jelly.RdfQuad | jelly.RdfGraphStart
38
+ Terms: TypeAlias = (
39
+ jelly.RdfIri | jelly.RdfLiteral | str | jelly.RdfDefaultGraph | jelly.RdfTriple
40
+ )
41
+
42
+
43
+ @mypyc_attr(allow_interpreted_subclasses=True)
44
+ class TermEncoder:
45
+ def __init__(
46
+ self,
47
+ lookup_preset: options.LookupPreset | None = None,
48
+ ) -> None:
49
+ if lookup_preset is None:
50
+ lookup_preset = options.LookupPreset()
51
+ self.lookup_preset = lookup_preset
52
+ self.names = LookupEncoder(lookup_size=lookup_preset.max_names)
53
+ self.prefixes = LookupEncoder(lookup_size=lookup_preset.max_prefixes)
54
+ self.datatypes = LookupEncoder(lookup_size=lookup_preset.max_datatypes)
55
+
56
+ def encode_iri_indices(self, iri_string: str) -> tuple[Rows, int, int]:
57
+ """
58
+ Encode lookup indices for IRI.
59
+
60
+ Args:
61
+ iri_string (str): full iri in string format.
62
+
63
+ Returns:
64
+ tuple[Rows, int, int]: additional rows (if any) and
65
+ indices in prefix and name tables.
66
+
67
+ """
68
+ prefix, name = split_iri(iri_string)
69
+ if self.prefixes.lookup.max_size:
70
+ prefix_entry_index = self.prefixes.encode_entry_index(prefix)
71
+ else:
72
+ name = iri_string
73
+ prefix_entry_index = None
74
+
75
+ name_entry_index = self.names.encode_entry_index(name)
76
+ term_rows = []
77
+
78
+ if prefix_entry_index is not None:
79
+ prefix_entry = jelly.RdfPrefixEntry(id=prefix_entry_index, value=prefix)
80
+ term_rows.append(jelly.RdfStreamRow(prefix=prefix_entry))
81
+
82
+ if name_entry_index is not None:
83
+ name_entry = jelly.RdfNameEntry(id=name_entry_index, value=name)
84
+ term_rows.append(jelly.RdfStreamRow(name=name_entry))
85
+
86
+ prefix_index = self.prefixes.encode_prefix_term_index(prefix)
87
+ name_index = self.names.encode_name_term_index(name)
88
+ return term_rows, prefix_index, name_index
89
+
90
+ def encode_iri(self, iri_string: str, iri: jelly.RdfIri) -> Rows:
91
+ """
92
+ Encode iri.
93
+
94
+ Args:
95
+ iri_string (str): full iri in string format.
96
+ iri (jelly.RdfIri): iri to fill
97
+
98
+ Returns:
99
+ Rows: extra rows for prefix and name tables, if any.
100
+
101
+ """
102
+ term_rows, prefix_index, name_index = self.encode_iri_indices(iri_string)
103
+ iri.prefix_id = prefix_index
104
+ iri.name_id = name_index
105
+ return term_rows
106
+
107
+ def encode_default_graph(self, g_default_graph: jelly.RdfDefaultGraph) -> Rows:
108
+ """
109
+ Encode default graph.
110
+
111
+ Returns:
112
+ Rows: empty extra rows (for API consistency)
113
+
114
+ """
115
+ g_default_graph.CopyFrom(jelly.RdfDefaultGraph())
116
+ return ()
117
+
118
+ def encode_literal(
119
+ self,
120
+ *,
121
+ lex: str,
122
+ language: str | None = None,
123
+ datatype: str | None = None,
124
+ literal: jelly.RdfLiteral,
125
+ ) -> Rows:
126
+ """
127
+ Encode literal.
128
+
129
+ Args:
130
+ lex (str): lexical form/literal value
131
+ language (str | None, optional): langtag. Defaults to None.
132
+ datatype (str | None, optional): data type if
133
+ it is a typed literal. Defaults to None.
134
+ literal (jelly.RdfLiteral): literal to fill.
135
+
136
+ Raises:
137
+ JellyConformanceError: if datatype specified while
138
+ datatable is not used.
139
+
140
+ Returns:
141
+ Rows: extra rows (i.e., datatype entries).
142
+
143
+ """
144
+ datatype_id = None
145
+ term_rows: tuple[()] | tuple[jelly.RdfStreamRow] = ()
146
+
147
+ if datatype and datatype != options.STRING_DATATYPE_IRI:
148
+ if self.datatypes.lookup.max_size == 0:
149
+ msg = (
150
+ f"can't encode literal with type {datatype}: "
151
+ "datatype lookup cannot be used if disabled "
152
+ "(its size was set to 0)"
153
+ )
154
+ raise JellyConformanceError(msg)
155
+ datatype_entry_id = self.datatypes.encode_entry_index(datatype)
156
+
157
+ if datatype_entry_id is not None:
158
+ entry = jelly.RdfDatatypeEntry(id=datatype_entry_id, value=datatype)
159
+ term_rows = (jelly.RdfStreamRow(datatype=entry),)
160
+
161
+ datatype_id = self.datatypes.encode_datatype_term_index(datatype)
162
+
163
+ literal.lex = lex
164
+ if language:
165
+ literal.langtag = language
166
+ if datatype_id:
167
+ literal.datatype = datatype_id
168
+ return term_rows
169
+
170
+ def encode_quoted_triple(
171
+ self, terms: Iterable[object], quoted_statement: jelly.RdfTriple
172
+ ) -> Rows:
173
+ """
174
+ Encode a quoted triple.
175
+
176
+ Notes:
177
+ Although a triple, it is treated as a part of a statement.
178
+ Repeated terms are not used when encoding quoted triples.
179
+
180
+ Args:
181
+ terms (Iterable[object]): triple terms to encode.
182
+ quoted_statement (jelly.RdfTriple): quoted triple to fill.
183
+
184
+ Returns:
185
+ Rows: additional stream rows with preceeding
186
+ information (prefixes, names, datatypes rows, if any).
187
+
188
+ """
189
+ rows: list[jelly.RdfStreamRow] = []
190
+ terms = iter(terms)
191
+ extra_rows = self.encode_spo(next(terms), Slot.subject, quoted_statement)
192
+ rows.extend(extra_rows)
193
+ extra_rows = self.encode_spo(next(terms), Slot.predicate, quoted_statement)
194
+ rows.extend(extra_rows)
195
+ extra_rows = self.encode_spo(next(terms), Slot.object, quoted_statement)
196
+ rows.extend(extra_rows)
197
+ return rows
198
+
199
+ def encode_spo(self, term: object, slot: Slot, statement: Statement) -> Rows:
200
+ msg = f"unsupported term type: {type(term)}"
201
+ raise NotImplementedError(msg)
202
+
203
+ def encode_graph(self, term: object, statement: HasGraph) -> Rows:
204
+ msg = f"unsupported term type: {type(term)}"
205
+ raise NotImplementedError(msg)
206
+
207
+ def get_iri_field(self, statement: Statement, slot: Slot) -> jelly.RdfIri:
208
+ """Get IRI field directly based on slot."""
209
+ if slot == Slot.subject:
210
+ return statement.s_iri
211
+ if slot == Slot.predicate:
212
+ return statement.p_iri
213
+ return statement.o_iri
214
+
215
+ def get_literal_field(self, statement: Statement, slot: Slot) -> jelly.RdfLiteral:
216
+ """Get literal field directly based on slot."""
217
+ if slot == Slot.subject:
218
+ return statement.s_literal
219
+ if slot == Slot.predicate:
220
+ return statement.p_literal
221
+ return statement.o_literal
222
+
223
+ def set_bnode_field(
224
+ self, statement: Statement, slot: Slot, identifier: str
225
+ ) -> None:
226
+ """Set bnode field directly based on slot."""
227
+ if slot == Slot.subject:
228
+ statement.s_bnode = identifier
229
+ elif slot == Slot.predicate:
230
+ statement.p_bnode = identifier
231
+ else:
232
+ statement.o_bnode = identifier
233
+
234
+ def get_triple_field(self, statement: Statement, slot: Slot) -> jelly.RdfTriple:
235
+ """Get triple term field directly based on slot."""
236
+ if slot == Slot.subject:
237
+ return statement.s_triple_term
238
+ if slot == Slot.predicate:
239
+ return statement.p_triple_term
240
+ return statement.o_triple_term
241
+
242
+
243
+ class Slot(IntEnum):
244
+ subject = 0
245
+ predicate = 1
246
+ object = 2
247
+ graph = 3
248
+
249
+
250
+ def encode_spo(
251
+ terms: Iterator[object],
252
+ term_encoder: TermEncoder,
253
+ repeated_terms: list[object | None],
254
+ statement: Statement,
255
+ ) -> list[jelly.RdfStreamRow]:
256
+ """
257
+ Encode the s/p/o of a statement.
258
+
259
+ Args:
260
+ terms (Iterator[object]): iterator for original terms to encode
261
+ term_encoder (TermEncoder): encoder with lookup tables
262
+ repeated_terms (list[object | None): list of repeated terms.
263
+ statement (Statement): Triple/Quad to fill.
264
+
265
+ Returns:
266
+ list[jelly.RdfStreamRow] extra rows to append.
267
+
268
+ """
269
+ rows: list[jelly.RdfStreamRow] = []
270
+ s = next(terms)
271
+ if repeated_terms[Slot.subject] != s:
272
+ extra_rows = term_encoder.encode_spo(s, Slot.subject, statement)
273
+ rows.extend(extra_rows)
274
+ repeated_terms[Slot.subject] = s
275
+ p = next(terms)
276
+ if repeated_terms[Slot.predicate] != p:
277
+ extra_rows = term_encoder.encode_spo(p, Slot.predicate, statement)
278
+ rows.extend(extra_rows)
279
+ repeated_terms[Slot.predicate] = p
280
+ o = next(terms)
281
+ if repeated_terms[Slot.object] != o:
282
+ extra_rows = term_encoder.encode_spo(o, Slot.object, statement)
283
+ rows.extend(extra_rows)
284
+ repeated_terms[Slot.object] = o
285
+ return rows
286
+
287
+
288
+ def encode_triple(
289
+ terms: Iterable[object],
290
+ term_encoder: TermEncoder,
291
+ repeated_terms: list[object | None],
292
+ ) -> list[jelly.RdfStreamRow]:
293
+ """
294
+ Encode one triple.
295
+
296
+ Args:
297
+ terms (Iterable[object]): original terms to encode
298
+ term_encoder (TermEncoder): current encoder with lookup tables
299
+ repeated_terms (list[object | None]): list of repeated terms.
300
+
301
+ Returns:
302
+ list[jelly.RdfStreamRow]: list of rows to add to the current flow.
303
+
304
+ """
305
+ triple = jelly.RdfTriple()
306
+ terms = iter(terms)
307
+ rows = encode_spo(terms, term_encoder, repeated_terms, triple)
308
+ row = jelly.RdfStreamRow(triple=triple)
309
+ rows.append(row)
310
+ return rows
311
+
312
+
313
+ def encode_quad(
314
+ terms: Iterable[object],
315
+ term_encoder: TermEncoder,
316
+ repeated_terms: list[object | None],
317
+ ) -> list[jelly.RdfStreamRow]:
318
+ """
319
+ Encode one quad.
320
+
321
+ Args:
322
+ terms (Iterable[object]): original terms to encode
323
+ term_encoder (TermEncoder): current encoder with lookup tables
324
+ repeated_terms (list[object | None]): list of repeated terms.
325
+
326
+ Returns:
327
+ list[jelly.RdfStreamRow]: list of messages to append to current flow.
328
+
329
+ """
330
+ terms = iter(terms)
331
+ quad = jelly.RdfQuad()
332
+ rows = encode_spo(terms, term_encoder, repeated_terms, quad)
333
+ g = next(terms)
334
+ if repeated_terms[Slot.graph] != g:
335
+ extra_rows = term_encoder.encode_graph(g, quad)
336
+ rows.extend(extra_rows)
337
+ repeated_terms[Slot.graph] = g
338
+ row = jelly.RdfStreamRow(quad=quad)
339
+ rows.append(row)
340
+ return rows
341
+
342
+
343
+ def encode_namespace_declaration(
344
+ name: str,
345
+ value: str,
346
+ term_encoder: TermEncoder,
347
+ ) -> list[jelly.RdfStreamRow]:
348
+ """
349
+ Encode namespace declaration.
350
+
351
+ Args:
352
+ name (str): namespace prefix label
353
+ value (str): namespace iri
354
+ term_encoder (TermEncoder): current encoder
355
+
356
+ Returns:
357
+ list[jelly.RdfStreamRow]: list of messages to append to current flow.
358
+
359
+ """
360
+ iri = jelly.RdfIri()
361
+ [*rows] = term_encoder.encode_iri(value, iri=iri)
362
+ declaration = jelly.RdfNamespaceDeclaration(name=name, value=iri)
363
+ row = jelly.RdfStreamRow(namespace=declaration)
364
+ rows.append(row)
365
+ return rows
366
+
367
+
368
+ def encode_options(
369
+ lookup_preset: options.LookupPreset,
370
+ stream_types: options.StreamTypes,
371
+ params: options.StreamParameters,
372
+ ) -> jelly.RdfStreamRow:
373
+ """
374
+ Encode stream options to ProtoBuf message.
375
+
376
+ Args:
377
+ lookup_preset (options.LookupPreset): lookup tables options
378
+ stream_types (options.StreamTypes): physical and logical types
379
+ params (options.StreamParameters): other params.
380
+
381
+ Returns:
382
+ jelly.RdfStreamRow: encoded stream options row
383
+
384
+ """
385
+ return jelly.RdfStreamRow(
386
+ options=jelly.RdfStreamOptions(
387
+ stream_name=params.stream_name,
388
+ physical_type=stream_types.physical_type,
389
+ generalized_statements=params.generalized_statements,
390
+ rdf_star=params.rdf_star,
391
+ max_name_table_size=lookup_preset.max_names,
392
+ max_prefix_table_size=lookup_preset.max_prefixes,
393
+ max_datatype_table_size=lookup_preset.max_datatypes,
394
+ logical_type=stream_types.logical_type,
395
+ version=params.version,
396
+ )
397
+ )
@@ -0,0 +1,196 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import UserList
4
+ from collections.abc import Iterable
5
+ from dataclasses import dataclass
6
+ from typing import Any, ClassVar
7
+ from typing_extensions import override
8
+
9
+ from pyjelly import jelly
10
+
11
+ DEFAULT_FRAME_SIZE = 250
12
+
13
+
14
+ class FrameFlow(UserList[jelly.RdfStreamRow]):
15
+ """
16
+ Abstract base class for producing Jelly frames from RDF stream rows.
17
+
18
+ Collects stream rows and assembles them into RdfStreamFrame objects when ready.
19
+
20
+ Allows for passing LogicalStreamType, required for
21
+ logical subtypes and non-delimited streams.
22
+ """
23
+
24
+ logical_type: jelly.LogicalStreamType
25
+ registry: ClassVar[dict[jelly.LogicalStreamType, type[FrameFlow]]] = {}
26
+
27
+ def __init__(
28
+ self,
29
+ initlist: Iterable[jelly.RdfStreamRow] | None = None,
30
+ *,
31
+ logical_type: jelly.LogicalStreamType | None = None,
32
+ **__kwargs: Any,
33
+ ) -> None:
34
+ super().__init__(initlist)
35
+ self.logical_type = logical_type or self.__class__.logical_type
36
+
37
+ def frame_from_graph(self) -> jelly.RdfStreamFrame | None:
38
+ """
39
+ Treat the current rows as a graph and produce a frame.
40
+
41
+ Default implementation returns None.
42
+ """
43
+ return None
44
+
45
+ def frame_from_dataset(self) -> jelly.RdfStreamFrame | None:
46
+ """
47
+ Treat the current rows as a dataset and produce a frame.
48
+
49
+ Default implementation returns None.
50
+ """
51
+ return None
52
+
53
+ def frame_from_bounds(self) -> jelly.RdfStreamFrame | None:
54
+ return None
55
+
56
+ def to_stream_frame(self) -> jelly.RdfStreamFrame | None:
57
+ """
58
+ Create stream frame from flow content.
59
+
60
+ Notes:
61
+ Clears flow content after creating the frame.
62
+
63
+ Returns:
64
+ jelly.RdfStreamFrame | None: stream frame
65
+
66
+ """
67
+ if not self:
68
+ return None
69
+ frame = jelly.RdfStreamFrame(rows=self)
70
+ self.clear()
71
+ return frame
72
+
73
+
74
+ class ManualFrameFlow(FrameFlow):
75
+ """
76
+ Produces frames only when manually requested (never automatically).
77
+
78
+ !!! warning
79
+ All stream rows are kept in memory until `to_stream_frame()` is called.
80
+ This may lead to high memory usage for large streams.
81
+
82
+ Used for non-delimited serialization.
83
+ """
84
+
85
+ logical_type = jelly.LOGICAL_STREAM_TYPE_UNSPECIFIED
86
+
87
+
88
+ @dataclass
89
+ class BoundedFrameFlow(FrameFlow):
90
+ """
91
+ Produce frames automatically when a fixed number of rows is reached.
92
+
93
+ Used for delimited encoding (default mode).
94
+ """
95
+
96
+ logical_type = jelly.LOGICAL_STREAM_TYPE_UNSPECIFIED
97
+ frame_size: int
98
+
99
+ @override
100
+ def __init__(
101
+ self,
102
+ initlist: Iterable[jelly.RdfStreamRow] | None = None,
103
+ logical_type: jelly.LogicalStreamType | None = None,
104
+ *,
105
+ frame_size: int | None = None,
106
+ ) -> None:
107
+ super().__init__(initlist, logical_type=logical_type)
108
+ self.frame_size = frame_size or DEFAULT_FRAME_SIZE
109
+
110
+ @override
111
+ def frame_from_bounds(self) -> jelly.RdfStreamFrame | None:
112
+ """
113
+ Emit frame from flow if full.
114
+
115
+ Returns:
116
+ jelly.RdfStreamFrame | None: stream frame
117
+
118
+ """
119
+ if len(self) >= self.frame_size:
120
+ return self.to_stream_frame()
121
+ return None
122
+
123
+
124
+ class FlatTriplesFrameFlow(BoundedFrameFlow):
125
+ logical_type = jelly.LOGICAL_STREAM_TYPE_FLAT_TRIPLES
126
+
127
+
128
+ class FlatQuadsFrameFlow(BoundedFrameFlow):
129
+ logical_type = jelly.LOGICAL_STREAM_TYPE_FLAT_QUADS
130
+
131
+
132
+ class GraphsFrameFlow(FrameFlow):
133
+ logical_type = jelly.LOGICAL_STREAM_TYPE_GRAPHS
134
+
135
+ def frame_from_graph(self) -> jelly.RdfStreamFrame | None:
136
+ """
137
+ Emit current flow content (one graph) as jelly frame.
138
+
139
+ Returns:
140
+ jelly.RdfStreamFrame | None: jelly frame or none if
141
+ flow is empty.
142
+
143
+ """
144
+ return self.to_stream_frame()
145
+
146
+
147
+ class DatasetsFrameFlow(FrameFlow):
148
+ logical_type = jelly.LOGICAL_STREAM_TYPE_DATASETS
149
+
150
+ def frame_from_dataset(self) -> jelly.RdfStreamFrame | None:
151
+ """
152
+ Emit current flow content (dataset) as jelly frame.
153
+
154
+ Returns:
155
+ jelly.RdfStreamFrame | None: jelly frame or none if
156
+ flow is empty.
157
+
158
+ """
159
+ return self.to_stream_frame()
160
+
161
+
162
+ FLOW_DISPATCH: dict[jelly.LogicalStreamType, type[FrameFlow]] = {
163
+ jelly.LOGICAL_STREAM_TYPE_FLAT_TRIPLES: FlatTriplesFrameFlow,
164
+ jelly.LOGICAL_STREAM_TYPE_FLAT_QUADS: FlatQuadsFrameFlow,
165
+ jelly.LOGICAL_STREAM_TYPE_GRAPHS: GraphsFrameFlow,
166
+ jelly.LOGICAL_STREAM_TYPE_DATASETS: DatasetsFrameFlow,
167
+ }
168
+
169
+
170
+ def flow_for_type(logical_type: jelly.LogicalStreamType) -> type[FrameFlow]:
171
+ """
172
+ Return flow based on logical type requested.
173
+
174
+ Note: uses base logical type for subtypes (i.e., SUBJECT_GRAPHS uses
175
+ the same flow as its base type GRAPHS).
176
+
177
+ Args:
178
+ logical_type (jelly.LogicalStreamType): logical type requested.
179
+
180
+ Raises:
181
+ NotImplementedError: if (base) logical stream type is not supported.
182
+
183
+ Returns:
184
+ type[FrameFlow]: FrameFlow for respective logical type.
185
+
186
+ """
187
+ try:
188
+ base_logical_type_value = logical_type % 10
189
+ base_name = jelly.LogicalStreamType.Name(base_logical_type_value)
190
+ return FLOW_DISPATCH[getattr(jelly.LogicalStreamType, base_name)]
191
+ except KeyError:
192
+ msg = (
193
+ "unsupported logical stream type: "
194
+ f"{jelly.LogicalStreamType.Name(logical_type)}"
195
+ )
196
+ raise NotImplementedError(msg) from None
@@ -0,0 +1,13 @@
1
+ from typing import IO
2
+
3
+ from google.protobuf.proto import serialize_length_prefixed
4
+
5
+ from pyjelly import jelly
6
+
7
+
8
+ def write_delimited(frame: jelly.RdfStreamFrame, output_stream: IO[bytes]) -> None:
9
+ serialize_length_prefixed(frame, output_stream)
10
+
11
+
12
+ def write_single(frame: jelly.RdfStreamFrame, output_stream: IO[bytes]) -> None:
13
+ output_stream.write(frame.SerializeToString(deterministic=True))