pytrilogy 0.3.138__cp311-cp311-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-311-x86_64-linux-gnu.so +0 -0
- pytrilogy-0.3.138.dist-info/METADATA +525 -0
- pytrilogy-0.3.138.dist-info/RECORD +182 -0
- pytrilogy-0.3.138.dist-info/WHEEL +5 -0
- pytrilogy-0.3.138.dist-info/entry_points.txt +2 -0
- pytrilogy-0.3.138.dist-info/licenses/LICENSE.md +19 -0
- trilogy/__init__.py +9 -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 +87 -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 +143 -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 +2672 -0
- trilogy/core/models/build.py +2521 -0
- trilogy/core/models/build_environment.py +180 -0
- trilogy/core/models/core.py +494 -0
- trilogy/core/models/datasource.py +322 -0
- trilogy/core/models/environment.py +748 -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 +517 -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 +106 -0
- trilogy/dialect/__init__.py +32 -0
- trilogy/dialect/base.py +1359 -0
- trilogy/dialect/bigquery.py +256 -0
- trilogy/dialect/common.py +147 -0
- trilogy/dialect/config.py +144 -0
- trilogy/dialect/dataframe.py +50 -0
- trilogy/dialect/duckdb.py +177 -0
- trilogy/dialect/enums.py +147 -0
- trilogy/dialect/metadata.py +173 -0
- trilogy/dialect/mock.py +190 -0
- trilogy/dialect/postgres.py +91 -0
- trilogy/dialect/presto.py +104 -0
- trilogy/dialect/results.py +89 -0
- trilogy/dialect/snowflake.py +90 -0
- trilogy/dialect/sql_server.py +92 -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 +750 -0
- trilogy/parsing/trilogy.lark +540 -0
- trilogy/py.typed +0 -0
- trilogy/render.py +42 -0
- trilogy/scripts/README.md +7 -0
- trilogy/scripts/__init__.py +0 -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 +162 -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 +289 -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 +460 -0
- trilogy/scripts/environment.py +46 -0
- trilogy/scripts/parallel_execution.py +483 -0
- trilogy/scripts/single_execution.py +131 -0
- trilogy/scripts/trilogy.py +772 -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,73 @@
|
|
|
1
|
+
from trilogy import Environment, Executor
|
|
2
|
+
from trilogy.authoring import DataType, Function
|
|
3
|
+
from trilogy.core.enums import FunctionType, Purpose, ValidationScope
|
|
4
|
+
from trilogy.core.exceptions import (
|
|
5
|
+
ModelValidationError,
|
|
6
|
+
)
|
|
7
|
+
from trilogy.core.validation.common import ValidationTest
|
|
8
|
+
from trilogy.core.validation.concept import validate_concept
|
|
9
|
+
from trilogy.core.validation.datasource import validate_datasource
|
|
10
|
+
from trilogy.parsing.common import function_to_concept
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_environment(
|
|
14
|
+
env: Environment,
|
|
15
|
+
scope: ValidationScope = ValidationScope.ALL,
|
|
16
|
+
targets: list[str] | None = None,
|
|
17
|
+
exec: Executor | None = None,
|
|
18
|
+
generate_only: bool = False,
|
|
19
|
+
) -> list[ValidationTest]:
|
|
20
|
+
# avoid mutating the environment for validation
|
|
21
|
+
generate_only = exec is None or generate_only
|
|
22
|
+
env = env.duplicate()
|
|
23
|
+
grain_check = function_to_concept(
|
|
24
|
+
parent=Function(
|
|
25
|
+
operator=FunctionType.SUM,
|
|
26
|
+
arguments=[1],
|
|
27
|
+
output_datatype=DataType.INTEGER,
|
|
28
|
+
output_purpose=Purpose.METRIC,
|
|
29
|
+
),
|
|
30
|
+
name="grain_check",
|
|
31
|
+
environment=env,
|
|
32
|
+
)
|
|
33
|
+
env.add_concept(grain_check)
|
|
34
|
+
new_concepts = []
|
|
35
|
+
for concept in env.concepts.values():
|
|
36
|
+
concept_grain_check = function_to_concept(
|
|
37
|
+
parent=Function(
|
|
38
|
+
operator=FunctionType.COUNT_DISTINCT,
|
|
39
|
+
arguments=[concept.reference],
|
|
40
|
+
output_datatype=DataType.INTEGER,
|
|
41
|
+
output_purpose=Purpose.METRIC,
|
|
42
|
+
),
|
|
43
|
+
name=f"grain_check_{concept.safe_address}",
|
|
44
|
+
environment=env,
|
|
45
|
+
)
|
|
46
|
+
new_concepts.append(concept_grain_check)
|
|
47
|
+
for concept in new_concepts:
|
|
48
|
+
env.add_concept(concept)
|
|
49
|
+
build_env = env.materialize_for_select()
|
|
50
|
+
results: list[ValidationTest] = []
|
|
51
|
+
if scope == ValidationScope.ALL or scope == ValidationScope.DATASOURCES:
|
|
52
|
+
for datasource in build_env.datasources.values():
|
|
53
|
+
if targets and datasource.name not in targets:
|
|
54
|
+
continue
|
|
55
|
+
results += validate_datasource(datasource, env, build_env, exec)
|
|
56
|
+
if scope == ValidationScope.ALL or scope == ValidationScope.CONCEPTS:
|
|
57
|
+
|
|
58
|
+
for bconcept in build_env.concepts.values():
|
|
59
|
+
if targets and bconcept.address not in targets:
|
|
60
|
+
continue
|
|
61
|
+
results += validate_concept(bconcept, env, build_env, exec)
|
|
62
|
+
|
|
63
|
+
# raise a nicely formatted union of all exceptions
|
|
64
|
+
exceptions: list[ModelValidationError] = [e.result for e in results if e.result]
|
|
65
|
+
if exceptions:
|
|
66
|
+
if not generate_only:
|
|
67
|
+
messages = "\n".join([str(e) for e in exceptions])
|
|
68
|
+
raise ModelValidationError(
|
|
69
|
+
f"Environment validation failed with the following errors:\n{messages}",
|
|
70
|
+
children=exceptions,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return results
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from trilogy import Environment, Executor
|
|
6
|
+
from trilogy.authoring import ConceptDeclarationStatement, Datasource
|
|
7
|
+
from trilogy.core.exceptions import (
|
|
8
|
+
DatasourceColumnBindingData,
|
|
9
|
+
DatasourceColumnBindingError,
|
|
10
|
+
)
|
|
11
|
+
from trilogy.core.validation.environment import validate_environment
|
|
12
|
+
from trilogy.parsing.render import Renderer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def rewrite_file_with_errors(
|
|
16
|
+
statements: list[Any], errors: list[DatasourceColumnBindingError]
|
|
17
|
+
):
|
|
18
|
+
renderer = Renderer()
|
|
19
|
+
output = []
|
|
20
|
+
ds_error_map: dict[str, list[DatasourceColumnBindingData]] = defaultdict(list)
|
|
21
|
+
concept_error_map: dict[str, list[DatasourceColumnBindingData]] = defaultdict(list)
|
|
22
|
+
for error in errors:
|
|
23
|
+
if isinstance(error, DatasourceColumnBindingError):
|
|
24
|
+
for x in error.errors:
|
|
25
|
+
if error.dataset_address not in ds_error_map:
|
|
26
|
+
ds_error_map[error.dataset_address] = []
|
|
27
|
+
# this is by dataset address
|
|
28
|
+
if x.is_modifier_issue():
|
|
29
|
+
ds_error_map[error.dataset_address].append(x)
|
|
30
|
+
# this is by column
|
|
31
|
+
if x.is_type_issue():
|
|
32
|
+
concept_error_map[x.address].append(x)
|
|
33
|
+
for statement in statements:
|
|
34
|
+
if isinstance(statement, Datasource):
|
|
35
|
+
if statement.identifier in ds_error_map:
|
|
36
|
+
error_cols = ds_error_map[statement.identifier]
|
|
37
|
+
for col in statement.columns:
|
|
38
|
+
if col.concept.address in [x.address for x in error_cols]:
|
|
39
|
+
error_col = [
|
|
40
|
+
x for x in error_cols if x.address == col.concept.address
|
|
41
|
+
][0]
|
|
42
|
+
col.modifiers = list(
|
|
43
|
+
set(col.modifiers + error_col.value_modifiers)
|
|
44
|
+
)
|
|
45
|
+
elif isinstance(statement, ConceptDeclarationStatement):
|
|
46
|
+
if statement.concept.address in concept_error_map:
|
|
47
|
+
error_cols = concept_error_map[statement.concept.address]
|
|
48
|
+
statement.concept.datatype = error_cols[0].value_type
|
|
49
|
+
output.append(statement)
|
|
50
|
+
|
|
51
|
+
return renderer.render_statement_string(output)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
DEPTH_CUTOFF = 3
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_and_rewrite(
|
|
58
|
+
input: Path | str, exec: Executor | None = None, depth: int = 0
|
|
59
|
+
) -> str | None:
|
|
60
|
+
if depth > DEPTH_CUTOFF:
|
|
61
|
+
print(f"Reached depth cutoff of {DEPTH_CUTOFF}, stopping.")
|
|
62
|
+
return None
|
|
63
|
+
if isinstance(input, str):
|
|
64
|
+
raw = input
|
|
65
|
+
env = Environment()
|
|
66
|
+
else:
|
|
67
|
+
with open(input, "r") as f:
|
|
68
|
+
raw = f.read()
|
|
69
|
+
env = Environment(working_path=input.parent)
|
|
70
|
+
if exec:
|
|
71
|
+
env = exec.environment
|
|
72
|
+
env, statements = env.parse(raw)
|
|
73
|
+
|
|
74
|
+
validation_results = validate_environment(env, exec=exec, generate_only=True)
|
|
75
|
+
|
|
76
|
+
errors = [
|
|
77
|
+
x.result
|
|
78
|
+
for x in validation_results
|
|
79
|
+
if isinstance(x.result, DatasourceColumnBindingError)
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
if not errors:
|
|
83
|
+
print("No validation errors found")
|
|
84
|
+
return None
|
|
85
|
+
print(
|
|
86
|
+
f"Found {len(errors)} validation errors, attempting to fix, current depth: {depth}..."
|
|
87
|
+
)
|
|
88
|
+
for error in errors:
|
|
89
|
+
for item in error.errors:
|
|
90
|
+
print(f"- {item.format_failure()}")
|
|
91
|
+
|
|
92
|
+
new_text = rewrite_file_with_errors(statements, errors)
|
|
93
|
+
|
|
94
|
+
while iteration := validate_and_rewrite(new_text, exec=exec, depth=depth + 1):
|
|
95
|
+
depth = depth + 1
|
|
96
|
+
if depth >= DEPTH_CUTOFF:
|
|
97
|
+
break
|
|
98
|
+
if iteration:
|
|
99
|
+
new_text = iteration
|
|
100
|
+
depth += 1
|
|
101
|
+
if isinstance(input, Path):
|
|
102
|
+
with open(input, "w") as f:
|
|
103
|
+
f.write(new_text)
|
|
104
|
+
return None
|
|
105
|
+
else:
|
|
106
|
+
return new_text
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .bigquery import BigqueryDialect
|
|
2
|
+
from .config import (
|
|
3
|
+
BigQueryConfig,
|
|
4
|
+
DialectConfig,
|
|
5
|
+
DuckDBConfig,
|
|
6
|
+
PostgresConfig,
|
|
7
|
+
PrestoConfig,
|
|
8
|
+
SnowflakeConfig,
|
|
9
|
+
SQLServerConfig,
|
|
10
|
+
)
|
|
11
|
+
from .duckdb import DuckDBDialect
|
|
12
|
+
from .postgres import PostgresDialect
|
|
13
|
+
from .presto import PrestoDialect
|
|
14
|
+
from .snowflake import SnowflakeDialect
|
|
15
|
+
from .sql_server import SqlServerDialect
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"BigqueryDialect",
|
|
19
|
+
"PrestoDialect",
|
|
20
|
+
"DuckDBDialect",
|
|
21
|
+
"SnowflakeDialect",
|
|
22
|
+
"PostgresDialect",
|
|
23
|
+
"SqlServerDialect",
|
|
24
|
+
"SQLServerConfig",
|
|
25
|
+
"DialectConfig",
|
|
26
|
+
"DuckDBConfig",
|
|
27
|
+
"BigQueryConfig",
|
|
28
|
+
"SnowflakeConfig",
|
|
29
|
+
"PrestoConfig",
|
|
30
|
+
"PostgresConfig",
|
|
31
|
+
"DialectConfig",
|
|
32
|
+
]
|