pytrilogy 0.0.3.3__py3-none-any.whl → 0.0.3.5__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 pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/RECORD +15 -14
- trilogy/__init__.py +1 -1
- trilogy/authoring/__init__.py +90 -0
- trilogy/core/models/author.py +3 -7
- trilogy/core/models/environment.py +45 -22
- trilogy/core/processing/concept_strategies_v3.py +1 -1
- trilogy/core/query_processor.py +1 -1
- trilogy/executor.py +7 -5
- trilogy/parsing/common.py +3 -9
- trilogy/parsing/parse_engine.py +28 -22
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.3.dist-info → pytrilogy-0.0.3.5.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=7rK8M4Aw3BDqS9a5ou3PjyRkVMzEhaVMf6a7V-8ll4E,302
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
|
|
4
4
|
trilogy/engine.py,sha256=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
|
|
5
|
-
trilogy/executor.py,sha256=
|
|
5
|
+
trilogy/executor.py,sha256=sssEPDnIDPiQtMSrt5pFiJXUfcDc6gSi4m2Eliod_BM,16844
|
|
6
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
7
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
9
|
+
trilogy/authoring/__init__.py,sha256=6eIJewNIbrMdP6_dyLpyZ0uU62_pSj9wCP80Pkz2Bl8,1922
|
|
9
10
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
11
|
trilogy/core/constants.py,sha256=7XaCpZn5mQmjTobbeBn56SzPWq9eMNDfzfsRU-fP0VE,171
|
|
11
12
|
trilogy/core/enums.py,sha256=aTFXL6nckmG0hpNLdCxI2kAT26cIsZPahF-pHzLQ9uc,7085
|
|
@@ -17,14 +18,14 @@ trilogy/core/functions.py,sha256=7Pq9jYSJd45L2pxT7AI-_rXVZmeLnmTPp8d1lA4z4Vk,244
|
|
|
17
18
|
trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
|
|
18
19
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
19
20
|
trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
|
|
20
|
-
trilogy/core/query_processor.py,sha256=
|
|
21
|
+
trilogy/core/query_processor.py,sha256=Do8YpdPBdsbKtl9n37hobzk8SORMGqH-e_zNNxd-BE4,19456
|
|
21
22
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
trilogy/core/models/author.py,sha256=
|
|
23
|
+
trilogy/core/models/author.py,sha256=oRCKWhz-i1fO1LlHWiHE3l1awCHdQ3yx6FKH9n9RxRU,67188
|
|
23
24
|
trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,56127
|
|
24
25
|
trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
|
|
25
26
|
trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
|
|
26
27
|
trilogy/core/models/datasource.py,sha256=c0tGxyH2WwTmAD047tr69U0a6GNVf-ug26H68yii7DA,9257
|
|
27
|
-
trilogy/core/models/environment.py,sha256=
|
|
28
|
+
trilogy/core/models/environment.py,sha256=GU8D3cn6lCFAseVPYfW_a-cnBbD1sYEeDVOkbZSWCxk,25943
|
|
28
29
|
trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
|
|
29
30
|
trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
|
|
30
31
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
@@ -32,7 +33,7 @@ trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52d
|
|
|
32
33
|
trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
|
|
33
34
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
|
|
34
35
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256=
|
|
36
|
+
trilogy/core/processing/concept_strategies_v3.py,sha256=wPlpg4L7uw-f0DgJBkI8VRdcisjDT1X6iApjEE6CmfA,40291
|
|
36
37
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
37
38
|
trilogy/core/processing/utility.py,sha256=Oc5tLGeDDpzhbfo2ZcF8ex1kez-NcJDMcG2Lm5BjS4c,20548
|
|
38
39
|
trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
|
|
@@ -83,18 +84,18 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
|
|
|
83
84
|
trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
|
|
84
85
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
86
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
86
|
-
trilogy/parsing/common.py,sha256=
|
|
87
|
+
trilogy/parsing/common.py,sha256=yAE3x4SyO4PfAb7HhZ_l9sNPYaf_pcM1K8ioEy76SCU,20301
|
|
87
88
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
88
89
|
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
89
90
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
90
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
91
|
+
trilogy/parsing/parse_engine.py,sha256=uazcnUhY3pERY8Xa116IHl1JhCPSlvQG6i4vP7I4Qpk,54638
|
|
91
92
|
trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,17837
|
|
92
93
|
trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
|
|
93
94
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
95
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
95
|
-
pytrilogy-0.0.3.
|
|
96
|
-
pytrilogy-0.0.3.
|
|
97
|
-
pytrilogy-0.0.3.
|
|
98
|
-
pytrilogy-0.0.3.
|
|
99
|
-
pytrilogy-0.0.3.
|
|
100
|
-
pytrilogy-0.0.3.
|
|
96
|
+
pytrilogy-0.0.3.5.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
97
|
+
pytrilogy-0.0.3.5.dist-info/METADATA,sha256=W8SsLe2qtgpl4SDr_xlL3lOwReWbKYBClHXkjLQSIqk,8983
|
|
98
|
+
pytrilogy-0.0.3.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
99
|
+
pytrilogy-0.0.3.5.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
100
|
+
pytrilogy-0.0.3.5.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
101
|
+
pytrilogy-0.0.3.5.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from trilogy.constants import DEFAULT_NAMESPACE
|
|
2
|
+
from trilogy.core.enums import (
|
|
3
|
+
BooleanOperator,
|
|
4
|
+
ComparisonOperator,
|
|
5
|
+
FunctionClass,
|
|
6
|
+
FunctionType,
|
|
7
|
+
InfiniteFunctionArgs,
|
|
8
|
+
Ordering,
|
|
9
|
+
Purpose,
|
|
10
|
+
)
|
|
11
|
+
from trilogy.core.functions import FunctionFactory
|
|
12
|
+
from trilogy.core.models.author import (
|
|
13
|
+
AggregateWrapper,
|
|
14
|
+
CaseElse,
|
|
15
|
+
CaseWhen,
|
|
16
|
+
Comparison,
|
|
17
|
+
Concept,
|
|
18
|
+
ConceptRef,
|
|
19
|
+
Conditional,
|
|
20
|
+
FilterItem,
|
|
21
|
+
Function,
|
|
22
|
+
HavingClause,
|
|
23
|
+
MagicConstants,
|
|
24
|
+
Metadata,
|
|
25
|
+
OrderBy,
|
|
26
|
+
OrderItem,
|
|
27
|
+
Parenthetical,
|
|
28
|
+
SubselectComparison,
|
|
29
|
+
WhereClause,
|
|
30
|
+
WindowItem,
|
|
31
|
+
WindowItemOrder,
|
|
32
|
+
WindowItemOver,
|
|
33
|
+
WindowOrder,
|
|
34
|
+
WindowType,
|
|
35
|
+
)
|
|
36
|
+
from trilogy.core.models.core import DataType, ListType, ListWrapper, StructType
|
|
37
|
+
from trilogy.core.models.environment import Environment
|
|
38
|
+
from trilogy.core.statements.author import (
|
|
39
|
+
ConceptDeclarationStatement,
|
|
40
|
+
ConceptTransform,
|
|
41
|
+
SelectItem,
|
|
42
|
+
SelectStatement,
|
|
43
|
+
)
|
|
44
|
+
from trilogy.parsing.common import arbitrary_to_concept, arg_to_datatype
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"Concept",
|
|
48
|
+
"Function",
|
|
49
|
+
"WhereClause",
|
|
50
|
+
"Comparison",
|
|
51
|
+
"FilterItem",
|
|
52
|
+
"CaseWhen",
|
|
53
|
+
"CaseElse",
|
|
54
|
+
"AggregateWrapper",
|
|
55
|
+
"WindowItem",
|
|
56
|
+
"WindowOrder",
|
|
57
|
+
"WindowType",
|
|
58
|
+
"WindowItemOrder",
|
|
59
|
+
"WindowItemOver",
|
|
60
|
+
"DataType",
|
|
61
|
+
"StructType",
|
|
62
|
+
"ListType",
|
|
63
|
+
"ListWrapper",
|
|
64
|
+
"FunctionType",
|
|
65
|
+
"FunctionFactory",
|
|
66
|
+
"ConceptDeclarationStatement",
|
|
67
|
+
"ConceptTransform",
|
|
68
|
+
"SelectItem",
|
|
69
|
+
"SelectStatement",
|
|
70
|
+
"Environment",
|
|
71
|
+
"ConceptRef",
|
|
72
|
+
"HavingClause",
|
|
73
|
+
"MagicConstants",
|
|
74
|
+
"Metadata",
|
|
75
|
+
"OrderBy",
|
|
76
|
+
"OrderItem",
|
|
77
|
+
"Parenthetical",
|
|
78
|
+
"SubselectComparison",
|
|
79
|
+
"Conditional",
|
|
80
|
+
"BooleanOperator",
|
|
81
|
+
"ComparisonOperator",
|
|
82
|
+
"FunctionClass",
|
|
83
|
+
"FunctionType",
|
|
84
|
+
"InfiniteFunctionArgs",
|
|
85
|
+
"Ordering",
|
|
86
|
+
"Purpose",
|
|
87
|
+
"DEFAULT_NAMESPACE",
|
|
88
|
+
"arbitrary_to_concept",
|
|
89
|
+
"arg_to_datatype",
|
|
90
|
+
]
|
trilogy/core/models/author.py
CHANGED
|
@@ -166,10 +166,8 @@ class UndefinedConcept(ConceptRef):
|
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
def address_with_namespace(address: str, namespace: str) -> str:
|
|
169
|
-
|
|
170
|
-
if
|
|
171
|
-
return address
|
|
172
|
-
if ns == DEFAULT_NAMESPACE:
|
|
169
|
+
existing_ns = address.split(".", 1)[0]
|
|
170
|
+
if existing_ns == DEFAULT_NAMESPACE:
|
|
173
171
|
return f"{namespace}.{address.split('.',1)[1]}"
|
|
174
172
|
return f"{namespace}.{address}"
|
|
175
173
|
|
|
@@ -203,7 +201,7 @@ class Parenthetical(
|
|
|
203
201
|
def __repr__(self):
|
|
204
202
|
return f"({str(self.content)})"
|
|
205
203
|
|
|
206
|
-
def with_namespace(self, namespace: str):
|
|
204
|
+
def with_namespace(self, namespace: str) -> Parenthetical:
|
|
207
205
|
return Parenthetical.model_construct(
|
|
208
206
|
content=(
|
|
209
207
|
self.content.with_namespace(namespace)
|
|
@@ -917,8 +915,6 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
917
915
|
return self.name.replace(".", "_")
|
|
918
916
|
|
|
919
917
|
def with_namespace(self, namespace: str) -> Self:
|
|
920
|
-
if namespace == self.namespace:
|
|
921
|
-
return self
|
|
922
918
|
return self.__class__.model_construct(
|
|
923
919
|
name=self.name,
|
|
924
920
|
datatype=self.datatype,
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
|
10
10
|
Annotated,
|
|
11
|
+
Any,
|
|
11
12
|
Dict,
|
|
12
13
|
ItemsView,
|
|
13
14
|
List,
|
|
@@ -383,14 +384,13 @@ class Environment(BaseModel):
|
|
|
383
384
|
):
|
|
384
385
|
exists = True
|
|
385
386
|
imp_stm = Import(alias=alias, path=Path(source.working_path))
|
|
386
|
-
same_namespace = alias ==
|
|
387
|
+
same_namespace = alias == DEFAULT_NAMESPACE
|
|
387
388
|
|
|
388
389
|
if not exists:
|
|
389
390
|
self.imports[alias].append(imp_stm)
|
|
390
391
|
# we can't exit early
|
|
391
392
|
# as there may be new concepts
|
|
392
393
|
for k, concept in source.concepts.items():
|
|
393
|
-
|
|
394
394
|
# skip internal namespace
|
|
395
395
|
if INTERNAL_NAMESPACE in concept.address:
|
|
396
396
|
continue
|
|
@@ -416,7 +416,6 @@ class Environment(BaseModel):
|
|
|
416
416
|
self.alias_origin_lookup[address_with_namespace(key, alias)] = (
|
|
417
417
|
val.with_namespace(alias)
|
|
418
418
|
)
|
|
419
|
-
|
|
420
419
|
return self
|
|
421
420
|
|
|
422
421
|
def add_file_import(
|
|
@@ -457,8 +456,10 @@ class Environment(BaseModel):
|
|
|
457
456
|
token_address=target,
|
|
458
457
|
)
|
|
459
458
|
nparser.set_text(text)
|
|
459
|
+
nparser.environment.concepts.fail_on_missing = False
|
|
460
460
|
nparser.transform(PARSER.parse(text))
|
|
461
461
|
nparser.hydrate_missing()
|
|
462
|
+
nparser.environment.concepts.fail_on_missing = True
|
|
462
463
|
|
|
463
464
|
except Exception as e:
|
|
464
465
|
raise ImportError(
|
|
@@ -664,37 +665,59 @@ class LazyEnvironment(Environment):
|
|
|
664
665
|
until relevant attributes accessed."""
|
|
665
666
|
|
|
666
667
|
load_path: Path
|
|
668
|
+
setup_queries: list[Any] = Field(default_factory=list)
|
|
667
669
|
loaded: bool = False
|
|
668
670
|
|
|
671
|
+
@property
|
|
672
|
+
def setup_path(self) -> Path:
|
|
673
|
+
return self.load_path.parent / "setup.preql"
|
|
674
|
+
|
|
669
675
|
def __init__(self, **data):
|
|
676
|
+
if not data.get("working_path"):
|
|
677
|
+
data["working_path"] = data["load_path"].parent
|
|
670
678
|
super().__init__(**data)
|
|
679
|
+
assert self.working_path == self.load_path.parent
|
|
671
680
|
|
|
672
681
|
def _add_path_concepts(self):
|
|
673
682
|
pass
|
|
674
683
|
|
|
684
|
+
def _load(self):
|
|
685
|
+
if self.loaded:
|
|
686
|
+
return
|
|
687
|
+
from trilogy import parse
|
|
688
|
+
|
|
689
|
+
env = Environment(working_path=self.load_path.parent)
|
|
690
|
+
assert env.working_path == self.load_path.parent
|
|
691
|
+
with open(self.load_path, "r") as f:
|
|
692
|
+
env, _ = parse(f.read(), env)
|
|
693
|
+
if self.setup_path.exists():
|
|
694
|
+
with open(self.setup_path, "r") as f2:
|
|
695
|
+
env, q = parse(f2.read(), env)
|
|
696
|
+
for q in q:
|
|
697
|
+
self.setup_queries.append(q)
|
|
698
|
+
self.loaded = True
|
|
699
|
+
self.datasources = env.datasources
|
|
700
|
+
self.concepts = env.concepts
|
|
701
|
+
self.imports = env.imports
|
|
702
|
+
self.alias_origin_lookup = env.alias_origin_lookup
|
|
703
|
+
self.materialized_concepts = env.materialized_concepts
|
|
704
|
+
self.functions = env.functions
|
|
705
|
+
self.data_types = env.data_types
|
|
706
|
+
self.cte_name_map = env.cte_name_map
|
|
707
|
+
|
|
675
708
|
def __getattribute__(self, name):
|
|
676
|
-
if name in (
|
|
677
|
-
"
|
|
678
|
-
"
|
|
679
|
-
"
|
|
680
|
-
"
|
|
681
|
-
"
|
|
682
|
-
"
|
|
709
|
+
if name not in (
|
|
710
|
+
"datasources",
|
|
711
|
+
"concepts",
|
|
712
|
+
"imports",
|
|
713
|
+
"materialized_concepts",
|
|
714
|
+
"functions",
|
|
715
|
+
"datatypes",
|
|
716
|
+
"cte_name_map",
|
|
683
717
|
) or name.startswith("_"):
|
|
684
718
|
return super().__getattribute__(name)
|
|
685
719
|
if not self.loaded:
|
|
686
|
-
|
|
687
|
-
f"lazily evaluating load path {self.load_path} to access {name}"
|
|
688
|
-
)
|
|
689
|
-
from trilogy import parse
|
|
690
|
-
|
|
691
|
-
env = Environment(working_path=str(self.working_path))
|
|
692
|
-
with open(self.load_path, "r") as f:
|
|
693
|
-
parse(f.read(), env)
|
|
694
|
-
self.loaded = True
|
|
695
|
-
self.datasources = env.datasources
|
|
696
|
-
self.concepts = env.concepts
|
|
697
|
-
self.imports = env.imports
|
|
720
|
+
self._load()
|
|
698
721
|
return super().__getattribute__(name)
|
|
699
722
|
|
|
700
723
|
|
|
@@ -1046,7 +1046,7 @@ def source_query_concepts(
|
|
|
1046
1046
|
f"{c.address}<{c.purpose}>{c.derivation}>" for c in output_concepts
|
|
1047
1047
|
]
|
|
1048
1048
|
raise ValueError(
|
|
1049
|
-
f"Could not resolve
|
|
1049
|
+
f"Could not resolve connections between {error_strings} from environment graph."
|
|
1050
1050
|
)
|
|
1051
1051
|
final = [x for x in root.output_concepts if x.address not in root.hidden_concepts]
|
|
1052
1052
|
logger.info(
|
trilogy/core/query_processor.py
CHANGED
|
@@ -385,7 +385,7 @@ def get_query_node(
|
|
|
385
385
|
)
|
|
386
386
|
graph = generate_graph(build_environment)
|
|
387
387
|
logger.info(
|
|
388
|
-
f"{LOGGER_PREFIX} getting source datasource for outputs {
|
|
388
|
+
f"{LOGGER_PREFIX} getting source datasource for outputs {build_statement.output_components} grain {build_statement.grain}"
|
|
389
389
|
)
|
|
390
390
|
|
|
391
391
|
search_concepts: list[BuildConcept] = build_statement.output_components
|
trilogy/executor.py
CHANGED
|
@@ -346,10 +346,10 @@ class Executor(object):
|
|
|
346
346
|
file = Path(file)
|
|
347
347
|
with open(file, "r") as f:
|
|
348
348
|
command = f.read()
|
|
349
|
-
return self.parse_text_generator(command, persist=persist)
|
|
349
|
+
return self.parse_text_generator(command, persist=persist, root=file)
|
|
350
350
|
|
|
351
351
|
def parse_text(
|
|
352
|
-
self, command: str, persist: bool = False
|
|
352
|
+
self, command: str, persist: bool = False, root: Path | None = None
|
|
353
353
|
) -> List[
|
|
354
354
|
ProcessedQuery
|
|
355
355
|
| ProcessedQueryPersist
|
|
@@ -357,9 +357,11 @@ class Executor(object):
|
|
|
357
357
|
| ProcessedRawSQLStatement
|
|
358
358
|
| ProcessedCopyStatement
|
|
359
359
|
]:
|
|
360
|
-
return list(self.parse_text_generator(command, persist=persist))
|
|
360
|
+
return list(self.parse_text_generator(command, persist=persist, root=root))
|
|
361
361
|
|
|
362
|
-
def parse_text_generator(
|
|
362
|
+
def parse_text_generator(
|
|
363
|
+
self, command: str, persist: bool = False, root: Path | None = None
|
|
364
|
+
) -> Generator[
|
|
363
365
|
ProcessedQuery
|
|
364
366
|
| ProcessedQueryPersist
|
|
365
367
|
| ProcessedShowStatement
|
|
@@ -369,7 +371,7 @@ class Executor(object):
|
|
|
369
371
|
None,
|
|
370
372
|
]:
|
|
371
373
|
"""Process a preql text command"""
|
|
372
|
-
_, parsed = parse_text(command, self.environment)
|
|
374
|
+
_, parsed = parse_text(command, self.environment, root=root)
|
|
373
375
|
generatable = [
|
|
374
376
|
x
|
|
375
377
|
for x in parsed
|
trilogy/parsing/common.py
CHANGED
|
@@ -152,7 +152,6 @@ def constant_to_concept(
|
|
|
152
152
|
parent: ListWrapper | MapWrapper | list | int | float | str,
|
|
153
153
|
name: str,
|
|
154
154
|
namespace: str,
|
|
155
|
-
purpose: Purpose | None = None,
|
|
156
155
|
metadata: Metadata | None = None,
|
|
157
156
|
) -> Concept:
|
|
158
157
|
const_function: Function = Function(
|
|
@@ -340,7 +339,6 @@ def filter_item_to_concept(
|
|
|
340
339
|
name: str,
|
|
341
340
|
namespace: str,
|
|
342
341
|
environment: Environment,
|
|
343
|
-
purpose: Purpose | None = None,
|
|
344
342
|
metadata: Metadata | None = None,
|
|
345
343
|
) -> Concept:
|
|
346
344
|
fmetadata = metadata or Metadata()
|
|
@@ -377,14 +375,13 @@ def window_item_to_concept(
|
|
|
377
375
|
name: str,
|
|
378
376
|
namespace: str,
|
|
379
377
|
environment: Environment,
|
|
380
|
-
purpose: Purpose | None = None,
|
|
381
378
|
metadata: Metadata | None = None,
|
|
382
379
|
) -> Concept:
|
|
383
380
|
fmetadata = metadata or Metadata()
|
|
384
381
|
bcontent = environment.concepts[parent.content.address]
|
|
385
382
|
if isinstance(bcontent, UndefinedConcept):
|
|
386
383
|
return UndefinedConcept(address=f"{namespace}.{name}", metadata=fmetadata)
|
|
387
|
-
local_purpose, keys = get_purpose_and_keys(
|
|
384
|
+
local_purpose, keys = get_purpose_and_keys(None, (bcontent,), environment)
|
|
388
385
|
|
|
389
386
|
if parent.order_by:
|
|
390
387
|
grain_components = parent.over + [bcontent.output]
|
|
@@ -568,7 +565,6 @@ def arbitrary_to_concept(
|
|
|
568
565
|
namespace: str | None = None,
|
|
569
566
|
name: str | None = None,
|
|
570
567
|
metadata: Metadata | None = None,
|
|
571
|
-
purpose: Purpose | None = None,
|
|
572
568
|
) -> Concept:
|
|
573
569
|
namespace = namespace or environment.namespace
|
|
574
570
|
if isinstance(parent, AggregateWrapper):
|
|
@@ -585,7 +581,6 @@ def arbitrary_to_concept(
|
|
|
585
581
|
name,
|
|
586
582
|
namespace,
|
|
587
583
|
environment=environment,
|
|
588
|
-
purpose=purpose,
|
|
589
584
|
metadata=metadata,
|
|
590
585
|
)
|
|
591
586
|
elif isinstance(parent, FilterItem):
|
|
@@ -596,7 +591,6 @@ def arbitrary_to_concept(
|
|
|
596
591
|
name,
|
|
597
592
|
namespace,
|
|
598
593
|
environment=environment,
|
|
599
|
-
purpose=purpose,
|
|
600
594
|
metadata=metadata,
|
|
601
595
|
)
|
|
602
596
|
elif isinstance(parent, Function):
|
|
@@ -612,8 +606,8 @@ def arbitrary_to_concept(
|
|
|
612
606
|
elif isinstance(parent, ListWrapper):
|
|
613
607
|
if not name:
|
|
614
608
|
name = f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(parent))}"
|
|
615
|
-
return constant_to_concept(parent, name, namespace,
|
|
609
|
+
return constant_to_concept(parent, name, namespace, metadata)
|
|
616
610
|
else:
|
|
617
611
|
if not name:
|
|
618
612
|
name = f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(parent))}"
|
|
619
|
-
return constant_to_concept(parent, name, namespace,
|
|
613
|
+
return constant_to_concept(parent, name, namespace, metadata)
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -147,6 +147,9 @@ with open(join(dirname(__file__), "trilogy.lark"), "r") as f:
|
|
|
147
147
|
|
|
148
148
|
|
|
149
149
|
def gen_cache_lookup(path: str, alias: str, parent: str) -> str:
|
|
150
|
+
# path is the path of the file
|
|
151
|
+
# alias is what it's being imported under
|
|
152
|
+
# parent is the...direct parnet?
|
|
150
153
|
return path + alias + parent
|
|
151
154
|
|
|
152
155
|
|
|
@@ -252,10 +255,12 @@ class ParseToObjects(Transformer):
|
|
|
252
255
|
for _, v in self.parsed.items():
|
|
253
256
|
v.prepare_parse()
|
|
254
257
|
|
|
255
|
-
def hydrate_missing(self):
|
|
258
|
+
def hydrate_missing(self, force: bool = False):
|
|
259
|
+
if self.token_address not in self.tokens:
|
|
260
|
+
return []
|
|
256
261
|
self.parse_pass = ParsePass.VALIDATION
|
|
257
|
-
for
|
|
258
|
-
if v.parse_pass == ParsePass.VALIDATION:
|
|
262
|
+
for _, v in list(self.parsed.items()):
|
|
263
|
+
if v.parse_pass == ParsePass.VALIDATION and not force:
|
|
259
264
|
continue
|
|
260
265
|
v.hydrate_missing()
|
|
261
266
|
reparsed = self.transform(self.tokens[self.token_address])
|
|
@@ -390,7 +395,6 @@ class ParseToObjects(Transformer):
|
|
|
390
395
|
|
|
391
396
|
@v_args(meta=True)
|
|
392
397
|
def column_assignment(self, meta: Meta, args):
|
|
393
|
-
# TODO -> deal with conceptual modifiers
|
|
394
398
|
modifiers = []
|
|
395
399
|
alias = args[0]
|
|
396
400
|
concept_list = args[1]
|
|
@@ -398,6 +402,7 @@ class ParseToObjects(Transformer):
|
|
|
398
402
|
if len(concept_list) > 1:
|
|
399
403
|
modifiers += concept_list[:-1]
|
|
400
404
|
concept = concept_list[-1]
|
|
405
|
+
assert not self.environment.concepts.fail_on_missing
|
|
401
406
|
resolved = self.environment.concepts.__getitem__( # type: ignore
|
|
402
407
|
key=concept, line_no=meta.line, file=self.token_address
|
|
403
408
|
)
|
|
@@ -508,8 +513,6 @@ class ParseToObjects(Transformer):
|
|
|
508
513
|
else:
|
|
509
514
|
metadata = None
|
|
510
515
|
purpose = args[0]
|
|
511
|
-
if purpose == Purpose.AUTO:
|
|
512
|
-
purpose = None
|
|
513
516
|
raw_name = args[1]
|
|
514
517
|
# abc.def.property pattern
|
|
515
518
|
if isinstance(raw_name, str):
|
|
@@ -538,10 +541,14 @@ class ParseToObjects(Transformer):
|
|
|
538
541
|
name=name,
|
|
539
542
|
namespace=namespace,
|
|
540
543
|
environment=self.environment,
|
|
541
|
-
purpose=purpose,
|
|
542
544
|
metadata=metadata,
|
|
543
545
|
)
|
|
544
546
|
|
|
547
|
+
if purpose and purpose != Purpose.AUTO and concept.purpose != purpose:
|
|
548
|
+
raise SyntaxError(
|
|
549
|
+
f'Concept {name} purpose {concept.purpose} does not match declared purpose {purpose}. Suggest defaulting to "auto"'
|
|
550
|
+
)
|
|
551
|
+
|
|
545
552
|
if concept.metadata:
|
|
546
553
|
concept.metadata.line_number = meta.line
|
|
547
554
|
self.environment.add_concept(concept, meta=meta)
|
|
@@ -552,7 +559,6 @@ class ParseToObjects(Transformer):
|
|
|
552
559
|
source_value,
|
|
553
560
|
name=name,
|
|
554
561
|
namespace=namespace,
|
|
555
|
-
purpose=purpose,
|
|
556
562
|
metadata=metadata,
|
|
557
563
|
)
|
|
558
564
|
if concept.metadata:
|
|
@@ -577,15 +583,6 @@ class ParseToObjects(Transformer):
|
|
|
577
583
|
namespace=self.environment.namespace or DEFAULT_NAMESPACE,
|
|
578
584
|
)
|
|
579
585
|
|
|
580
|
-
# clean up current definitions
|
|
581
|
-
# to_delete = set()
|
|
582
|
-
# if output.name in self.environment.named_statements:
|
|
583
|
-
# for k, v in self.environment.concepts.items():
|
|
584
|
-
# if v.derivation == Derivation.ROWSET and v.lineage.rowset.name == name:
|
|
585
|
-
# to_delete.add(k)
|
|
586
|
-
# for k in to_delete:
|
|
587
|
-
# self.environment.concepts.pop(k)
|
|
588
|
-
|
|
589
586
|
for new_concept in rowset_to_concepts(output, self.environment):
|
|
590
587
|
if new_concept.metadata:
|
|
591
588
|
new_concept.metadata.line_number = meta.line
|
|
@@ -887,10 +884,14 @@ class ParseToObjects(Transformer):
|
|
|
887
884
|
if cache_lookup in self.parsed:
|
|
888
885
|
nparser = self.parsed[cache_lookup]
|
|
889
886
|
new_env = nparser.environment
|
|
887
|
+
if nparser.parse_pass != ParsePass.VALIDATION:
|
|
888
|
+
# nparser.transform(raw_tokens)
|
|
889
|
+
nparser.hydrate_missing()
|
|
890
890
|
else:
|
|
891
891
|
try:
|
|
892
892
|
new_env = Environment(
|
|
893
893
|
working_path=dirname(target),
|
|
894
|
+
env_file_path=token_lookup,
|
|
894
895
|
)
|
|
895
896
|
new_env.concepts.fail_on_missing = False
|
|
896
897
|
self.parsed[self.parse_address] = self
|
|
@@ -908,10 +909,11 @@ class ParseToObjects(Transformer):
|
|
|
908
909
|
raise ImportError(
|
|
909
910
|
f"Unable to import file {target}, parsing error: {e}"
|
|
910
911
|
) from e
|
|
912
|
+
parsed_path = Path(args[0])
|
|
913
|
+
imps = ImportStatement(alias=alias, path=parsed_path)
|
|
911
914
|
|
|
912
|
-
imps = ImportStatement(alias=alias, path=Path(args[0]))
|
|
913
915
|
self.environment.add_import(
|
|
914
|
-
alias, new_env, Import(alias=alias, path=
|
|
916
|
+
alias, new_env, Import(alias=alias, path=parsed_path)
|
|
915
917
|
)
|
|
916
918
|
return imps
|
|
917
919
|
|
|
@@ -1602,7 +1604,9 @@ def parse_text_raw(text: str, environment: Optional[Environment] = None):
|
|
|
1602
1604
|
PARSER.parse(text)
|
|
1603
1605
|
|
|
1604
1606
|
|
|
1605
|
-
def parse_text(
|
|
1607
|
+
def parse_text(
|
|
1608
|
+
text: str, environment: Optional[Environment] = None, root: Path | None = None
|
|
1609
|
+
) -> Tuple[
|
|
1606
1610
|
Environment,
|
|
1607
1611
|
List[
|
|
1608
1612
|
Datasource
|
|
@@ -1614,7 +1618,9 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
|
1614
1618
|
| None
|
|
1615
1619
|
],
|
|
1616
1620
|
]:
|
|
1617
|
-
environment = environment or
|
|
1621
|
+
environment = environment or (
|
|
1622
|
+
Environment(working_path=root) if root else Environment()
|
|
1623
|
+
)
|
|
1618
1624
|
parser = ParseToObjects(environment=environment)
|
|
1619
1625
|
|
|
1620
1626
|
try:
|
|
@@ -1623,7 +1629,7 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
|
1623
1629
|
parser.prepare_parse()
|
|
1624
1630
|
parser.transform(PARSER.parse(text))
|
|
1625
1631
|
# this will reset fail on missing
|
|
1626
|
-
pass_two = parser.hydrate_missing()
|
|
1632
|
+
pass_two = parser.hydrate_missing(force=True)
|
|
1627
1633
|
output = [v for v in pass_two if v]
|
|
1628
1634
|
environment.concepts.fail_on_missing = True
|
|
1629
1635
|
except VisitError as e:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|