pytrilogy 0.0.3.10__tar.gz → 0.0.3.12__tar.gz

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.

Files changed (133) hide show
  1. {pytrilogy-0.0.3.10/pytrilogy.egg-info → pytrilogy-0.0.3.12}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pytrilogy.egg-info/SOURCES.txt +2 -0
  4. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_parsing.py +43 -1
  5. pytrilogy-0.0.3.12/tests/test_query_render.py +13 -0
  6. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/authoring/__init__.py +8 -1
  8. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/environment.py +18 -3
  9. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/group_node.py +1 -1
  10. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/bigquery.py +2 -3
  11. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/duckdb.py +2 -2
  12. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/enums.py +5 -0
  13. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/presto.py +1 -1
  14. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/sql_server.py +1 -1
  15. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/engine.py +21 -1
  16. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/executor.py +2 -34
  17. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/parse_engine.py +35 -8
  18. pytrilogy-0.0.3.12/trilogy/render.py +38 -0
  19. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/LICENSE.md +0 -0
  20. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/README.md +0 -0
  21. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pyproject.toml +0 -0
  22. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pytrilogy.egg-info/dependency_links.txt +0 -0
  23. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pytrilogy.egg-info/entry_points.txt +0 -0
  24. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pytrilogy.egg-info/requires.txt +0 -0
  25. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/pytrilogy.egg-info/top_level.txt +0 -0
  26. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/setup.cfg +0 -0
  27. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/setup.py +0 -0
  28. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_datatypes.py +0 -0
  29. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_declarations.py +0 -0
  30. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_derived_concepts.py +0 -0
  31. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_discovery_nodes.py +0 -0
  32. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_enums.py +0 -0
  33. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_environment.py +0 -0
  34. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_executor.py +0 -0
  35. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_functions.py +0 -0
  36. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_imports.py +0 -0
  37. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_metadata.py +0 -0
  38. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_models.py +0 -0
  39. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_multi_join_assignments.py +0 -0
  40. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_parse_engine.py +0 -0
  41. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_partial_handling.py +0 -0
  42. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_query_processing.py +0 -0
  43. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_select.py +0 -0
  44. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_show.py +0 -0
  45. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_statements.py +0 -0
  46. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_typing.py +0 -0
  47. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_undefined_concept.py +0 -0
  48. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_user_functions.py +0 -0
  49. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/tests/test_where_clause.py +0 -0
  50. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/compiler.py +0 -0
  51. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/constants.py +0 -0
  52. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/__init__.py +0 -0
  53. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/constants.py +0 -0
  54. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/enums.py +0 -0
  55. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/env_processor.py +0 -0
  56. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/environment_helpers.py +0 -0
  57. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/ergonomics.py +0 -0
  58. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/exceptions.py +0 -0
  59. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/functions.py +0 -0
  60. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/graph_models.py +0 -0
  61. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/internal.py +0 -0
  62. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/__init__.py +0 -0
  63. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/author.py +0 -0
  64. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/build.py +0 -0
  65. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/build_environment.py +0 -0
  66. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/core.py +0 -0
  67. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/datasource.py +0 -0
  68. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/models/execute.py +0 -0
  69. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimization.py +0 -0
  70. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimizations/__init__.py +0 -0
  71. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimizations/base_optimization.py +0 -0
  72. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimizations/inline_constant.py +0 -0
  73. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimizations/inline_datasource.py +0 -0
  74. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  75. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/__init__.py +0 -0
  76. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  77. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/graph_utils.py +0 -0
  78. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/__init__.py +0 -0
  79. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  80. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/common.py +0 -0
  81. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  82. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/group_node.py +0 -0
  83. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  84. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  85. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  86. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  87. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  88. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  89. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  90. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/select_node.py +0 -0
  91. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  92. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/union_node.py +0 -0
  93. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  94. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/node_generators/window_node.py +0 -0
  95. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/__init__.py +0 -0
  96. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/base_node.py +0 -0
  97. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/filter_node.py +0 -0
  98. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/merge_node.py +0 -0
  99. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  100. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/union_node.py +0 -0
  101. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  102. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/nodes/window_node.py +0 -0
  103. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/processing/utility.py +0 -0
  104. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/query_processor.py +0 -0
  105. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/statements/__init__.py +0 -0
  106. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/statements/author.py +0 -0
  107. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/statements/build.py +0 -0
  108. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/statements/common.py +0 -0
  109. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/core/statements/execute.py +0 -0
  110. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/__init__.py +0 -0
  111. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/base.py +0 -0
  112. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/common.py +0 -0
  113. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/config.py +0 -0
  114. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/dataframe.py +0 -0
  115. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/postgres.py +0 -0
  116. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/dialect/snowflake.py +0 -0
  117. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/hooks/__init__.py +0 -0
  118. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/hooks/base_hook.py +0 -0
  119. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/hooks/graph_hook.py +0 -0
  120. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/hooks/query_debugger.py +0 -0
  121. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/metadata/__init__.py +0 -0
  122. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parser.py +0 -0
  123. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/__init__.py +0 -0
  124. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/common.py +0 -0
  125. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/config.py +0 -0
  126. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/exceptions.py +0 -0
  127. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/helpers.py +0 -0
  128. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/render.py +0 -0
  129. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/parsing/trilogy.lark +0 -0
  130. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/py.typed +0 -0
  131. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/scripts/__init__.py +0 -0
  132. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/scripts/trilogy.py +0 -0
  133. {pytrilogy-0.0.3.10 → pytrilogy-0.0.3.12}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.10
