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.
- cb523b6bada1c6eba8b4__mypyc.cpython-311-darwin.so +0 -0
- pyjelly/__init__.py +0 -0
- pyjelly/_proto/grpc.proto +33 -0
- pyjelly/_proto/patch.proto +165 -0
- pyjelly/_proto/rdf.proto +384 -0
- pyjelly/errors.py +10 -0
- pyjelly/integrations/__init__.py +0 -0
- pyjelly/integrations/generic/__init__.py +0 -0
- pyjelly/integrations/generic/generic_sink.py +202 -0
- pyjelly/integrations/generic/parse.py +412 -0
- pyjelly/integrations/generic/serialize.cpython-311-darwin.so +0 -0
- pyjelly/integrations/generic/serialize.py +402 -0
- pyjelly/integrations/rdflib/__init__.py +24 -0
- pyjelly/integrations/rdflib/parse.py +560 -0
- pyjelly/integrations/rdflib/serialize.py +408 -0
- pyjelly/jelly/__init__.py +5 -0
- pyjelly/jelly/rdf_pb2.py +70 -0
- pyjelly/jelly/rdf_pb2.pyi +231 -0
- pyjelly/options.py +141 -0
- pyjelly/parse/__init__.py +0 -0
- pyjelly/parse/decode.cpython-311-darwin.so +0 -0
- pyjelly/parse/decode.py +447 -0
- pyjelly/parse/ioutils.cpython-311-darwin.so +0 -0
- pyjelly/parse/ioutils.py +115 -0
- pyjelly/parse/lookup.cpython-311-darwin.so +0 -0
- pyjelly/parse/lookup.py +70 -0
- pyjelly/serialize/__init__.py +0 -0
- pyjelly/serialize/encode.cpython-311-darwin.so +0 -0
- pyjelly/serialize/encode.py +397 -0
- pyjelly/serialize/flows.py +196 -0
- pyjelly/serialize/ioutils.cpython-311-darwin.so +0 -0
- pyjelly/serialize/ioutils.py +13 -0
- pyjelly/serialize/lookup.cpython-311-darwin.so +0 -0
- pyjelly/serialize/lookup.py +137 -0
- pyjelly/serialize/streams.cpython-311-darwin.so +0 -0
- pyjelly/serialize/streams.py +281 -0
- pyjelly-0.7.1.dist-info/METADATA +114 -0
- pyjelly-0.7.1.dist-info/RECORD +41 -0
- pyjelly-0.7.1.dist-info/WHEEL +6 -0
- pyjelly-0.7.1.dist-info/entry_points.txt +7 -0
- 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
|
|
Binary file
|
|
@@ -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))
|
|
Binary file
|