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.

Files changed (122) hide show
  1. modelable/__init__.py +1 -0
  2. modelable/__main__.py +3 -0
  3. modelable/_pydantic_py314_compat.py +31 -0
  4. modelable/cli.py +41 -0
  5. modelable/commands/__init__.py +1 -0
  6. modelable/commands/apicurio.py +84 -0
  7. modelable/commands/codegen.py +241 -0
  8. modelable/commands/common.py +43 -0
  9. modelable/commands/compile.py +237 -0
  10. modelable/commands/create.py +164 -0
  11. modelable/commands/diff.py +82 -0
  12. modelable/commands/graph.py +53 -0
  13. modelable/commands/llm.py +564 -0
  14. modelable/commands/lsp.py +15 -0
  15. modelable/commands/runtime.py +37 -0
  16. modelable/commands/scenario.py +104 -0
  17. modelable/commands/spec.py +197 -0
  18. modelable/commands/workspace.py +240 -0
  19. modelable/compat/__init__.py +11 -0
  20. modelable/compat/checker.py +179 -0
  21. modelable/compat/diff.py +169 -0
  22. modelable/compiler/__init__.py +3 -0
  23. modelable/compiler/compiler.py +19 -0
  24. modelable/compiler/workspace.py +346 -0
  25. modelable/diagnostics/__init__.py +3 -0
  26. modelable/diagnostics/model.py +27 -0
  27. modelable/emitters/__init__.py +0 -0
  28. modelable/emitters/base.py +22 -0
  29. modelable/emitters/csharp.py +245 -0
  30. modelable/emitters/dbt_yaml.py +290 -0
  31. modelable/emitters/diagnostics.py +25 -0
  32. modelable/emitters/fhir.py +694 -0
  33. modelable/emitters/fhir_validator.py +36 -0
  34. modelable/emitters/go.py +334 -0
  35. modelable/emitters/java.py +264 -0
  36. modelable/emitters/json_schema.py +458 -0
  37. modelable/emitters/markdown.py +252 -0
  38. modelable/emitters/odcs.py +355 -0
  39. modelable/emitters/openlineage.py +315 -0
  40. modelable/emitters/openmetadata.py +258 -0
  41. modelable/emitters/python.py +282 -0
  42. modelable/emitters/rust.py +643 -0
  43. modelable/emitters/shapes.py +261 -0
  44. modelable/emitters/sql.py +266 -0
  45. modelable/emitters/targets.py +141 -0
  46. modelable/emitters/typescript.py +352 -0
  47. modelable/expressions/__init__.py +0 -0
  48. modelable/expressions/cel.py +547 -0
  49. modelable/governance/__init__.py +3 -0
  50. modelable/governance/checker.py +271 -0
  51. modelable/governance/por.py +46 -0
  52. modelable/grammar/__init__.py +1 -0
  53. modelable/grammar/modelable.lark +257 -0
  54. modelable/graph/__init__.py +5 -0
  55. modelable/graph/export.py +442 -0
  56. modelable/llm/__init__.py +43 -0
  57. modelable/llm/chat.py +255 -0
  58. modelable/llm/config.py +87 -0
  59. modelable/llm/context.py +194 -0
  60. modelable/llm/engine.py +976 -0
  61. modelable/llm/importers.py +1077 -0
  62. modelable/llm/provenance.py +84 -0
  63. modelable/llm/providers.py +182 -0
  64. modelable/llm/qa.py +126 -0
  65. modelable/llm/recommendations.py +33 -0
  66. modelable/llm/redaction.py +19 -0
  67. modelable/llm/render.py +279 -0
  68. modelable/llm/update_plan.py +101 -0
  69. modelable/llm/validation_help.py +10 -0
  70. modelable/lsp/__init__.py +3 -0
  71. modelable/lsp/__main__.py +4 -0
  72. modelable/lsp/code_actions.py +210 -0
  73. modelable/lsp/completion.py +480 -0
  74. modelable/lsp/definition.py +343 -0
  75. modelable/lsp/diagnostics.py +31 -0
  76. modelable/lsp/document_symbols.py +197 -0
  77. modelable/lsp/federation.py +261 -0
  78. modelable/lsp/folding.py +33 -0
  79. modelable/lsp/formatting.py +64 -0
  80. modelable/lsp/highlight.py +30 -0
  81. modelable/lsp/hover.py +370 -0
  82. modelable/lsp/inlay_hints.py +158 -0
  83. modelable/lsp/references.py +511 -0
  84. modelable/lsp/rename.py +564 -0
  85. modelable/lsp/semantic_tokens.py +412 -0
  86. modelable/lsp/server.py +370 -0
  87. modelable/lsp/workspace.py +83 -0
  88. modelable/lsp/workspace_symbols.py +104 -0
  89. modelable/parser/__init__.py +94 -0
  90. modelable/parser/ir.py +451 -0
  91. modelable/parser/parse.py +47 -0
  92. modelable/parser/transformer.py +798 -0
  93. modelable/parser/wire.py +68 -0
  94. modelable/planner/__init__.py +0 -0
  95. modelable/planner/lineage.py +91 -0
  96. modelable/planner/planner.py +134 -0
  97. modelable/planner/plans.py +122 -0
  98. modelable/py.typed +0 -0
  99. modelable/registry/__init__.py +9 -0
  100. modelable/registry/apicurio.py +166 -0
  101. modelable/registry/base.py +18 -0
  102. modelable/registry/factory.py +18 -0
  103. modelable/registry/index.py +419 -0
  104. modelable/registry/local.py +26 -0
  105. modelable/registry/oci.py +22 -0
  106. modelable/registry/resolver.py +213 -0
  107. modelable/registry/schema.sql +119 -0
  108. modelable/registry/signature.py +26 -0
  109. modelable/release.py +125 -0
  110. modelable/runtime/__init__.py +5 -0
  111. modelable/runtime/adapter/__init__.py +17 -0
  112. modelable/runtime/adapter/base.py +18 -0
  113. modelable/runtime/adapter/postgres.py +82 -0
  114. modelable/specs/__init__.py +23 -0
  115. modelable/specs/tracking.py +220 -0
  116. modelable/validation/__init__.py +3 -0
  117. modelable/validation/semantic.py +659 -0
  118. modelable-1.0.0.dist-info/METADATA +61 -0
  119. modelable-1.0.0.dist-info/RECORD +122 -0
  120. modelable-1.0.0.dist-info/WHEEL +4 -0
  121. modelable-1.0.0.dist-info/entry_points.txt +2 -0
  122. modelable-1.0.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,412 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+
