pytrilogy 0.3.142__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_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.
- LICENSE.md +19 -0
- _preql_import_resolver/__init__.py +5 -0
- _preql_import_resolver/_preql_import_resolver.cpython-313-x86_64-linux-gnu.so +0 -0
- pytrilogy-0.3.142.dist-info/METADATA +555 -0
- pytrilogy-0.3.142.dist-info/RECORD +200 -0
- pytrilogy-0.3.142.dist-info/WHEEL +5 -0
- pytrilogy-0.3.142.dist-info/entry_points.txt +2 -0
- pytrilogy-0.3.142.dist-info/licenses/LICENSE.md +19 -0
- trilogy/__init__.py +16 -0
- trilogy/ai/README.md +10 -0
- trilogy/ai/__init__.py +19 -0
- trilogy/ai/constants.py +92 -0
- trilogy/ai/conversation.py +107 -0
- trilogy/ai/enums.py +7 -0
- trilogy/ai/execute.py +50 -0
- trilogy/ai/models.py +34 -0
- trilogy/ai/prompts.py +100 -0
- trilogy/ai/providers/__init__.py +0 -0
- trilogy/ai/providers/anthropic.py +106 -0
- trilogy/ai/providers/base.py +24 -0
- trilogy/ai/providers/google.py +146 -0
- trilogy/ai/providers/openai.py +89 -0
- trilogy/ai/providers/utils.py +68 -0
- trilogy/authoring/README.md +3 -0
- trilogy/authoring/__init__.py +148 -0
- trilogy/constants.py +113 -0
- trilogy/core/README.md +52 -0
- trilogy/core/__init__.py +0 -0
- trilogy/core/constants.py +6 -0
- trilogy/core/enums.py +443 -0
- trilogy/core/env_processor.py +120 -0
- trilogy/core/environment_helpers.py +320 -0
- trilogy/core/ergonomics.py +193 -0
- trilogy/core/exceptions.py +123 -0
- trilogy/core/functions.py +1227 -0
- trilogy/core/graph_models.py +139 -0
- trilogy/core/internal.py +85 -0
- trilogy/core/models/__init__.py +0 -0
- trilogy/core/models/author.py +2669 -0
- trilogy/core/models/build.py +2521 -0
- trilogy/core/models/build_environment.py +180 -0
- trilogy/core/models/core.py +501 -0
- trilogy/core/models/datasource.py +322 -0
- trilogy/core/models/environment.py +751 -0
- trilogy/core/models/execute.py +1177 -0
- trilogy/core/optimization.py +251 -0
- trilogy/core/optimizations/__init__.py +12 -0
- trilogy/core/optimizations/base_optimization.py +17 -0
- trilogy/core/optimizations/hide_unused_concept.py +47 -0
- trilogy/core/optimizations/inline_datasource.py +102 -0
- trilogy/core/optimizations/predicate_pushdown.py +245 -0
- trilogy/core/processing/README.md +94 -0
- trilogy/core/processing/READMEv2.md +121 -0
- trilogy/core/processing/VIRTUAL_UNNEST.md +30 -0
- trilogy/core/processing/__init__.py +0 -0
- trilogy/core/processing/concept_strategies_v3.py +508 -0
- trilogy/core/processing/constants.py +15 -0
- trilogy/core/processing/discovery_node_factory.py +451 -0
- trilogy/core/processing/discovery_utility.py +548 -0
- trilogy/core/processing/discovery_validation.py +167 -0
- trilogy/core/processing/graph_utils.py +43 -0
- trilogy/core/processing/node_generators/README.md +9 -0
- trilogy/core/processing/node_generators/__init__.py +31 -0
- trilogy/core/processing/node_generators/basic_node.py +160 -0
- trilogy/core/processing/node_generators/common.py +268 -0
- trilogy/core/processing/node_generators/constant_node.py +38 -0
- trilogy/core/processing/node_generators/filter_node.py +315 -0
- trilogy/core/processing/node_generators/group_node.py +213 -0
- trilogy/core/processing/node_generators/group_to_node.py +117 -0
- trilogy/core/processing/node_generators/multiselect_node.py +205 -0
- trilogy/core/processing/node_generators/node_merge_node.py +653 -0
- trilogy/core/processing/node_generators/recursive_node.py +88 -0
- trilogy/core/processing/node_generators/rowset_node.py +165 -0
- trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +261 -0
- trilogy/core/processing/node_generators/select_merge_node.py +748 -0
- trilogy/core/processing/node_generators/select_node.py +95 -0
- trilogy/core/processing/node_generators/synonym_node.py +98 -0
- trilogy/core/processing/node_generators/union_node.py +91 -0
- trilogy/core/processing/node_generators/unnest_node.py +182 -0
- trilogy/core/processing/node_generators/window_node.py +201 -0
- trilogy/core/processing/nodes/README.md +28 -0
- trilogy/core/processing/nodes/__init__.py +179 -0
- trilogy/core/processing/nodes/base_node.py +519 -0
- trilogy/core/processing/nodes/filter_node.py +75 -0
- trilogy/core/processing/nodes/group_node.py +194 -0
- trilogy/core/processing/nodes/merge_node.py +420 -0
- trilogy/core/processing/nodes/recursive_node.py +46 -0
- trilogy/core/processing/nodes/select_node_v2.py +242 -0
- trilogy/core/processing/nodes/union_node.py +53 -0
- trilogy/core/processing/nodes/unnest_node.py +62 -0
- trilogy/core/processing/nodes/window_node.py +56 -0
- trilogy/core/processing/utility.py +823 -0
- trilogy/core/query_processor.py +596 -0
- trilogy/core/statements/README.md +35 -0
- trilogy/core/statements/__init__.py +0 -0
- trilogy/core/statements/author.py +536 -0
- trilogy/core/statements/build.py +0 -0
- trilogy/core/statements/common.py +20 -0
- trilogy/core/statements/execute.py +155 -0
- trilogy/core/table_processor.py +66 -0
- trilogy/core/utility.py +8 -0
- trilogy/core/validation/README.md +46 -0
- trilogy/core/validation/__init__.py +0 -0
- trilogy/core/validation/common.py +161 -0
- trilogy/core/validation/concept.py +146 -0
- trilogy/core/validation/datasource.py +227 -0
- trilogy/core/validation/environment.py +73 -0
- trilogy/core/validation/fix.py +256 -0
- trilogy/dialect/__init__.py +32 -0
- trilogy/dialect/base.py +1392 -0
- trilogy/dialect/bigquery.py +308 -0
- trilogy/dialect/common.py +147 -0
- trilogy/dialect/config.py +144 -0
- trilogy/dialect/dataframe.py +50 -0
- trilogy/dialect/duckdb.py +231 -0
- trilogy/dialect/enums.py +147 -0
- trilogy/dialect/metadata.py +173 -0
- trilogy/dialect/mock.py +190 -0
- trilogy/dialect/postgres.py +117 -0
- trilogy/dialect/presto.py +110 -0
- trilogy/dialect/results.py +89 -0
- trilogy/dialect/snowflake.py +129 -0
- trilogy/dialect/sql_server.py +137 -0
- trilogy/engine.py +48 -0
- trilogy/execution/config.py +75 -0
- trilogy/executor.py +568 -0
- trilogy/hooks/__init__.py +4 -0
- trilogy/hooks/base_hook.py +40 -0
- trilogy/hooks/graph_hook.py +139 -0
- trilogy/hooks/query_debugger.py +166 -0
- trilogy/metadata/__init__.py +0 -0
- trilogy/parser.py +10 -0
- trilogy/parsing/README.md +21 -0
- trilogy/parsing/__init__.py +0 -0
- trilogy/parsing/common.py +1069 -0
- trilogy/parsing/config.py +5 -0
- trilogy/parsing/exceptions.py +8 -0
- trilogy/parsing/helpers.py +1 -0
- trilogy/parsing/parse_engine.py +2813 -0
- trilogy/parsing/render.py +769 -0
- trilogy/parsing/trilogy.lark +540 -0
- trilogy/py.typed +0 -0
- trilogy/render.py +42 -0
- trilogy/scripts/README.md +9 -0
- trilogy/scripts/__init__.py +0 -0
- trilogy/scripts/agent.py +41 -0
- trilogy/scripts/agent_info.py +303 -0
- trilogy/scripts/common.py +355 -0
- trilogy/scripts/dependency/Cargo.lock +617 -0
- trilogy/scripts/dependency/Cargo.toml +39 -0
- trilogy/scripts/dependency/README.md +131 -0
- trilogy/scripts/dependency/build.sh +25 -0
- trilogy/scripts/dependency/src/directory_resolver.rs +177 -0
- trilogy/scripts/dependency/src/lib.rs +16 -0
- trilogy/scripts/dependency/src/main.rs +770 -0
- trilogy/scripts/dependency/src/parser.rs +435 -0
- trilogy/scripts/dependency/src/preql.pest +208 -0
- trilogy/scripts/dependency/src/python_bindings.rs +303 -0
- trilogy/scripts/dependency/src/resolver.rs +716 -0
- trilogy/scripts/dependency/tests/base.preql +3 -0
- trilogy/scripts/dependency/tests/cli_integration.rs +377 -0
- trilogy/scripts/dependency/tests/customer.preql +6 -0
- trilogy/scripts/dependency/tests/main.preql +9 -0
- trilogy/scripts/dependency/tests/orders.preql +7 -0
- trilogy/scripts/dependency/tests/test_data/base.preql +9 -0
- trilogy/scripts/dependency/tests/test_data/consumer.preql +1 -0
- trilogy/scripts/dependency.py +323 -0
- trilogy/scripts/display.py +512 -0
- trilogy/scripts/environment.py +46 -0
- trilogy/scripts/fmt.py +32 -0
- trilogy/scripts/ingest.py +471 -0
- trilogy/scripts/ingest_helpers/__init__.py +1 -0
- trilogy/scripts/ingest_helpers/foreign_keys.py +123 -0
- trilogy/scripts/ingest_helpers/formatting.py +93 -0
- trilogy/scripts/ingest_helpers/typing.py +161 -0
- trilogy/scripts/init.py +105 -0
- trilogy/scripts/parallel_execution.py +713 -0
- trilogy/scripts/plan.py +189 -0
- trilogy/scripts/run.py +63 -0
- trilogy/scripts/serve.py +140 -0
- trilogy/scripts/serve_helpers/__init__.py +41 -0
- trilogy/scripts/serve_helpers/file_discovery.py +142 -0
- trilogy/scripts/serve_helpers/index_generation.py +206 -0
- trilogy/scripts/serve_helpers/models.py +38 -0
- trilogy/scripts/single_execution.py +131 -0
- trilogy/scripts/testing.py +119 -0
- trilogy/scripts/trilogy.py +68 -0
- trilogy/std/__init__.py +0 -0
- trilogy/std/color.preql +3 -0
- trilogy/std/date.preql +13 -0
- trilogy/std/display.preql +18 -0
- trilogy/std/geography.preql +22 -0
- trilogy/std/metric.preql +15 -0
- trilogy/std/money.preql +67 -0
- trilogy/std/net.preql +14 -0
- trilogy/std/ranking.preql +7 -0
- trilogy/std/report.preql +5 -0
- trilogy/std/semantic.preql +6 -0
- trilogy/utility.py +34 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, ItemsView, List, Optional, Union, ValuesView
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, ValidationInfo, field_validator
|
|
4
|
+
|
|
5
|
+
from trilogy.constants import DEFAULT_NAMESPACE, logger
|
|
6
|
+
from trilogy.core.enums import DatasourceState, Modifier
|
|
7
|
+
from trilogy.core.models.author import (
|
|
8
|
+
Concept,
|
|
9
|
+
ConceptRef,
|
|
10
|
+
Function,
|
|
11
|
+
Grain,
|
|
12
|
+
HasUUID,
|
|
13
|
+
LooseConceptList,
|
|
14
|
+
Namespaced,
|
|
15
|
+
WhereClause,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
LOGGER_PREFIX = "[MODELS_DATASOURCE]"
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RawColumnExpr(BaseModel):
|
|
25
|
+
text: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ColumnAssignment(BaseModel):
|
|
29
|
+
alias: str | RawColumnExpr | Function
|
|
30
|
+
concept: ConceptRef
|
|
31
|
+
modifiers: List[Modifier] = Field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
@field_validator("concept", mode="before")
|
|
34
|
+
def force_reference(cls, v: ConceptRef, info: ValidationInfo):
|
|
35
|
+
if isinstance(v, Concept):
|
|
36
|
+
return v.reference
|
|
37
|
+
return v
|
|
38
|
+
|
|
39
|
+
def __eq__(self, other):
|
|
40
|
+
if not isinstance(other, ColumnAssignment):
|
|
41
|
+
return False
|
|
42
|
+
return (
|
|
43
|
+
self.alias == other.alias
|
|
44
|
+
and self.concept == other.concept
|
|
45
|
+
and self.modifiers == other.modifiers
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def is_concrete(self) -> bool:
|
|
50
|
+
return isinstance(self.alias, str)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def is_complete(self) -> bool:
|
|
54
|
+
return Modifier.PARTIAL not in self.modifiers
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_nullable(self) -> bool:
|
|
58
|
+
return Modifier.NULLABLE in self.modifiers
|
|
59
|
+
|
|
60
|
+
def with_namespace(self, namespace: str) -> "ColumnAssignment":
|
|
61
|
+
return ColumnAssignment.model_construct(
|
|
62
|
+
alias=(
|
|
63
|
+
self.alias.with_namespace(namespace)
|
|
64
|
+
if isinstance(self.alias, Function)
|
|
65
|
+
else self.alias
|
|
66
|
+
),
|
|
67
|
+
concept=self.concept.with_namespace(namespace),
|
|
68
|
+
modifiers=self.modifiers,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def with_merge(
|
|
72
|
+
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
73
|
+
) -> "ColumnAssignment":
|
|
74
|
+
return ColumnAssignment.model_construct(
|
|
75
|
+
alias=self.alias,
|
|
76
|
+
concept=self.concept.with_merge(source, target, modifiers),
|
|
77
|
+
modifiers=(
|
|
78
|
+
modifiers if self.concept.address == source.address else self.modifiers
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Address(BaseModel):
|
|
84
|
+
location: str
|
|
85
|
+
is_query: bool = False
|
|
86
|
+
quoted: bool = False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Query(BaseModel):
|
|
90
|
+
text: str
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DatasourceMetadata(BaseModel):
|
|
94
|
+
freshness_concept: Concept | None
|
|
95
|
+
partition_fields: List[Concept] = Field(default_factory=list)
|
|
96
|
+
line_no: int | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def safe_grain(v) -> Grain:
|
|
100
|
+
if isinstance(v, dict):
|
|
101
|
+
return Grain.model_validate(v)
|
|
102
|
+
elif isinstance(v, Grain):
|
|
103
|
+
return v
|
|
104
|
+
elif not v:
|
|
105
|
+
return Grain(components=set())
|
|
106
|
+
else:
|
|
107
|
+
raise ValueError(f"Invalid input type to safe_grain {type(v)}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Datasource(HasUUID, Namespaced, BaseModel):
|
|
111
|
+
name: str
|
|
112
|
+
columns: List[ColumnAssignment]
|
|
113
|
+
address: Union[Address, str]
|
|
114
|
+
grain: Grain = Field(
|
|
115
|
+
default_factory=lambda: Grain(components=set()), validate_default=True
|
|
116
|
+
)
|
|
117
|
+
namespace: Optional[str] = Field(default=DEFAULT_NAMESPACE, validate_default=True)
|
|
118
|
+
metadata: DatasourceMetadata = Field(
|
|
119
|
+
default_factory=lambda: DatasourceMetadata(freshness_concept=None)
|
|
120
|
+
)
|
|
121
|
+
where: Optional[WhereClause] = None
|
|
122
|
+
non_partial_for: Optional[WhereClause] = None
|
|
123
|
+
status: DatasourceState = Field(default=DatasourceState.PUBLISHED)
|
|
124
|
+
incremental_by: List[ConceptRef] = Field(default_factory=list)
|
|
125
|
+
partition_by: List[ConceptRef] = Field(default_factory=list)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def safe_address(self) -> str:
|
|
129
|
+
if isinstance(self.address, Address):
|
|
130
|
+
return self.address.location
|
|
131
|
+
return self.address
|
|
132
|
+
|
|
133
|
+
def __eq__(self, other):
|
|
134
|
+
if not isinstance(other, Datasource):
|
|
135
|
+
return False
|
|
136
|
+
return (
|
|
137
|
+
self.name == other.name
|
|
138
|
+
and self.namespace == other.namespace
|
|
139
|
+
and self.grain == other.grain
|
|
140
|
+
and self.address == other.address
|
|
141
|
+
and self.where == other.where
|
|
142
|
+
and self.columns == other.columns
|
|
143
|
+
and self.non_partial_for == other.non_partial_for
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def duplicate(self) -> "Datasource":
|
|
147
|
+
return self.model_copy(deep=True)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def concrete_columns(self) -> dict[str, ColumnAssignment]:
|
|
151
|
+
return {c.alias: c for c in self.columns if c.is_concrete} # type: ignore[misc]
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def hidden_concepts(self) -> List[Concept]:
|
|
155
|
+
return []
|
|
156
|
+
|
|
157
|
+
def merge_concept(
|
|
158
|
+
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
159
|
+
):
|
|
160
|
+
original = [c for c in self.columns if c.concept.address == source.address]
|
|
161
|
+
early_exit_check = [
|
|
162
|
+
c for c in self.columns if c.concept.address == target.address
|
|
163
|
+
]
|
|
164
|
+
if early_exit_check:
|
|
165
|
+
logger.info(
|
|
166
|
+
f"No concept merge needed on merge of {source} to {target}, have {[x.concept.address for x in self.columns]}"
|
|
167
|
+
)
|
|
168
|
+
return None
|
|
169
|
+
if len(original) != 1:
|
|
170
|
+
raise ValueError(
|
|
171
|
+
f"Expected exactly one column to merge, got {len(original)} for {source.address}, {[x.alias for x in original]}"
|
|
172
|
+
)
|
|
173
|
+
# map to the alias with the modifier, and the original
|
|
174
|
+
self.columns = [
|
|
175
|
+
c.with_merge(source, target, modifiers)
|
|
176
|
+
for c in self.columns
|
|
177
|
+
if c.concept.address != source.address
|
|
178
|
+
] + original
|
|
179
|
+
self.grain = self.grain.with_merge(source, target, modifiers)
|
|
180
|
+
self.where = (
|
|
181
|
+
self.where.with_merge(source, target, modifiers) if self.where else None
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.add_column(target, original[0].alias, modifiers)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def identifier(self) -> str:
|
|
188
|
+
if not self.namespace or self.namespace == DEFAULT_NAMESPACE:
|
|
189
|
+
return self.name
|
|
190
|
+
return f"{self.namespace}.{self.name}"
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def safe_identifier(self) -> str:
|
|
194
|
+
return self.identifier.replace(".", "_")
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def output_lcl(self) -> LooseConceptList:
|
|
198
|
+
return LooseConceptList(concepts=self.output_concepts)
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def non_partial_concept_addresses(self) -> set[str]:
|
|
202
|
+
return set([c.address for c in self.full_concepts])
|
|
203
|
+
|
|
204
|
+
@field_validator("namespace", mode="plain")
|
|
205
|
+
@classmethod
|
|
206
|
+
def namespace_validation(cls, v):
|
|
207
|
+
return v or DEFAULT_NAMESPACE
|
|
208
|
+
|
|
209
|
+
@field_validator("address")
|
|
210
|
+
@classmethod
|
|
211
|
+
def address_enforcement(cls, v):
|
|
212
|
+
if isinstance(v, str):
|
|
213
|
+
v = Address(location=v)
|
|
214
|
+
return v
|
|
215
|
+
|
|
216
|
+
@field_validator("grain", mode="before")
|
|
217
|
+
@classmethod
|
|
218
|
+
def grain_enforcement(cls, v: Grain, info: ValidationInfo):
|
|
219
|
+
grain: Grain = safe_grain(v)
|
|
220
|
+
return grain
|
|
221
|
+
|
|
222
|
+
def add_column(
|
|
223
|
+
self,
|
|
224
|
+
concept: Concept,
|
|
225
|
+
alias: str | RawColumnExpr | Function,
|
|
226
|
+
modifiers: List[Modifier] | None = None,
|
|
227
|
+
):
|
|
228
|
+
self.columns.append(
|
|
229
|
+
ColumnAssignment(
|
|
230
|
+
alias=alias, concept=concept.reference, modifiers=modifiers or []
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def __add__(self, other):
|
|
235
|
+
if not other == self:
|
|
236
|
+
raise ValueError(
|
|
237
|
+
"Attempted to add two datasources that are not identical, this is not a valid operation"
|
|
238
|
+
)
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
def __repr__(self):
|
|
242
|
+
return f"Datasource<{self.identifier}@<{self.grain}>"
|
|
243
|
+
|
|
244
|
+
def __str__(self):
|
|
245
|
+
return self.__repr__()
|
|
246
|
+
|
|
247
|
+
def __hash__(self):
|
|
248
|
+
return self.identifier.__hash__()
|
|
249
|
+
|
|
250
|
+
def with_namespace(self, namespace: str):
|
|
251
|
+
new_namespace = (
|
|
252
|
+
namespace + "." + self.namespace
|
|
253
|
+
if self.namespace and self.namespace != DEFAULT_NAMESPACE
|
|
254
|
+
else namespace
|
|
255
|
+
)
|
|
256
|
+
new = Datasource.model_construct(
|
|
257
|
+
name=self.name,
|
|
258
|
+
namespace=new_namespace,
|
|
259
|
+
grain=self.grain.with_namespace(namespace),
|
|
260
|
+
address=self.address,
|
|
261
|
+
columns=[c.with_namespace(namespace) for c in self.columns],
|
|
262
|
+
where=self.where.with_namespace(namespace) if self.where else None,
|
|
263
|
+
non_partial_for=(
|
|
264
|
+
self.non_partial_for.with_namespace(namespace)
|
|
265
|
+
if self.non_partial_for
|
|
266
|
+
else None
|
|
267
|
+
),
|
|
268
|
+
status=self.status,
|
|
269
|
+
incremental_by=[c.with_namespace(namespace) for c in self.incremental_by],
|
|
270
|
+
partition_by=[c.with_namespace(namespace) for c in self.partition_by],
|
|
271
|
+
)
|
|
272
|
+
return new
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def concepts(self) -> List[ConceptRef]:
|
|
276
|
+
return [c.concept for c in self.columns]
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def group_required(self):
|
|
280
|
+
return False
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def full_concepts(self) -> List[ConceptRef]:
|
|
284
|
+
return [c.concept for c in self.columns if Modifier.PARTIAL not in c.modifiers]
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def nullable_concepts(self) -> List[ConceptRef]:
|
|
288
|
+
return [c.concept for c in self.columns if Modifier.NULLABLE in c.modifiers]
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def output_concepts(self) -> List[ConceptRef]:
|
|
292
|
+
return self.concepts
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def partial_concepts(self) -> List[ConceptRef]:
|
|
296
|
+
return [c.concept for c in self.columns if Modifier.PARTIAL in c.modifiers]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class EnvironmentDatasourceDict(dict):
|
|
300
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
301
|
+
super().__init__(self, *args, **kwargs)
|
|
302
|
+
|
|
303
|
+
def __getitem__(self, key: str) -> Datasource:
|
|
304
|
+
try:
|
|
305
|
+
return super(EnvironmentDatasourceDict, self).__getitem__(key)
|
|
306
|
+
except KeyError:
|
|
307
|
+
if DEFAULT_NAMESPACE + "." + key in self:
|
|
308
|
+
return self.__getitem__(DEFAULT_NAMESPACE + "." + key)
|
|
309
|
+
if "." in key and key.split(".", 1)[0] == DEFAULT_NAMESPACE:
|
|
310
|
+
return self.__getitem__(key.split(".", 1)[1])
|
|
311
|
+
raise
|
|
312
|
+
|
|
313
|
+
def values(self) -> ValuesView[Datasource]: # type: ignore
|
|
314
|
+
return super().values()
|
|
315
|
+
|
|
316
|
+
def items(self) -> ItemsView[str, Datasource]: # type: ignore
|
|
317
|
+
return super().items()
|
|
318
|
+
|
|
319
|
+
def duplicate(self) -> "EnvironmentDatasourceDict":
|
|
320
|
+
new = EnvironmentDatasourceDict()
|
|
321
|
+
new.update({k: v.duplicate() for k, v in self.items()})
|
|
322
|
+
return new
|