3
+ Version: 0.0.3.12
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.10
3
+ Version: 0.0.3.12
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -24,6 +24,7 @@ tests/test_parse_engine.py
24
24
  tests/test_parsing.py
25
25
  tests/test_partial_handling.py
26
26
  tests/test_query_processing.py
27
+ tests/test_query_render.py
27
28
  tests/test_select.py
28
29
  tests/test_show.py
29
30
  tests/test_statements.py
@@ -38,6 +39,7 @@ trilogy/engine.py
38
39
  trilogy/executor.py
39
40
  trilogy/parser.py
40
41
  trilogy/py.typed
42
+ trilogy/render.py
41
43
  trilogy/utility.py
42
44
  trilogy/authoring/__init__.py
43
45
  trilogy/core/__init__.py
@@ -8,7 +8,11 @@ from trilogy.core.models.core import (
8
8
  TupleWrapper,
9
9
  )
10
10
  from trilogy.core.models.datasource import Datasource
11
- from trilogy.core.models.environment import Environment
11
+ from trilogy.core.models.environment import (
12
+ DictImportResolver,
13
+ Environment,
14
+ EnvironmentOptions,
15
+ )
12
16
  from trilogy.core.statements.author import SelectStatement, ShowStatement
13
17
  from trilogy.core.statements.execute import ProcessedQuery
14
18
  from trilogy.dialect.base import BaseDialect
@@ -619,3 +623,41 @@ select [1,2,3,4] as int_array, 2 as scalar
619
623
 
620
624
  env, parsed = parse_text(x)
621
625
  assert env.concepts["split"].datatype == DataType.INTEGER
