modelable 1.0.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.
Potentially problematic release.
This version of modelable might be problematic. Click here for more details.
- modelable/__init__.py +1 -0
- modelable/__main__.py +3 -0
- modelable/_pydantic_py314_compat.py +31 -0
- modelable/cli.py +41 -0
- modelable/commands/__init__.py +1 -0
- modelable/commands/apicurio.py +84 -0
- modelable/commands/codegen.py +241 -0
- modelable/commands/common.py +43 -0
- modelable/commands/compile.py +237 -0
- modelable/commands/create.py +164 -0
- modelable/commands/diff.py +82 -0
- modelable/commands/graph.py +53 -0
- modelable/commands/llm.py +564 -0
- modelable/commands/lsp.py +15 -0
- modelable/commands/runtime.py +37 -0
- modelable/commands/scenario.py +104 -0
- modelable/commands/spec.py +197 -0
- modelable/commands/workspace.py +240 -0
- modelable/compat/__init__.py +11 -0
- modelable/compat/checker.py +179 -0
- modelable/compat/diff.py +169 -0
- modelable/compiler/__init__.py +3 -0
- modelable/compiler/compiler.py +19 -0
- modelable/compiler/workspace.py +346 -0
- modelable/diagnostics/__init__.py +3 -0
- modelable/diagnostics/model.py +27 -0
- modelable/emitters/__init__.py +0 -0
- modelable/emitters/base.py +22 -0
- modelable/emitters/csharp.py +245 -0
- modelable/emitters/dbt_yaml.py +290 -0
- modelable/emitters/diagnostics.py +25 -0
- modelable/emitters/fhir.py +694 -0
- modelable/emitters/fhir_validator.py +36 -0
- modelable/emitters/go.py +334 -0
- modelable/emitters/java.py +264 -0
- modelable/emitters/json_schema.py +458 -0
- modelable/emitters/markdown.py +252 -0
- modelable/emitters/odcs.py +355 -0
- modelable/emitters/openlineage.py +315 -0
- modelable/emitters/openmetadata.py +258 -0
- modelable/emitters/python.py +282 -0
- modelable/emitters/rust.py +643 -0
- modelable/emitters/shapes.py +261 -0
- modelable/emitters/sql.py +266 -0
- modelable/emitters/targets.py +141 -0
- modelable/emitters/typescript.py +352 -0
- modelable/expressions/__init__.py +0 -0
- modelable/expressions/cel.py +547 -0
- modelable/governance/__init__.py +3 -0
- modelable/governance/checker.py +271 -0
- modelable/governance/por.py +46 -0
- modelable/grammar/__init__.py +1 -0
- modelable/grammar/modelable.lark +257 -0
- modelable/graph/__init__.py +5 -0
- modelable/graph/export.py +442 -0
- modelable/llm/__init__.py +43 -0
- modelable/llm/chat.py +255 -0
- modelable/llm/config.py +87 -0
- modelable/llm/context.py +194 -0
- modelable/llm/engine.py +976 -0
- modelable/llm/importers.py +1077 -0
- modelable/llm/provenance.py +84 -0
- modelable/llm/providers.py +182 -0
- modelable/llm/qa.py +126 -0
- modelable/llm/recommendations.py +33 -0
- modelable/llm/redaction.py +19 -0
- modelable/llm/render.py +279 -0
- modelable/llm/update_plan.py +101 -0
- modelable/llm/validation_help.py +10 -0
- modelable/lsp/__init__.py +3 -0
- modelable/lsp/__main__.py +4 -0
- modelable/lsp/code_actions.py +210 -0
- modelable/lsp/completion.py +480 -0
- modelable/lsp/definition.py +343 -0
- modelable/lsp/diagnostics.py +31 -0
- modelable/lsp/document_symbols.py +197 -0
- modelable/lsp/federation.py +261 -0
- modelable/lsp/folding.py +33 -0
- modelable/lsp/formatting.py +64 -0
- modelable/lsp/highlight.py +30 -0
- modelable/lsp/hover.py +370 -0
- modelable/lsp/inlay_hints.py +158 -0
- modelable/lsp/references.py +511 -0
- modelable/lsp/rename.py +564 -0
- modelable/lsp/semantic_tokens.py +412 -0
- modelable/lsp/server.py +370 -0
- modelable/lsp/workspace.py +83 -0
- modelable/lsp/workspace_symbols.py +104 -0
- modelable/parser/__init__.py +94 -0
- modelable/parser/ir.py +451 -0
- modelable/parser/parse.py +47 -0
- modelable/parser/transformer.py +798 -0
- modelable/parser/wire.py +68 -0
- modelable/planner/__init__.py +0 -0
- modelable/planner/lineage.py +91 -0
- modelable/planner/planner.py +134 -0
- modelable/planner/plans.py +122 -0
- modelable/py.typed +0 -0
- modelable/registry/__init__.py +9 -0
- modelable/registry/apicurio.py +166 -0
- modelable/registry/base.py +18 -0
- modelable/registry/factory.py +18 -0
- modelable/registry/index.py +419 -0
- modelable/registry/local.py +26 -0
- modelable/registry/oci.py +22 -0
- modelable/registry/resolver.py +213 -0
- modelable/registry/schema.sql +119 -0
- modelable/registry/signature.py +26 -0
- modelable/release.py +125 -0
- modelable/runtime/__init__.py +5 -0
- modelable/runtime/adapter/__init__.py +17 -0
- modelable/runtime/adapter/base.py +18 -0
- modelable/runtime/adapter/postgres.py +82 -0
- modelable/specs/__init__.py +23 -0
- modelable/specs/tracking.py +220 -0
- modelable/validation/__init__.py +3 -0
- modelable/validation/semantic.py +659 -0
- modelable-1.0.0.dist-info/METADATA +61 -0
- modelable-1.0.0.dist-info/RECORD +122 -0
- modelable-1.0.0.dist-info/WHEEL +4 -0
- modelable-1.0.0.dist-info/entry_points.txt +2 -0
- modelable-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from lark import Transformer
|
|
6
|
+
|
|
7
|
+
from modelable.parser.ir import (
|
|
8
|
+
AccessBlock,
|
|
9
|
+
AccessGrant,
|
|
10
|
+
AiConfig,
|
|
11
|
+
AnnClassification,
|
|
12
|
+
AnnCustom,
|
|
13
|
+
AnnDeprecated,
|
|
14
|
+
AnnKey,
|
|
15
|
+
AnnLatestBefore,
|
|
16
|
+
AnnLatestOnly,
|
|
17
|
+
AnnOwner,
|
|
18
|
+
AnnPii,
|
|
19
|
+
AnnPitCutoff,
|
|
20
|
+
AnnServer,
|
|
21
|
+
AnnWire,
|
|
22
|
+
ArrayType,
|
|
23
|
+
AutoProjectionDecl,
|
|
24
|
+
AutoProjectionTarget,
|
|
25
|
+
BindingDef,
|
|
26
|
+
ChangeKind,
|
|
27
|
+
ComputedMapping,
|
|
28
|
+
DecimalType,
|
|
29
|
+
DirectMapping,
|
|
30
|
+
DomainDef,
|
|
31
|
+
EnumType,
|
|
32
|
+
FieldDef,
|
|
33
|
+
FieldMapping,
|
|
34
|
+
FieldType,
|
|
35
|
+
GenerateTarget,
|
|
36
|
+
JoinRef,
|
|
37
|
+
MapType,
|
|
38
|
+
MdlFile,
|
|
39
|
+
ModelKind,
|
|
40
|
+
ModelVersion,
|
|
41
|
+
NamedType,
|
|
42
|
+
ObjectType,
|
|
43
|
+
PrimitiveType,
|
|
44
|
+
ProjectionField,
|
|
45
|
+
ProjectionVersion,
|
|
46
|
+
RefType,
|
|
47
|
+
SourceRef,
|
|
48
|
+
VersionExact,
|
|
49
|
+
VersionMin,
|
|
50
|
+
VersionPinned,
|
|
51
|
+
VersionRange,
|
|
52
|
+
WireTargetHint,
|
|
53
|
+
WorkspaceDef,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
ANNOTATION_TYPES = (
|
|
57
|
+
AnnKey,
|
|
58
|
+
AnnPii,
|
|
59
|
+
AnnClassification,
|
|
60
|
+
AnnDeprecated,
|
|
61
|
+
AnnOwner,
|
|
62
|
+
AnnServer,
|
|
63
|
+
AnnWire,
|
|
64
|
+
AnnPitCutoff,
|
|
65
|
+
AnnLatestBefore,
|
|
66
|
+
AnnLatestOnly,
|
|
67
|
+
AnnCustom,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _str(value: object) -> str:
|
|
72
|
+
text = str(value)
|
|
73
|
+
if len(text) >= 2 and text[0] == '"' and text[-1] == '"':
|
|
74
|
+
return text[1:-1]
|
|
75
|
+
return text
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class MdlTransformer(Transformer[list[object], Any]):
|
|
79
|
+
def start(self, items: list[object]) -> MdlFile:
|
|
80
|
+
domains: list[DomainDef] = []
|
|
81
|
+
bindings: list[BindingDef] = []
|
|
82
|
+
workspace: WorkspaceDef | None = None
|
|
83
|
+
for item in items:
|
|
84
|
+
if isinstance(item, DomainDef):
|
|
85
|
+
domains.append(item)
|
|
86
|
+
elif isinstance(item, BindingDef):
|
|
87
|
+
bindings.append(item)
|
|
88
|
+
elif isinstance(item, WorkspaceDef):
|
|
89
|
+
workspace = item
|
|
90
|
+
return MdlFile(domains=domains, bindings=bindings, workspace=workspace)
|
|
91
|
+
|
|
92
|
+
def statement(self, items: list[object]) -> object:
|
|
93
|
+
return items[0]
|
|
94
|
+
|
|
95
|
+
def domain_decl(self, items: list[object]) -> DomainDef:
|
|
96
|
+
name = _str(items[0])
|
|
97
|
+
owner = None
|
|
98
|
+
contact = None
|
|
99
|
+
description = None
|
|
100
|
+
models: dict[str, list[ModelVersion]] = {}
|
|
101
|
+
projections: dict[str, list[ProjectionVersion]] = {}
|
|
102
|
+
auto_projections: list[AutoProjectionDecl] = []
|
|
103
|
+
generate_targets: list[GenerateTarget] = []
|
|
104
|
+
|
|
105
|
+
for tag, value in [item for item in items[1:] if isinstance(item, tuple)]:
|
|
106
|
+
if tag == "owner":
|
|
107
|
+
owner = value
|
|
108
|
+
elif tag == "contact":
|
|
109
|
+
contact = value
|
|
110
|
+
elif tag == "description":
|
|
111
|
+
description = value
|
|
112
|
+
elif tag == "model":
|
|
113
|
+
model_name, model_version = value
|
|
114
|
+
models.setdefault(model_name, []).append(model_version)
|
|
115
|
+
elif tag == "projection":
|
|
116
|
+
projection_name, projection_version = value
|
|
117
|
+
projections.setdefault(projection_name, []).append(projection_version)
|
|
118
|
+
elif tag == "auto_projection":
|
|
119
|
+
auto_projections.append(value)
|
|
120
|
+
elif tag == "generate":
|
|
121
|
+
generate_targets = value
|
|
122
|
+
|
|
123
|
+
return DomainDef(
|
|
124
|
+
name=name,
|
|
125
|
+
owner=owner,
|
|
126
|
+
contact=contact,
|
|
127
|
+
description=description,
|
|
128
|
+
models=models,
|
|
129
|
+
projections=projections,
|
|
130
|
+
auto_projections=auto_projections,
|
|
131
|
+
generate_targets=generate_targets,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def domain_name(self, items: list[object]) -> str:
|
|
135
|
+
return _str(items[0])
|
|
136
|
+
|
|
137
|
+
def domain_item(self, items: list[object]) -> object:
|
|
138
|
+
return items[0]
|
|
139
|
+
|
|
140
|
+
def owner_attr(self, items: list[object]) -> tuple[str, str]:
|
|
141
|
+
return ("owner", _str(items[0]))
|
|
142
|
+
|
|
143
|
+
def contact_attr(self, items: list[object]) -> tuple[str, str]:
|
|
144
|
+
return ("contact", _str(items[0]))
|
|
145
|
+
|
|
146
|
+
def desc_attr(self, items: list[object]) -> tuple[str, str]:
|
|
147
|
+
return ("description", _str(items[0]))
|
|
148
|
+
|
|
149
|
+
def model_decl(self, items: list[object]) -> tuple[str, tuple[str, ModelVersion]]:
|
|
150
|
+
annotations = [item for item in items if isinstance(item, ANNOTATION_TYPES)]
|
|
151
|
+
items = [item for item in items if not isinstance(item, ANNOTATION_TYPES)]
|
|
152
|
+
name = str(items[1])
|
|
153
|
+
header = items[2] if len(items) > 2 and isinstance(items[2], tuple) and items[2][0] == "model_header" else None
|
|
154
|
+
body_start = 3 if header is not None else 2
|
|
155
|
+
version = header[1] if header is not None else 0
|
|
156
|
+
change_kind = header[2] if header is not None else ChangeKind.additive
|
|
157
|
+
has_change_kind = header[3] if header is not None else False
|
|
158
|
+
access = next((item for item in items[body_start:] if isinstance(item, AccessBlock)), None)
|
|
159
|
+
model_kind = items[0] if isinstance(items[0], ModelKind) else ModelKind.entity
|
|
160
|
+
model_version = ModelVersion(
|
|
161
|
+
model_kind=model_kind,
|
|
162
|
+
version=int(version) if isinstance(version, (int, str)) else 0,
|
|
163
|
+
change_kind=change_kind if isinstance(change_kind, ChangeKind) else ChangeKind.additive,
|
|
164
|
+
fields=[item for item in items[body_start:] if isinstance(item, FieldDef)],
|
|
165
|
+
access=access,
|
|
166
|
+
has_version_header=header is not None,
|
|
167
|
+
has_change_kind=has_change_kind,
|
|
168
|
+
annotations=annotations,
|
|
169
|
+
)
|
|
170
|
+
return ("model", (name, model_version))
|
|
171
|
+
|
|
172
|
+
def model_header(self, items: list[object]) -> tuple[str, int, ChangeKind, bool]:
|
|
173
|
+
if len(items) == 1 and isinstance(items[0], tuple):
|
|
174
|
+
h = items[0]
|
|
175
|
+
return (
|
|
176
|
+
"model_header",
|
|
177
|
+
int(h[1]) if len(h) > 1 else 0,
|
|
178
|
+
h[2] if len(h) > 2 and isinstance(h[2], ChangeKind) else ChangeKind.additive,
|
|
179
|
+
True,
|
|
180
|
+
)
|
|
181
|
+
if len(items) == 2:
|
|
182
|
+
v = int(items[0]) if isinstance(items[0], (int, str)) else 0
|
|
183
|
+
ck = items[1] if isinstance(items[1], ChangeKind) else ChangeKind.additive
|
|
184
|
+
return ("model_header", v, ck, True)
|
|
185
|
+
return ("model_header", 0, ChangeKind.additive, False)
|
|
186
|
+
|
|
187
|
+
def model_change(self, items: list[object]) -> object:
|
|
188
|
+
return items[0]
|
|
189
|
+
|
|
190
|
+
def model_body_item(self, items: list[object]) -> object:
|
|
191
|
+
return items[0]
|
|
192
|
+
|
|
193
|
+
def mk_entity(self, _items: list[object]) -> ModelKind:
|
|
194
|
+
return ModelKind.entity
|
|
195
|
+
|
|
196
|
+
def mk_aggregate(self, _items: list[object]) -> ModelKind:
|
|
197
|
+
return ModelKind.aggregate
|
|
198
|
+
|
|
199
|
+
def mk_event(self, _items: list[object]) -> ModelKind:
|
|
200
|
+
return ModelKind.event
|
|
201
|
+
|
|
202
|
+
def mk_value(self, _items: list[object]) -> ModelKind:
|
|
203
|
+
return ModelKind.value
|
|
204
|
+
|
|
205
|
+
def ck_additive(self, _items: list[object]) -> ChangeKind:
|
|
206
|
+
return ChangeKind.additive
|
|
207
|
+
|
|
208
|
+
def ck_breaking(self, _items: list[object]) -> ChangeKind:
|
|
209
|
+
return ChangeKind.breaking
|
|
210
|
+
|
|
211
|
+
def field_decl(self, items: list[object]) -> FieldDef:
|
|
212
|
+
annotations = [item for item in items if isinstance(item, ANNOTATION_TYPES)]
|
|
213
|
+
rest = [item for item in items if not isinstance(item, ANNOTATION_TYPES)]
|
|
214
|
+
default = next((item[1] for item in rest if isinstance(item, tuple) and item[0] == "default"), None)
|
|
215
|
+
type_item = next(
|
|
216
|
+
(
|
|
217
|
+
item
|
|
218
|
+
for item in rest
|
|
219
|
+
if not isinstance(item, str) and not (isinstance(item, tuple) and item[0] == "default")
|
|
220
|
+
),
|
|
221
|
+
None,
|
|
222
|
+
)
|
|
223
|
+
return FieldDef(
|
|
224
|
+
name=str(rest[0]),
|
|
225
|
+
optional=any(item == "?" for item in rest),
|
|
226
|
+
type=type_item
|
|
227
|
+
if isinstance(
|
|
228
|
+
type_item, (PrimitiveType, DecimalType, ArrayType, MapType, RefType, EnumType, ObjectType, NamedType)
|
|
229
|
+
)
|
|
230
|
+
else PrimitiveType(kind="string"),
|
|
231
|
+
default=default,
|
|
232
|
+
annotations=annotations,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def optional_marker(self, _items: list[object]) -> str:
|
|
236
|
+
return "?"
|
|
237
|
+
|
|
238
|
+
def field_default(self, items: list[object]) -> tuple[str, str]:
|
|
239
|
+
return ("default", str(items[0]).strip())
|
|
240
|
+
|
|
241
|
+
def ann_key(self, _items: list[object]) -> AnnKey:
|
|
242
|
+
return AnnKey()
|
|
243
|
+
|
|
244
|
+
def ann_pii(self, _items: list[object]) -> AnnPii:
|
|
245
|
+
return AnnPii()
|
|
246
|
+
|
|
247
|
+
def ann_classification(self, items: list[object]) -> AnnClassification:
|
|
248
|
+
return AnnClassification(level=_str(items[0]))
|
|
249
|
+
|
|
250
|
+
def ann_deprecated(self, items: list[object]) -> AnnDeprecated:
|
|
251
|
+
return AnnDeprecated(replaced_by=_str(items[0]))
|
|
252
|
+
|
|
253
|
+
def ann_owner(self, items: list[object]) -> AnnOwner:
|
|
254
|
+
return AnnOwner(team=_str(items[0]))
|
|
255
|
+
|
|
256
|
+
def ann_server(self, _items: list[object]) -> AnnServer:
|
|
257
|
+
return AnnServer()
|
|
258
|
+
|
|
259
|
+
def ann_wire(self, items: list[object]) -> AnnWire:
|
|
260
|
+
targets: dict[str, WireTargetHint] = {}
|
|
261
|
+
for target, modifier, value in items:
|
|
262
|
+
hint = targets.get(target, WireTargetHint())
|
|
263
|
+
if modifier is None:
|
|
264
|
+
if hint.encoding is not None and hint.encoding != value:
|
|
265
|
+
raise ValueError(
|
|
266
|
+
f"conflicting wire encodings for target '{target}': {hint.encoding!r} vs {value!r}"
|
|
267
|
+
)
|
|
268
|
+
hint.encoding = value
|
|
269
|
+
elif modifier == "type":
|
|
270
|
+
if hint.type is not None and hint.type != value:
|
|
271
|
+
raise ValueError(f"conflicting wire types for target '{target}': {hint.type!r} vs {value!r}")
|
|
272
|
+
hint.type = value
|
|
273
|
+
elif modifier == "case":
|
|
274
|
+
if hint.case is not None and hint.case != value:
|
|
275
|
+
raise ValueError(f"conflicting wire cases for target '{target}': {hint.case!r} vs {value!r}")
|
|
276
|
+
hint.case = value
|
|
277
|
+
elif modifier == "overrides":
|
|
278
|
+
overlap = sorted(set(hint.overrides) & set(value))
|
|
279
|
+
for key in overlap:
|
|
280
|
+
if hint.overrides[key] != value[key]:
|
|
281
|
+
raise ValueError(
|
|
282
|
+
f"conflicting wire override for target '{target}' member '{key}': "
|
|
283
|
+
f"{hint.overrides[key]!r} vs {value[key]!r}"
|
|
284
|
+
)
|
|
285
|
+
hint.overrides.update(value)
|
|
286
|
+
elif modifier == "fieldCase":
|
|
287
|
+
if hint.field_case is not None and hint.field_case != value:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
f"conflicting wire field cases for target '{target}': {hint.field_case!r} vs {value!r}"
|
|
290
|
+
)
|
|
291
|
+
hint.field_case = value
|
|
292
|
+
else:
|
|
293
|
+
raise ValueError(f"unsupported wire modifier: {modifier}")
|
|
294
|
+
targets[target] = hint
|
|
295
|
+
return AnnWire(targets=targets)
|
|
296
|
+
|
|
297
|
+
def ann_pit_cutoff(self, items: list[object]) -> AnnPitCutoff:
|
|
298
|
+
return AnnPitCutoff(expression=str(items[0]).strip())
|
|
299
|
+
|
|
300
|
+
def ann_latest_before(self, items: list[object]) -> AnnLatestBefore:
|
|
301
|
+
return AnnLatestBefore(expression=str(items[0]).strip())
|
|
302
|
+
|
|
303
|
+
def ann_latest_only(self, _items: list[object]) -> AnnLatestOnly:
|
|
304
|
+
return AnnLatestOnly()
|
|
305
|
+
|
|
306
|
+
def ann_custom(self, items: list[object]) -> AnnCustom:
|
|
307
|
+
name = str(items[0])
|
|
308
|
+
expression = str(items[1]).strip() if len(items) > 1 else None
|
|
309
|
+
return AnnCustom(name=name, expression=expression)
|
|
310
|
+
|
|
311
|
+
def annotation(self, items: list[object]) -> object:
|
|
312
|
+
return items[0]
|
|
313
|
+
|
|
314
|
+
def wire_option(self, items: list[object]) -> tuple[object, object, object]:
|
|
315
|
+
target, modifier = items[0]
|
|
316
|
+
return target, modifier, items[1]
|
|
317
|
+
|
|
318
|
+
def wire_key(self, items: list[object]) -> tuple[str, str | None]:
|
|
319
|
+
if len(items) == 1:
|
|
320
|
+
return str(items[0]), None
|
|
321
|
+
return str(items[0]), str(items[1])
|
|
322
|
+
|
|
323
|
+
def wire_string(self, items: list[object]) -> str:
|
|
324
|
+
return _str(items[0])
|
|
325
|
+
|
|
326
|
+
def wire_value(self, items: list[object]) -> object:
|
|
327
|
+
return items[0]
|
|
328
|
+
|
|
329
|
+
def wire_map(self, items: list[object]) -> dict[str, object]:
|
|
330
|
+
return dict(items)
|
|
331
|
+
|
|
332
|
+
def wire_map_item(self, items: list[object]) -> tuple[str, str]:
|
|
333
|
+
return str(items[0]), _str(items[1])
|
|
334
|
+
|
|
335
|
+
def type_expr(self, items: list[object]) -> FieldType:
|
|
336
|
+
item = items[0]
|
|
337
|
+
if isinstance(item, str):
|
|
338
|
+
return NamedType(name=item)
|
|
339
|
+
return item # type: ignore[return-value]
|
|
340
|
+
|
|
341
|
+
def pt_string(self, _items: list[object]) -> PrimitiveType:
|
|
342
|
+
return PrimitiveType(kind="string")
|
|
343
|
+
|
|
344
|
+
def pt_int(self, _items: list[object]) -> PrimitiveType:
|
|
345
|
+
return PrimitiveType(kind="int")
|
|
346
|
+
|
|
347
|
+
def pt_float(self, _items: list[object]) -> PrimitiveType:
|
|
348
|
+
return PrimitiveType(kind="float")
|
|
349
|
+
|
|
350
|
+
def pt_bool(self, _items: list[object]) -> PrimitiveType:
|
|
351
|
+
return PrimitiveType(kind="bool")
|
|
352
|
+
|
|
353
|
+
def pt_date(self, _items: list[object]) -> PrimitiveType:
|
|
354
|
+
return PrimitiveType(kind="date")
|
|
355
|
+
|
|
356
|
+
def pt_time(self, _items: list[object]) -> PrimitiveType:
|
|
357
|
+
return PrimitiveType(kind="time")
|
|
358
|
+
|
|
359
|
+
def pt_timestamp(self, _items: list[object]) -> PrimitiveType:
|
|
360
|
+
return PrimitiveType(kind="timestamp")
|
|
361
|
+
|
|
362
|
+
def pt_uuid(self, _items: list[object]) -> PrimitiveType:
|
|
363
|
+
return PrimitiveType(kind="uuid")
|
|
364
|
+
|
|
365
|
+
def pt_duration(self, _items: list[object]) -> PrimitiveType:
|
|
366
|
+
return PrimitiveType(kind="duration")
|
|
367
|
+
|
|
368
|
+
def pt_binary(self, _items: list[object]) -> PrimitiveType:
|
|
369
|
+
return PrimitiveType(kind="binary")
|
|
370
|
+
|
|
371
|
+
def pt_json(self, _items: list[object]) -> PrimitiveType:
|
|
372
|
+
return PrimitiveType(kind="json")
|
|
373
|
+
|
|
374
|
+
def primitive_type(self, items: list[object]) -> object:
|
|
375
|
+
return items[0]
|
|
376
|
+
|
|
377
|
+
def decimal_type(self, items: list[object]) -> DecimalType:
|
|
378
|
+
return DecimalType(precision=int(items[0]), scale=int(items[1]))
|
|
379
|
+
|
|
380
|
+
def enum_type(self, items: list[object]) -> EnumType:
|
|
381
|
+
return EnumType(values=[str(item) for item in items])
|
|
382
|
+
|
|
383
|
+
def array_type(self, items: list[object]) -> ArrayType:
|
|
384
|
+
return ArrayType(item=items[0])
|
|
385
|
+
|
|
386
|
+
def map_type(self, items: list[object]) -> MapType:
|
|
387
|
+
return MapType(key=items[0], value=items[1])
|
|
388
|
+
|
|
389
|
+
def ref_type(self, items: list[object]) -> RefType:
|
|
390
|
+
return RefType(target=str(items[0]))
|
|
391
|
+
|
|
392
|
+
def object_type(self, items: list[object]) -> ObjectType:
|
|
393
|
+
return ObjectType(fields=[item for item in items if isinstance(item, FieldDef)])
|
|
394
|
+
|
|
395
|
+
def dotted_ref(self, items: list[object]) -> str:
|
|
396
|
+
return ".".join(str(item) for item in items)
|
|
397
|
+
|
|
398
|
+
def IDENT(self, token: object) -> str: # noqa: N802
|
|
399
|
+
return str(token)
|
|
400
|
+
|
|
401
|
+
def projection_decl(self, items: list[object]) -> tuple[str, tuple[str, ProjectionVersion]]:
|
|
402
|
+
annotations = [item for item in items if isinstance(item, ANNOTATION_TYPES)]
|
|
403
|
+
items = [item for item in items if not isinstance(item, ANNOTATION_TYPES)]
|
|
404
|
+
source_index = next(
|
|
405
|
+
(
|
|
406
|
+
i
|
|
407
|
+
for i, item in enumerate(items[2:], start=2)
|
|
408
|
+
if isinstance(item, tuple) and len(item) == 4 and isinstance(item[0], SourceRef)
|
|
409
|
+
),
|
|
410
|
+
None,
|
|
411
|
+
)
|
|
412
|
+
if source_index is None:
|
|
413
|
+
source = SourceRef(model="", version=VersionExact(version=0), alias="", where=None)
|
|
414
|
+
joins: list[JoinRef] = []
|
|
415
|
+
where = None
|
|
416
|
+
group_by: list[str] = []
|
|
417
|
+
body_start = 2
|
|
418
|
+
else:
|
|
419
|
+
source, joins, where, group_by = items[source_index]
|
|
420
|
+
body_start = source_index + 1
|
|
421
|
+
access = next((item for item in items[body_start:] if isinstance(item, AccessBlock)), None)
|
|
422
|
+
projection_version = ProjectionVersion(
|
|
423
|
+
version=int(items[1]),
|
|
424
|
+
source=source,
|
|
425
|
+
joins=joins,
|
|
426
|
+
where=where,
|
|
427
|
+
group_by=group_by,
|
|
428
|
+
fields=[item for item in items[body_start:] if isinstance(item, ProjectionField)],
|
|
429
|
+
access=access,
|
|
430
|
+
annotations=annotations,
|
|
431
|
+
)
|
|
432
|
+
return ("projection", (str(items[0]), projection_version))
|
|
433
|
+
|
|
434
|
+
def join_prefix(self, items):
|
|
435
|
+
if len(items) == 5:
|
|
436
|
+
return ("join", "left", str(items[1]), items[2], str(items[3]), str(items[4]).strip())
|
|
437
|
+
return ("join", "inner", str(items[0]), items[1], str(items[2]), str(items[3]).strip())
|
|
438
|
+
|
|
439
|
+
def projection_body_item(self, items):
|
|
440
|
+
return items[0]
|
|
441
|
+
|
|
442
|
+
def projection_source_block(self, items):
|
|
443
|
+
return items[0]
|
|
444
|
+
|
|
445
|
+
def source_clause(self, items):
|
|
446
|
+
joins = [item for item in items[3:] if isinstance(item, JoinRef)]
|
|
447
|
+
where = next((item for item in items[3:] if isinstance(item, str)), None)
|
|
448
|
+
group_by = next((item for item in items[3:] if isinstance(item, list)), [])
|
|
449
|
+
return (
|
|
450
|
+
SourceRef(model=str(items[0]), version=items[1], alias=str(items[2]), where=where),
|
|
451
|
+
joins,
|
|
452
|
+
where,
|
|
453
|
+
group_by,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
def join_clause(self, items):
|
|
457
|
+
prefix = items[0]
|
|
458
|
+
annotations = [item for item in items[1:] if isinstance(item, ANNOTATION_TYPES)]
|
|
459
|
+
cardinality = next(
|
|
460
|
+
(item[1] for item in items[1:] if isinstance(item, tuple) and item[0] == "cardinality"), None
|
|
461
|
+
)
|
|
462
|
+
return JoinRef(
|
|
463
|
+
model=str(prefix[2]),
|
|
464
|
+
version=prefix[3],
|
|
465
|
+
alias=str(prefix[4]),
|
|
466
|
+
on=prefix[5],
|
|
467
|
+
join_kind=prefix[1],
|
|
468
|
+
cardinality=cardinality,
|
|
469
|
+
annotations=annotations,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def where_clause(self, items):
|
|
473
|
+
return str(items[0]).strip()
|
|
474
|
+
|
|
475
|
+
def join_modifier(self, _items):
|
|
476
|
+
return "left"
|
|
477
|
+
|
|
478
|
+
def cardinality_attr(self, items):
|
|
479
|
+
return ("cardinality", str(items[0]))
|
|
480
|
+
|
|
481
|
+
def group_clause(self, items):
|
|
482
|
+
return [str(item) for item in items]
|
|
483
|
+
|
|
484
|
+
def group_item(self, items):
|
|
485
|
+
return str(items[0]).strip()
|
|
486
|
+
|
|
487
|
+
def version_spec(self, items):
|
|
488
|
+
return items[0]
|
|
489
|
+
|
|
490
|
+
def version_exact(self, items):
|
|
491
|
+
return VersionExact(version=int(items[0]))
|
|
492
|
+
|
|
493
|
+
def version_pinned(self, items):
|
|
494
|
+
return VersionPinned(version=int(items[0]), content_hash=str(items[1]))
|
|
495
|
+
|
|
496
|
+
def version_range(self, items):
|
|
497
|
+
return VersionRange(min_inclusive=int(items[0]), max_exclusive=int(items[1]))
|
|
498
|
+
|
|
499
|
+
def version_min(self, items):
|
|
500
|
+
return VersionMin(min_inclusive=int(items[0]))
|
|
501
|
+
|
|
502
|
+
def qualified_field(self, items):
|
|
503
|
+
return f"{items[0]}.{items[1]}"
|
|
504
|
+
|
|
505
|
+
def direct_field(self, items):
|
|
506
|
+
annotations = [item for item in items if isinstance(item, ANNOTATION_TYPES)]
|
|
507
|
+
rest = [item for item in items if not isinstance(item, ANNOTATION_TYPES)]
|
|
508
|
+
source_alias, source_field = str(rest[1]).split(".", 1)
|
|
509
|
+
return ProjectionField(
|
|
510
|
+
name=str(rest[0]),
|
|
511
|
+
mapping=DirectMapping(source_alias=source_alias, source_field=source_field),
|
|
512
|
+
annotations=annotations,
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
def computed_field(self, items):
|
|
516
|
+
annotations = [item for item in items if isinstance(item, ANNOTATION_TYPES)]
|
|
517
|
+
rest = [item for item in items if not isinstance(item, ANNOTATION_TYPES)]
|
|
518
|
+
return ProjectionField(
|
|
519
|
+
name=str(rest[0]),
|
|
520
|
+
mapping=ComputedMapping(expression=str(rest[1]).strip()),
|
|
521
|
+
annotations=annotations,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def proj_field(self, items):
|
|
525
|
+
return items[0]
|
|
526
|
+
|
|
527
|
+
def auto_projections_decl(self, items):
|
|
528
|
+
return (
|
|
529
|
+
"auto_projection",
|
|
530
|
+
AutoProjectionDecl(
|
|
531
|
+
model=str(items[0]),
|
|
532
|
+
version=int(items[1]),
|
|
533
|
+
targets=[item for item in items[2:] if isinstance(item, AutoProjectionTarget)],
|
|
534
|
+
),
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
def access_block(self, items):
|
|
538
|
+
entity = []
|
|
539
|
+
properties: dict[str, list[AccessGrant]] = {}
|
|
540
|
+
for item in items:
|
|
541
|
+
if not isinstance(item, tuple):
|
|
542
|
+
continue
|
|
543
|
+
if item[0] == "entity":
|
|
544
|
+
entity.append(item[1])
|
|
545
|
+
elif item[0] == "property":
|
|
546
|
+
field_name, grant = item[1]
|
|
547
|
+
properties.setdefault(field_name, []).append(grant)
|
|
548
|
+
return AccessBlock(entity=entity, properties=properties)
|
|
549
|
+
|
|
550
|
+
def entity_grant(self, items):
|
|
551
|
+
return ("entity", AccessGrant(principal=str(items[0]), permissions=list(items[1])))
|
|
552
|
+
|
|
553
|
+
def property_grant(self, items):
|
|
554
|
+
field_name = str(items[0])
|
|
555
|
+
return (
|
|
556
|
+
"property",
|
|
557
|
+
(
|
|
558
|
+
field_name,
|
|
559
|
+
AccessGrant(principal=str(items[1]), permissions=list(items[2])),
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def access_item(self, items):
|
|
564
|
+
return items[0]
|
|
565
|
+
|
|
566
|
+
def principal(self, items):
|
|
567
|
+
return str(items[0])
|
|
568
|
+
|
|
569
|
+
def permission_list(self, items):
|
|
570
|
+
return [str(item) for item in items]
|
|
571
|
+
|
|
572
|
+
def p_read(self, _items):
|
|
573
|
+
return "read"
|
|
574
|
+
|
|
575
|
+
def p_project(self, _items):
|
|
576
|
+
return "project"
|
|
577
|
+
|
|
578
|
+
def p_subscribe(self, _items):
|
|
579
|
+
return "subscribe"
|
|
580
|
+
|
|
581
|
+
def p_write(self, _items):
|
|
582
|
+
return "write"
|
|
583
|
+
|
|
584
|
+
def p_transfer(self, _items):
|
|
585
|
+
return "transfer"
|
|
586
|
+
|
|
587
|
+
def p_manage_access(self, _items):
|
|
588
|
+
return "manage_access"
|
|
589
|
+
|
|
590
|
+
def p_derive(self, _items):
|
|
591
|
+
return "derive"
|
|
592
|
+
|
|
593
|
+
def p_redact(self, _items):
|
|
594
|
+
return "redact"
|
|
595
|
+
|
|
596
|
+
def auto_projection_item(self, items):
|
|
597
|
+
kind = items[0]
|
|
598
|
+
excluded_fields = []
|
|
599
|
+
excluded_annotations = []
|
|
600
|
+
operations = []
|
|
601
|
+
for option in items[1:]:
|
|
602
|
+
if option is None:
|
|
603
|
+
continue
|
|
604
|
+
opt_kind, opt_values = option
|
|
605
|
+
if opt_kind == "exclude":
|
|
606
|
+
for val in opt_values:
|
|
607
|
+
if isinstance(val, str):
|
|
608
|
+
excluded_fields.append(val)
|
|
609
|
+
else:
|
|
610
|
+
excluded_annotations.append(val)
|
|
611
|
+
elif opt_kind == "on":
|
|
612
|
+
operations.extend(opt_values)
|
|
613
|
+
return AutoProjectionTarget(
|
|
614
|
+
kind=kind,
|
|
615
|
+
excluded_fields=excluded_fields,
|
|
616
|
+
excluded_annotations=excluded_annotations,
|
|
617
|
+
operations=operations,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
def auto_projection_kind(self, items):
|
|
621
|
+
return items[0]
|
|
622
|
+
|
|
623
|
+
def apk_db(self, _items):
|
|
624
|
+
return "db"
|
|
625
|
+
|
|
626
|
+
def apk_request(self, _items):
|
|
627
|
+
return "request"
|
|
628
|
+
|
|
629
|
+
def apk_reply(self, _items):
|
|
630
|
+
return "reply"
|
|
631
|
+
|
|
632
|
+
def apk_event(self, _items):
|
|
633
|
+
return "event"
|
|
634
|
+
|
|
635
|
+
def auto_projection_option(self, items):
|
|
636
|
+
return items[0]
|
|
637
|
+
|
|
638
|
+
def exclude_option(self, items):
|
|
639
|
+
return ("exclude", [item for item in items if item is not None])
|
|
640
|
+
|
|
641
|
+
def on_option(self, items):
|
|
642
|
+
return ("on", [str(item) for item in items if item is not None])
|
|
643
|
+
|
|
644
|
+
def auto_projection_exclusion(self, items):
|
|
645
|
+
return items[0]
|
|
646
|
+
|
|
647
|
+
def generate_block(self, items):
|
|
648
|
+
return ("generate", [item for item in items if isinstance(item, GenerateTarget)])
|
|
649
|
+
|
|
650
|
+
def generate_target(self, items):
|
|
651
|
+
target = items[0]
|
|
652
|
+
output_path = _str(items[1]) if len(items) > 1 else None
|
|
653
|
+
if isinstance(target, tuple):
|
|
654
|
+
name, dialect = target
|
|
655
|
+
else:
|
|
656
|
+
name, dialect = target, None
|
|
657
|
+
return GenerateTarget(name=name, dialect=dialect, output_path=output_path)
|
|
658
|
+
|
|
659
|
+
def target_name(self, items):
|
|
660
|
+
return items[0]
|
|
661
|
+
|
|
662
|
+
def tn_openapi(self, _items):
|
|
663
|
+
return "openapi"
|
|
664
|
+
|
|
665
|
+
def tn_typescript(self, _items):
|
|
666
|
+
return "typescript"
|
|
667
|
+
|
|
668
|
+
def tn_avro(self, _items):
|
|
669
|
+
return "avro"
|
|
670
|
+
|
|
671
|
+
def tn_protobuf(self, _items):
|
|
672
|
+
return "protobuf"
|
|
673
|
+
|
|
674
|
+
def tn_sql(self, items):
|
|
675
|
+
return ("sql", str(items[0]))
|
|
676
|
+
|
|
677
|
+
def tn_jsonschema(self, _items):
|
|
678
|
+
return "jsonschema"
|
|
679
|
+
|
|
680
|
+
def tn_asyncapi(self, _items):
|
|
681
|
+
return "asyncapi"
|
|
682
|
+
|
|
683
|
+
def tn_docs(self, _items):
|
|
684
|
+
return "docs"
|
|
685
|
+
|
|
686
|
+
def db_dialect(self, items):
|
|
687
|
+
return items[0]
|
|
688
|
+
|
|
689
|
+
def dd_postgres(self, _items):
|
|
690
|
+
return "postgres"
|
|
691
|
+
|
|
692
|
+
def dd_mysql(self, _items):
|
|
693
|
+
return "mysql"
|
|
694
|
+
|
|
695
|
+
def dd_clickhouse(self, _items):
|
|
696
|
+
return "clickhouse"
|
|
697
|
+
|
|
698
|
+
def dd_sqlite(self, _items):
|
|
699
|
+
return "sqlite"
|
|
700
|
+
|
|
701
|
+
def binding_decl(self, items):
|
|
702
|
+
name = str(items[0])
|
|
703
|
+
model = ""
|
|
704
|
+
model_version = 0
|
|
705
|
+
adapter = ""
|
|
706
|
+
table = None
|
|
707
|
+
for item in items[1:]:
|
|
708
|
+
if not isinstance(item, tuple):
|
|
709
|
+
continue
|
|
710
|
+
tag, *vals = item
|
|
711
|
+
if tag == "adapter":
|
|
712
|
+
adapter = vals[0]
|
|
713
|
+
elif tag == "model":
|
|
714
|
+
model, model_version = vals[0], vals[1]
|
|
715
|
+
elif tag == "table":
|
|
716
|
+
table = vals[0]
|
|
717
|
+
return BindingDef(name=name, model=model, model_version=model_version, adapter=adapter, table=table)
|
|
718
|
+
|
|
719
|
+
def binding_item(self, items):
|
|
720
|
+
return items[0]
|
|
721
|
+
|
|
722
|
+
def binding_adapter_attr(self, items):
|
|
723
|
+
return ("adapter", str(items[0]))
|
|
724
|
+
|
|
725
|
+
def binding_model_attr(self, items):
|
|
726
|
+
model_fqn = str(items[0])
|
|
727
|
+
version = int(items[1])
|
|
728
|
+
return ("model", model_fqn, version)
|
|
729
|
+
|
|
730
|
+
def binding_table_attr(self, items):
|
|
731
|
+
return ("table", _str(items[0]))
|
|
732
|
+
|
|
733
|
+
def workspace_decl(self, _items):
|
|
734
|
+
label = None
|
|
735
|
+
name = None
|
|
736
|
+
description = None
|
|
737
|
+
generate_targets: list[GenerateTarget] = []
|
|
738
|
+
ai = None
|
|
739
|
+
|
|
740
|
+
for item in _items:
|
|
741
|
+
if isinstance(item, str):
|
|
742
|
+
label = _str(item)
|
|
743
|
+
elif isinstance(item, tuple):
|
|
744
|
+
tag, value = item
|
|
745
|
+
if tag == "name":
|
|
746
|
+
name = value
|
|
747
|
+
elif tag == "description":
|
|
748
|
+
description = value
|
|
749
|
+
elif tag == "generate":
|
|
750
|
+
generate_targets = value
|
|
751
|
+
elif tag == "ai":
|
|
752
|
+
ai = value
|
|
753
|
+
|
|
754
|
+
return WorkspaceDef(
|
|
755
|
+
label=label,
|
|
756
|
+
name=name,
|
|
757
|
+
description=description,
|
|
758
|
+
generate_targets=generate_targets,
|
|
759
|
+
ai=ai,
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
def workspace_item(self, items):
|
|
763
|
+
return items[0]
|
|
764
|
+
|
|
765
|
+
def workspace_label(self, items):
|
|
766
|
+
return _str(items[0])
|
|
767
|
+
|
|
768
|
+
def workspace_name_attr(self, items):
|
|
769
|
+
return ("name", _str(items[0]))
|
|
770
|
+
|
|
771
|
+
def workspace_description_attr(self, items):
|
|
772
|
+
return ("description", _str(items[0]))
|
|
773
|
+
|
|
774
|
+
def ai_block(self, items):
|
|
775
|
+
attrs = dict(items)
|
|
776
|
+
return (
|
|
777
|
+
"ai",
|
|
778
|
+
AiConfig(
|
|
779
|
+
provider=attrs.get("provider"),
|
|
780
|
+
model=attrs.get("model"),
|
|
781
|
+
repair_attempts=attrs.get("repair_attempts"),
|
|
782
|
+
),
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
def ai_provider(self, items):
|
|
786
|
+
return ("provider", _str(items[0]))
|
|
787
|
+
|
|
788
|
+
def ai_model(self, items):
|
|
789
|
+
return ("model", _str(items[0]))
|
|
790
|
+
|
|
791
|
+
def ai_repair_attempts(self, items):
|
|
792
|
+
return ("repair_attempts", int(items[0]))
|
|
793
|
+
|
|
794
|
+
def field_mapping(self, items):
|
|
795
|
+
return FieldMapping(source=str(items[0]), target=str(items[1]))
|
|
796
|
+
|
|
797
|
+
def ai_item(self, items):
|
|
798
|
+
return items[0]
|