6
+ from lsprotocol import types
7
+
8
+ _TOKEN_TYPES = [
9
+ types.SemanticTokenTypes.Namespace.value,
10
+ types.SemanticTokenTypes.Class.value,
11
+ types.SemanticTokenTypes.Property.value,
12
+ types.SemanticTokenTypes.Parameter.value,
13
+ types.SemanticTokenTypes.Variable.value,
14
+ types.SemanticTokenTypes.Type.value,
15
+ types.SemanticTokenTypes.Keyword.value,
16
+ types.SemanticTokenTypes.Decorator.value,
17
+ types.SemanticTokenTypes.Comment.value,
18
+ types.SemanticTokenTypes.String.value,
19
+ types.SemanticTokenTypes.Number.value,
20
+ types.SemanticTokenTypes.Operator.value,
21
+ ]
22
+
23
+ _KEYWORDS = {
24
+ "access",
25
+ "additive",
26
+ "aggregate",
27
+ "ai",
28
+ "as",
29
+ "asyncapi",
30
+ "binding",
31
+ "by",
32
+ "cardinality",
33
+ "clickhouse",
34
+ "contact",
35
+ "consumer",
36
+ "description",
37
+ "derive",
38
+ "docs",
39
+ "domain",
40
+ "entity",
41
+ "event",
42
+ "exclude",
43
+ "from",
44
+ "generate",
45
+ "group",
46
+ "internal",
47
+ "join",
48
+ "left",
49
+ "materialisation",
50
+ "mysql",
51
+ "on",
52
+ "openapi",
53
+ "owner",
54
+ "peers",
55
+ "pii",
56
+ "postgres",
57
+ "project",
58
+ "projection",
59
+ "property",
60
+ "protobuf",
61
+ "read",
62
+ "redact",
63
+ "registry",
64
+ "restricted",
65
+ "sql",
66
+ "sqlite",
67
+ "string",
68
+ "subscribe",
69
+ "subscription",
70
+ "transfer",
71
+ "typescript",
72
+ "value",
73
+ "where",
74
+ "workspace",
75
+ "write",
76
+ "jsonschema",
77
+ }
78
+ _TYPE_WORDS = {
79
+ "array",
80
+ "binary",
81
+ "bool",
82
+ "date",
83
+ "decimal",
84
+ "duration",
85
+ "enum",
86
+ "float",
87
+ "int",
88
+ "map",
89
+ "object",
90
+ "ref",
91
+ "time",
92
+ "timestamp",
93
+ "uuid",
94
+ }
95
+
96
+ _ANNOTATION_PATTERN = re.compile(r"@[A-Za-z_][A-Za-z0-9_-]*")
97
+ _WORD_PATTERN = re.compile(r"[A-Za-z_][A-Za-z0-9_-]*")
98
+ _NUMBER_PATTERN = re.compile(r"\d+")
99
+ _DOMAIN_DECL_PATTERN = re.compile(r"^\s*domain\s+(?P<name>[A-Za-z_][A-Za-z0-9_]*)")
100
+ _DECL_PATTERN = re.compile(
101
+ r"^\s*(?P<kind>entity|aggregate|event|value|projection)\s+"
102
+ r"(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*@\s*(?P<version>\d+)"
103
+ )
104
+ _FIELD_PATTERN = re.compile(
105
+ r"^\s*(?:@[A-Za-z_][A-Za-z0-9_-]*(?:\([^)]*\))?\s+)*(?P<name>[A-Za-z_][A-Za-z0-9_]*)\??\s*:"
106
+ )
107
+ _PROJECTION_FIELD_PATTERN = re.compile(
108
+ r"^\s*(?:@[A-Za-z_][A-Za-z0-9_-]*(?:\([^)]*\))?\s+)*(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*(?P<op><-|=)"
109
+ )
110
+ _SOURCE_PATTERN = re.compile(
111
+ r"^\s*(?P<join_prefix>left\s+)?(?P<kind>from|join)\s+"
112
+ r"(?P<domain>[A-Za-z_][A-Za-z0-9_.-]*)\.(?P<model>[A-Za-z_][A-Za-z0-9_.-]*)\s*@\s*"
113
+ r"(?P<version>\d+)\s+as\s+(?P<alias>[A-Za-z_][A-Za-z0-9_]*)"
114
+ )
115
+ _IMPORT_PINNED_PATTERN = re.compile(
116
+ r"^\s*import\s+domain\s+(?P<import_domain>[A-Za-z_][A-Za-z0-9_.-]*)\s+"
117
+ r"from\s+registry\s+\"[^\"]*\"\s+at\s+"
118
+ r"(?P<domain>[A-Za-z_][A-Za-z0-9_.-]*)\.(?P<model>[A-Za-z_][A-Za-z0-9_.-]*)\s*@\s*"
119
+ r"(?P<version>\d+)"
120
+ )
121
+ _IMPORT_DOMAIN_PATTERN = re.compile(r"^\s*import\s+domain\s+(?P<import_domain>[A-Za-z_][A-Za-z0-9_.-]*)")
122
+ _ALIASED_REFERENCE_PATTERN = re.compile(r"(?P<alias>[A-Za-z_][A-Za-z0-9_]*)\.(?P<field>[A-Za-z_][A-Za-z0-9_]*)")
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class _Token:
127
+ line: int
128
+ start: int
129
+ length: int
130
+ token_type: str
131
+ token_modifiers: int = 0
132
+
133
+
134
+ def semantic_tokens_legend() -> types.SemanticTokensLegend:
135
+ return types.SemanticTokensLegend(token_types=_TOKEN_TYPES, token_modifiers=[])
136
+
137
+
138
+ def build_semantic_tokens(text: str) -> types.SemanticTokens:
139
+ tokens: list[_Token] = []
140
+ for line_no, line in enumerate(text.splitlines()):
141
+ spans = _lex_line_spans(line)
142
+ _add_line_tokens(tokens, line_no, line, spans)
143
+ _add_span_tokens(tokens, line_no, spans)
144
+ return types.SemanticTokens(data=_encode_tokens(_dedupe_tokens(tokens)))
145
+
146
+
147
+ def _add_line_tokens(tokens: list[_Token], line_no: int, line: str, spans: list[tuple[int, int, str]]) -> None:
148
+ _add_match_token(tokens, line_no, line, _DOMAIN_DECL_PATTERN, "name", types.SemanticTokenTypes.Namespace.value)
149
+ _add_match_token(tokens, line_no, line, _DECL_PATTERN, "kind", types.SemanticTokenTypes.Keyword.value)
150
+ _add_match_token(tokens, line_no, line, _DECL_PATTERN, "name", types.SemanticTokenTypes.Class.value)
151
+ _add_match_token(tokens, line_no, line, _DECL_PATTERN, "version", types.SemanticTokenTypes.Number.value)
152
+ _add_match_token(tokens, line_no, line, _FIELD_PATTERN, "name", types.SemanticTokenTypes.Property.value)
153
+ _add_match_token(
154
+ tokens,
155
+ line_no,
156
+ line,
157
+ _PROJECTION_FIELD_PATTERN,
158
+ "name",
159
+ types.SemanticTokenTypes.Property.value,
160
+ )
161
+ _add_match_token(
162
+ tokens,
163
+ line_no,
164
+ line,
165
+ _PROJECTION_FIELD_PATTERN,
166
+ "op",
167
+ types.SemanticTokenTypes.Operator.value,
168
+ )
169
+ _add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "kind", types.SemanticTokenTypes.Keyword.value)
170
+ _add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "version", types.SemanticTokenTypes.Number.value)
171
+ _add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "alias", types.SemanticTokenTypes.Parameter.value)
172
+ _add_dotted_reference_token(
173
+ tokens, line_no, line, _SOURCE_PATTERN, "domain", types.SemanticTokenTypes.Namespace.value
174
+ )
175
+ _add_dotted_reference_token(tokens, line_no, line, _SOURCE_PATTERN, "model", types.SemanticTokenTypes.Class.value)
176
+ _add_match_token(
177
+ tokens,
178
+ line_no,
179
+ line,
180
+ _IMPORT_DOMAIN_PATTERN,
181
+ "import_domain",
182
+ types.SemanticTokenTypes.Namespace.value,
183
+ )
184
+ _add_dotted_reference_token(
185
+ tokens,
186
+ line_no,
187
+ line,
188
+ _IMPORT_PINNED_PATTERN,
189
+ "domain",
190
+ types.SemanticTokenTypes.Namespace.value,
191
+ )
192
+ _add_dotted_reference_token(
193
+ tokens,
194
+ line_no,
195
+ line,
196
+ _IMPORT_PINNED_PATTERN,
197
+ "model",
198
+ types.SemanticTokenTypes.Class.value,
199
+ )
200
+ _add_match_token(tokens, line_no, line, _IMPORT_PINNED_PATTERN, "version", types.SemanticTokenTypes.Number.value)
201
+
202
+ if "<-" in line or "=" in line:
203
+ for match in _ALIASED_REFERENCE_PATTERN.finditer(line):
204
+ if _overlaps_any(match.start(), match.end(), spans):
205
+ continue
206
+ tokens.append(
207
+ _Token(
208
+ line=line_no,
209
+ start=match.start("alias"),
210
+ length=match.end("alias") - match.start("alias"),
211
+ token_type=types.SemanticTokenTypes.Variable.value,
212
+ )
213
+ )
214
+ tokens.append(
215
+ _Token(
216
+ line=line_no,
217
+ start=match.start("field"),
218
+ length=match.end("field") - match.start("field"),
219
+ token_type=types.SemanticTokenTypes.Property.value,
220
+ )
221
+ )
222
+
223
+ for annotation in _ANNOTATION_PATTERN.finditer(line):
224
+ if _overlaps_any(annotation.start(), annotation.end(), spans):
225
+ continue
226
+ tokens.append(
227
+ _Token(
228
+ line=line_no,
229
+ start=annotation.start(),
230
+ length=annotation.end() - annotation.start(),
231
+ token_type=types.SemanticTokenTypes.Decorator.value,
232
+ )
233
+ )
234
+
235
+ for match in _WORD_PATTERN.finditer(line):
236
+ if _overlaps_any(match.start(), match.end(), spans):
237
+ continue
238
+ if _covered_by_existing_token(tokens, line_no, match.start(), match.end()):
239
+ continue
240
+ token_type = _classify_word(match.group(0))
241
+ if token_type is None:
242
+ continue
243
+ tokens.append(
244
+ _Token(
245
+ line=line_no,
246
+ start=match.start(),
247
+ length=match.end() - match.start(),
248
+ token_type=token_type,
249
+ )
250
+ )
251
+
252
+ for match in _NUMBER_PATTERN.finditer(line):
253
+ if _overlaps_any(match.start(), match.end(), spans):
254
+ continue
255
+ if _covered_by_existing_token(tokens, line_no, match.start(), match.end()):
256
+ continue
257
+ tokens.append(
258
+ _Token(
259
+ line=line_no,
260
+ start=match.start(),
261
+ length=match.end() - match.start(),
262
+ token_type=types.SemanticTokenTypes.Number.value,
263
+ )
264
+ )
265
+
266
+ _add_operator_tokens(tokens, line_no, line, spans)
267
+
268
+
269
+ def _add_span_tokens(tokens: list[_Token], line_no: int, spans: list[tuple[int, int, str]]) -> None:
270
+ for start, end, kind in spans:
271
+ token_type = (
272
+ types.SemanticTokenTypes.Comment.value if kind == "comment" else types.SemanticTokenTypes.String.value
273
+ )
274
+ tokens.append(
275
+ _Token(
276
+ line=line_no,
277
+ start=start,
278
+ length=end - start,
279
+ token_type=token_type,
280
+ )
281
+ )
282
+
283
+
284
+ def _classify_word(word: str) -> str | None:
285
+ lowered = word.lower()
286
+ if lowered in _TYPE_WORDS:
287
+ return types.SemanticTokenTypes.Type.value
288
+ if lowered in _KEYWORDS:
289
+ return types.SemanticTokenTypes.Keyword.value
290
+ return None
291
+
292
+
293
+ def _lex_line_spans(line: str) -> list[tuple[int, int, str]]:
294
+ spans: list[tuple[int, int, str]] = []
295
+ i = 0
296
+ while i < len(line):
297
+ if line.startswith("//", i):
298
+ spans.append((i, len(line), "comment"))
299
+ break
300
+ if line[i] == '"':
301
+ start = i
302
+ i += 1
303
+ while i < len(line):
304
+ if line[i] == "\\":
305
+ i += 2
306
+ continue
307
+ if line[i] == '"':
308
+ i += 1
309
+ break
310
+ i += 1
311
+ spans.append((start, i, "string"))
312
+ continue
313
+ i += 1
314
+ return spans
315
+
316
+
317
+ def _add_operator_tokens(tokens: list[_Token], line_no: int, line: str, spans: list[tuple[int, int, str]]) -> None:
318
+ i = 0
319
+ while i < len(line):
320
+ if _overlaps_any(i, i + 1, spans):
321
+ i += 1
322
+ continue
323
+ if line.startswith("<-", i):
324
+ tokens.append(_Token(line=line_no, start=i, length=2, token_type=types.SemanticTokenTypes.Operator.value))
325
+ i += 2
326
+ continue
327
+ if line[i] in "{}()[]:,.<>#=":
328
+ tokens.append(_Token(line=line_no, start=i, length=1, token_type=types.SemanticTokenTypes.Operator.value))
329
+ i += 1
330
+
331
+
332
+ def _add_match_token(
333
+ tokens: list[_Token],
334
+ line_no: int,
335
+ line: str,
336
+ pattern: re.Pattern[str],
337
+ group_name: str,
338
+ token_type: str,
339
+ ) -> None:
340
+ match = pattern.match(line)
341
+ if match is None:
342
+ return
343
+ start = match.start(group_name)
344
+ end = match.end(group_name)
345
+ if start < 0 or end <= start:
346
+ return
347
+ tokens.append(_Token(line=line_no, start=start, length=end - start, token_type=token_type))
348
+
349
+
350
+ def _add_dotted_reference_token(
351
+ tokens: list[_Token],
352
+ line_no: int,
353
+ line: str,
354
+ pattern: re.Pattern[str],
355
+ group_name: str,
356
+ token_type: str,
357
+ ) -> None:
358
+ match = pattern.match(line)
359
+ if match is None:
360
+ return
361
+ value = match.group(group_name)
362
+ start = match.start(group_name)
363
+ parts = value.split(".")
364
+ offset = 0
365
+ for index, part in enumerate(parts):
366
+ part_type = token_type if index == len(parts) - 1 else types.SemanticTokenTypes.Namespace.value
367
+ tokens.append(
368
+ _Token(
369
+ line=line_no,
370
+ start=start + offset,
371
+ length=len(part),
372
+ token_type=part_type,
373
+ )
374
+ )
375
+ offset += len(part) + 1
376
+
377
+
378
+ def _overlaps_any(start: int, end: int, spans: list[tuple[int, int, str]]) -> bool:
379
+ return any(start < span_end and end > span_start for span_start, span_end, _kind in spans)
380
+
381
+
382
+ def _covered_by_existing_token(tokens: list[_Token], line_no: int, start: int, end: int) -> bool:
383
+ return any(token.line == line_no and start < token.start + token.length and end > token.start for token in tokens)
384
+
385
+
386
+ def _dedupe_tokens(tokens: list[_Token]) -> list[_Token]:
387
+ seen: set[tuple[int, int, int, str, int]] = set()
388
+ deduped: list[_Token] = []
389
+ for token in tokens:
390
+ key = (token.line, token.start, token.length, token.token_type, token.token_modifiers)
391
+ if key in seen:
392
+ continue
393
+ seen.add(key)
394
+ deduped.append(token)
395
+ return deduped
396
+
397
+
398
+ def _encode_tokens(tokens: list[_Token]) -> list[int]:
399
+ ordered = sorted(tokens, key=lambda token: (token.line, token.start, token.length))
400
+ data: list[int] = []
401
+ previous_line = 0
402
+ previous_start = 0
403
+ type_index = {token_type: index for index, token_type in enumerate(_TOKEN_TYPES)}
404
+
405
+ for token in ordered:
406
+ delta_line = token.line - previous_line
407
+ delta_start = token.start - previous_start if delta_line == 0 else token.start
408
+ data.extend([delta_line, delta_start, token.length, type_index[token.token_type], token.token_modifiers])
409
+ previous_line = token.line
410
+ previous_start = token.start
411
+
412
+ return data