626
+
627
+
628
+ def test_non_file_imports():
629
+
630
+ env = Environment(
631
+ config=EnvironmentOptions(
632
+ import_resolver=DictImportResolver(
633
+ content={
634
+ "test": """
635
+ import test_dep as test_dep;
636
+ key x int;
637
+
638
+ datasource test (
639
+ x: x)
640
+ grain(x)
641
+ query '''
642
+ select 1 as x
643
+ union all
644
+ select 11 as x
645
+ ''';
646
+ """,
647
+ "test_dep": """
648
+ key x int;
649
+ """,
650
+ }
651
+ )
652
+ )
653
+ )
654
+ assert isinstance(env.config.import_resolver, DictImportResolver)
655
+ env.parse(
656
+ """
657
+ import test;
658
+
659
+ select x % 10 -> x_mod_10;
660
+
661
+
662
+ """
663
+ )
@@ -0,0 +1,13 @@
1
+ from trilogy import Dialects, Environment
2
+ from trilogy.core.query_processor import process_query
3
+ from trilogy.render import get_dialect_generator
4
+
5
+
6
+ def test_sql_generators():
7
+ env = Environment()
8
+ _, statements = env.parse("""const a <- 1; select a;""")
9
+ processed = process_query(env, statements[-1])
10
+ for dialect in Dialects:
11
+ generator = get_dialect_generator(dialect)
12
+ compiled = generator.compile_statement(processed)
13
+ assert compiled.startswith("""SELECT\n :a"""), f"{dialect} compiled"
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.10"
7
+ __version__ = "0.0.3.12"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -33,7 +33,13 @@ from trilogy.core.models.author import (
33
33
  WindowOrder,
34
34
  WindowType,
35
35
  )
36
- from trilogy.core.models.core import DataType, ListType, ListWrapper, StructType
36
+ from trilogy.core.models.core import (
37
+ DataType,
38
+ ListType,
39
+ ListWrapper,
40
+ MapType,
41
+ StructType,
42
+ )
37
43
  from trilogy.core.models.environment import Environment
38
44
  from trilogy.core.statements.author import (
39
45
  ConceptDeclarationStatement,
@@ -63,6 +69,7 @@ __all__ = [
63
69
  "DataType",
64
70
  "StructType",
65
71
  "ListType",
72
+ "MapType",
66
73
  "ListWrapper",
67
74
  "FunctionType",
68
75
  "FunctionFactory",
@@ -60,8 +60,23 @@ class Import:
60
60
  path: Path
61
61
 
62
62
 
63
+ class BaseImportResolver(BaseModel):
64
+ pass
65
+
66
+
67
+ class FileSystemImportResolver(BaseImportResolver):
68
+ pass
69
+
70
+
71
+ class DictImportResolver(BaseImportResolver):
72
+ content: Dict[str, str]
73
+
74
+
63
75
  class EnvironmentOptions(BaseModel):
64
76
  allow_duplicate_declaration: bool = True
77
+ import_resolver: BaseImportResolver = Field(
78
+ default_factory=FileSystemImportResolver
79
+ )
65
80
 
66
81
 
67
82
  class EnvironmentConceptDict(dict):
@@ -199,7 +214,7 @@ class Environment(BaseModel):
199
214
  )
200
215
  namespace: str = DEFAULT_NAMESPACE
201
216
  working_path: str | Path = Field(default_factory=lambda: os.getcwd())
202
- environment_config: EnvironmentOptions = Field(default_factory=EnvironmentOptions)
217
+ config: EnvironmentOptions = Field(default_factory=EnvironmentOptions)
203
218
  version: str = Field(default_factory=get_version)
204
219
  cte_name_map: Dict[str, str] = Field(default_factory=dict)
205
220
  materialized_concepts: set[str] = Field(default_factory=set)
@@ -234,7 +249,7 @@ class Environment(BaseModel):
234
249
  imports=dict(self.imports),
235
250
  namespace=self.namespace,
236
251
  working_path=self.working_path,
237
- environment_config=self.environment_config,
252
+ environment_config=self.config,
238
253
  version=self.version,
239
254
  cte_name_map=dict(self.cte_name_map),
240
255
  materialized_concepts=set(self.materialized_concepts),
@@ -342,7 +357,7 @@ class Environment(BaseModel):
342
357
 
343
358
  return None
344
359
 
345
- if existing and self.environment_config.allow_duplicate_declaration:
360
+ if existing and self.config.allow_duplicate_declaration:
346
361
  if existing.metadata.concept_source == ConceptSource.PERSIST_STATEMENT:
347
362
  return handle_persist()
348
363
  return
@@ -92,7 +92,7 @@ class GroupNode(StrategyNode):
92
92
  comp_grain += source.grain
93
93
  for x in source.output_concepts:
94
94
  concept_map[x.address] = x
95
- lookups = [
95
+ lookups: list[BuildConcept | str] = [
96
96
  concept_map[x] if x in concept_map else x for x in comp_grain.components
97
97
  ]
98
98
  comp_grain = BuildGrain.from_concepts(lookups, environment=environment)
@@ -42,10 +42,9 @@ CREATE OR REPLACE TABLE {{ output.address.location }} AS
42
42
  {% endif %}{%- if ctes %}
43
43
  WITH {% for cte in ctes %}
44
44
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
45
- {%- if full_select %}
45
+ {%- if full_select -%}
46
46
  {{full_select}}
47
- {% else -%}
48
-
47
+ {%- else -%}
49
48
  SELECT
50
49
  {%- for select in select_columns %}
51
50
  {{ select }}{% if not loop.last %},{% endif %}{% endfor %}
@@ -57,8 +57,8 @@ CREATE OR REPLACE TABLE {{ output.address.location }} AS
57
57
  WITH {% for cte in ctes %}
58
58
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
59
59
  {%- if full_select -%}{{full_select}}
60
- {%- else -%}{%- if comment %}
61
- -- {{ comment }}{% endif %}
60
+ {%- else -%}{%- if comment -%}
61
+ -- {{ comment }}{%- endif -%}
62
62
  SELECT
63
63
  {%- for select in select_columns %}
64
64
  {{ select }}{% if not loop.last %},{% endif %}{% endfor %}
@@ -41,6 +41,11 @@ class Dialects(Enum):
41
41
  return cls.DUCK_DB
42
42
  return super()._missing_(value)
43
43
 
44
+ def default_renderer(self, conf=None, _engine_factory: Callable = default_factory):
45
+ from trilogy.render import get_dialect_generator
46
+
47
+ return get_dialect_generator(self)
48
+
44
49
  def default_engine(self, conf=None, _engine_factory: Callable = default_factory):
45
50
  if self == Dialects.BIGQUERY:
46
51
  from google.auth import default
@@ -50,7 +50,7 @@ WITH {% for cte in ctes %}
50
50
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
51
51
  {%- if full_select -%}
52
52
  {{full_select}}
53
- {%- else %}
53
+ {%- else -%}
54
54
  SELECT
55
55
  {%- for select in select_columns %}
56
56
  {{ select }}{% if not loop.last %},{% endif %}{% endfor %}
@@ -42,7 +42,7 @@ WITH {% for cte in ctes %}
42
42
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
43
43
  {%- if full_select -%}{{full_select}}
44
44
  {%- else -%}{%- if comment %}
45
- -- {{ comment }}{% endif %}
45
+ -- {{ comment }}{%- endif -%}
46
46
  SELECT
47
47
  {%- if limit is not none %}
48
48
  TOP {{ limit }}{% endif %}
@@ -18,6 +18,15 @@ class EngineConnection(Protocol):
18
18
  def execute(self, statement: str, parameters: Any | None = None) -> EngineResult:
19
19
  pass
20
20
 
21
+ def commit(self):
22
+ raise NotImplementedError()
23
+
24
+ def begin(self):
25
+ raise NotImplementedError()
26
+
27
+ def rollback(self):
28
+ raise NotImplementedError()
29
+
21
30
 
22
31
  class ExecutionEngine(Protocol):
23
32
  pass
@@ -40,13 +49,24 @@ class SqlAlchemyResult(EngineResult):
40
49
 
41
50
  class SqlAlchemyConnection(EngineConnection):
42
51
  def __init__(self, connection: Connection):
43
- self.connection = connection
52
+ from sqlalchemy.future import Connection
53
+
54
+ self.connection: Connection = connection
44
55
 
45
56
  def execute(
46
57
  self, statement: str, parameters: Any | None = None
47
58
  ) -> SqlAlchemyResult:
48
59
  return SqlAlchemyResult(self.connection.execute(statement, parameters))
49
60
 
61
+ def commit(self):
62
+ self.connection.commit()
63
+
64
+ def begin(self):
65
+ self.connection.begin()
66
+
67
+ def rollback(self):
68
+ self.connection.rollback()
69
+
50
70
 
51
71
  class SqlAlchemyEngine(ExecutionEngine):
52
72
  def __init__(self, engine: Engine):
@@ -36,6 +36,7 @@ from trilogy.dialect.enums import Dialects
36
36
  from trilogy.engine import ExecutionEngine
37
37
  from trilogy.hooks.base_hook import BaseHook
38
38
  from trilogy.parser import parse_text
39
+ from trilogy.render import get_dialect_generator
39
40
 
40
41
 
41
42
  class ResultProtocol(Protocol):
@@ -82,40 +83,7 @@ class Executor(object):
82
83
  self.generator: BaseDialect
83
84
  self.logger = logger
84
85
  self.hooks = hooks
85
- if self.dialect == Dialects.BIGQUERY:
86
- from trilogy.dialect.bigquery import BigqueryDialect
87
-
88
- self.generator = BigqueryDialect()
89
- elif self.dialect == Dialects.SQL_SERVER:
90
- from trilogy.dialect.sql_server import SqlServerDialect
91
-
92
- self.generator = SqlServerDialect()
93
- elif self.dialect == Dialects.DUCK_DB:
94
- from trilogy.dialect.duckdb import DuckDBDialect
95
-
96
- self.generator = DuckDBDialect()
97
- elif self.dialect == Dialects.PRESTO:
98
- from trilogy.dialect.presto import PrestoDialect
99
-
100
- self.generator = PrestoDialect()
101
- elif self.dialect == Dialects.TRINO:
102
- from trilogy.dialect.presto import TrinoDialect
103
-
104
- self.generator = TrinoDialect()
105
- elif self.dialect == Dialects.POSTGRES:
106
- from trilogy.dialect.postgres import PostgresDialect
107
-
108
- self.generator = PostgresDialect()
109
- elif self.dialect == Dialects.SNOWFLAKE:
110
- from trilogy.dialect.snowflake import SnowflakeDialect
111
-
112
- self.generator = SnowflakeDialect()
113
- elif self.dialect == Dialects.DATAFRAME:
114
- from trilogy.dialect.dataframe import DataframeDialect
115
-
116
- self.generator = DataframeDialect()
117
- else:
118
- raise ValueError(f"Unsupported dialect {self.dialect}")
86
+ self.generator = get_dialect_generator(self.dialect)
119
87
  self.connection = self.engine.connect()
120
88
  # TODO: make generic
121
89
  if self.dialect == Dialects.DATAFRAME:
@@ -97,7 +97,12 @@ from trilogy.core.models.datasource import (
97
97
  Query,
98
98
  RawColumnExpr,
99
99
  )
100
- from trilogy.core.models.environment import Environment, Import
100
+ from trilogy.core.models.environment import (
101
+ DictImportResolver,
102
+ Environment,
103
+ FileSystemImportResolver,
104
+ Import,
105
+ )
101
106
  from trilogy.core.statements.author import (
102
107
  ConceptDeclarationStatement,
103
108
  ConceptDerivationStatement,
@@ -223,7 +228,7 @@ class ParseToObjects(Transformer):
223
228
  self,
224
229
  environment: Environment,
225
230
  parse_address: str | None = None,
226
- token_address: Path | None = None,
231
+ token_address: Path | str | None = None,
227
232
  parsed: dict[str, "ParseToObjects"] | None = None,
228
233
  tokens: dict[Path | str, ParseTree] | None = None,
229
234
  text_lookup: dict[Path | str, str] | None = None,
@@ -854,8 +859,22 @@ class ParseToObjects(Transformer):
854
859
  )
855
860
 
856
861
  def resolve_import_address(self, address) -> str:
857
- with open(address, "r", encoding="utf-8") as f:
858
- text = f.read()
862
+ if isinstance(
863
+ self.environment.config.import_resolver, FileSystemImportResolver
864
+ ):
865
+ with open(address, "r", encoding="utf-8") as f:
866
+ text = f.read()
867
+ elif isinstance(self.environment.config.import_resolver, DictImportResolver):
868
+ lookup = address
869
+ if lookup not in self.environment.config.import_resolver.content:
870
+ raise ImportError(
871
+ f"Unable to import file {lookup}, not found in import resolver"
872
+ )
873
+ text = self.environment.config.import_resolver.content[lookup]
874
+ else:
875
+ raise ImportError(
876
+ f"Unable to import file {address}, resolver type {type(self.environment.config.import_resolver)} not supported"
877
+ )
859
878
  return text
860
879
 
861
880
  def import_statement(self, args: list[str]) -> ImportStatement:
@@ -867,10 +886,17 @@ class ParseToObjects(Transformer):
867
886
  cache_key = args[0]
868
887
  path = args[0].split(".")
869
888
 
870
- target = join(self.environment.working_path, *path) + ".preql"
871
-
872
- # tokens + text are cached by path
873
- token_lookup = Path(target)
889
+ if isinstance(
890
+ self.environment.config.import_resolver, FileSystemImportResolver
891
+ ):
892
+ target = join(self.environment.working_path, *path) + ".preql"
893
+ # tokens + text are cached by path
894
+ token_lookup: Path | str = Path(target)
895
+ elif isinstance(self.environment.config.import_resolver, DictImportResolver):
896
+ target = ".".join(path)
897
+ token_lookup = target
898
+ else:
899
+ raise NotImplementedError
874
900
 
875
901
  # parser + env has to be cached by prior import path + current key
876
902
  key_path = self.import_keys + [cache_key]
@@ -901,6 +927,7 @@ class ParseToObjects(Transformer):
901
927
  new_env = Environment(
902
928
  working_path=dirname(target),
903
929
  env_file_path=token_lookup,
930
+ config=self.environment.config,
904
931
  )
905
932
  new_env.concepts.fail_on_missing = False
906
933
  self.parsed[self.parse_address] = self
@@ -0,0 +1,38 @@
1
+ from trilogy.dialect.enums import Dialects
2
+
3
+
4
+ def get_dialect_generator(dialect: Dialects):
5
+ if dialect == Dialects.BIGQUERY:
6
+ from trilogy.dialect.bigquery import BigqueryDialect
7
+
8
+ return BigqueryDialect()
9
+ elif dialect == Dialects.SQL_SERVER:
10
+ from trilogy.dialect.sql_server import SqlServerDialect
11
+
12
+ return SqlServerDialect()
13
+ elif dialect == Dialects.DUCK_DB:
14
+ from trilogy.dialect.duckdb import DuckDBDialect
15
+
16
+ return DuckDBDialect()
17
+ elif dialect == Dialects.PRESTO:
18
+ from trilogy.dialect.presto import PrestoDialect
19
+
20
+ return PrestoDialect()
21
+ elif dialect == Dialects.TRINO:
22
+ from trilogy.dialect.presto import TrinoDialect
23
+
24
+ return TrinoDialect()
25
+ elif dialect == Dialects.POSTGRES:
26
+ from trilogy.dialect.postgres import PostgresDialect
27
+
28
+ return PostgresDialect()
29
+ elif dialect == Dialects.SNOWFLAKE:
30
+ from trilogy.dialect.snowflake import SnowflakeDialect
31
+
32
+ return SnowflakeDialect()
33
+ elif dialect == Dialects.DATAFRAME:
34
+ from trilogy.dialect.dataframe import DataframeDialect
35
+
36
+ return DataframeDialect()
37
+ else:
38
+ raise ValueError(f"Unsupported dialect {dialect}")
File without changes
File without changes
File without changes
File without changes