pytrilogy 0.0.3.95__tar.gz → 0.0.3.96__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 (157) hide show
  1. {pytrilogy-0.0.3.95/pytrilogy.egg-info → pytrilogy-0.0.3.96}/PKG-INFO +30 -7
  2. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/README.md +29 -6
  3. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96/pytrilogy.egg-info}/PKG-INFO +30 -7
  4. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/SOURCES.txt +1 -1
  5. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_executor.py +1 -1
  6. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/authoring/__init__.py +59 -45
  8. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/graph_models.py +4 -4
  9. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/execute.py +2 -0
  10. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/common.py +2 -1
  11. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/concept.py +24 -21
  12. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/datasource.py +16 -14
  13. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/environment.py +4 -4
  14. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/base.py +9 -1
  15. pytrilogy-0.0.3.96/trilogy/dialect/metadata.py +233 -0
  16. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/executor.py +33 -163
  17. pytrilogy-0.0.3.95/trilogy/compiler.py +0 -0
  18. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/LICENSE.md +0 -0
  19. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pyproject.toml +0 -0
  20. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/dependency_links.txt +0 -0
  21. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/entry_points.txt +0 -0
  22. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/requires.txt +0 -0
  23. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/pytrilogy.egg-info/top_level.txt +0 -0
  24. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/setup.cfg +0 -0
  25. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/setup.py +0 -0
  26. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_datatypes.py +0 -0
  27. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_declarations.py +0 -0
  28. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_derived_concepts.py +0 -0
  29. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_discovery_nodes.py +0 -0
  30. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_enums.py +0 -0
  31. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_environment.py +0 -0
  32. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_execute_models.py +0 -0
  33. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_failure.py +0 -0
  34. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_functions.py +0 -0
  35. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_imports.py +0 -0
  36. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_metadata.py +0 -0
  37. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_models.py +0 -0
  38. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_multi_join_assignments.py +0 -0
  39. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parse_engine.py +0 -0
  40. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parsing.py +0 -0
  41. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_parsing_failures.py +0 -0
  42. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_partial_handling.py +0 -0
  43. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_query_processing.py +0 -0
  44. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_query_render.py +0 -0
  45. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_select.py +0 -0
  46. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_show.py +0 -0
  47. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_statements.py +0 -0
  48. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_typing.py +0 -0
  49. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_undefined_concept.py +0 -0
  50. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_user_functions.py +0 -0
  51. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/tests/test_where_clause.py +0 -0
  52. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/constants.py +0 -0
  53. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/__init__.py +0 -0
  54. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/constants.py +0 -0
  55. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/enums.py +0 -0
  56. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/env_processor.py +0 -0
  57. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/environment_helpers.py +0 -0
  58. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/ergonomics.py +0 -0
  59. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/exceptions.py +0 -0
  60. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/functions.py +0 -0
  61. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/internal.py +0 -0
  62. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/__init__.py +0 -0
  63. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/author.py +0 -0
  64. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/build.py +0 -0
  65. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/build_environment.py +0 -0
  66. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/core.py +0 -0
  67. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/datasource.py +0 -0
  68. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/environment.py +0 -0
  69. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/models/execute.py +0 -0
  70. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimization.py +0 -0
  71. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/__init__.py +0 -0
  72. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/base_optimization.py +0 -0
  73. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/inline_datasource.py +0 -0
  74. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  75. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/__init__.py +0 -0
  76. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  77. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_loop.py +0 -0
  78. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_node_factory.py +0 -0
  79. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_utility.py +0 -0
  80. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/discovery_validation.py +0 -0
  81. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/graph_utils.py +0 -0
  82. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/__init__.py +0 -0
  83. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  84. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/common.py +0 -0
  85. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/constant_node.py +0 -0
  86. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  87. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/group_node.py +0 -0
  88. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  89. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  90. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  91. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
  92. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  93. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  94. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  95. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  96. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/select_node.py +0 -0
  97. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  98. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/union_node.py +0 -0
  99. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  100. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/node_generators/window_node.py +0 -0
  101. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/__init__.py +0 -0
  102. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/base_node.py +0 -0
  103. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/filter_node.py +0 -0
  104. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/group_node.py +0 -0
  105. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/merge_node.py +0 -0
  106. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/recursive_node.py +0 -0
  107. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  108. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/union_node.py +0 -0
  109. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  110. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/nodes/window_node.py +0 -0
  111. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/processing/utility.py +0 -0
  112. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/query_processor.py +0 -0
  113. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/__init__.py +0 -0
  114. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/author.py +0 -0
  115. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/build.py +0 -0
  116. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/statements/common.py +0 -0
  117. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/utility.py +0 -0
  118. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/core/validation/__init__.py +0 -0
  119. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/__init__.py +0 -0
  120. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/bigquery.py +0 -0
  121. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/common.py +0 -0
  122. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/config.py +0 -0
  123. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/dataframe.py +0 -0
  124. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/duckdb.py +0 -0
  125. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/enums.py +0 -0
  126. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/postgres.py +0 -0
  127. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/presto.py +0 -0
  128. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/snowflake.py +0 -0
  129. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/dialect/sql_server.py +0 -0
  130. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/engine.py +0 -0
  131. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/__init__.py +0 -0
  132. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/base_hook.py +0 -0
  133. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/graph_hook.py +0 -0
  134. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/hooks/query_debugger.py +0 -0
  135. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/metadata/__init__.py +0 -0
  136. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parser.py +0 -0
  137. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/__init__.py +0 -0
  138. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/common.py +0 -0
  139. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/config.py +0 -0
  140. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/exceptions.py +0 -0
  141. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/helpers.py +0 -0
  142. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/parse_engine.py +0 -0
  143. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/render.py +0 -0
  144. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/parsing/trilogy.lark +0 -0
  145. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/py.typed +0 -0
  146. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/render.py +0 -0
  147. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/scripts/__init__.py +0 -0
  148. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/scripts/trilogy.py +0 -0
  149. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/__init__.py +0 -0
  150. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/date.preql +0 -0
  151. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/display.preql +0 -0
  152. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/geography.preql +0 -0
  153. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/money.preql +0 -0
  154. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/net.preql +0 -0
  155. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/ranking.preql +0 -0
  156. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/std/report.preql +0 -0
  157. {pytrilogy-0.0.3.95 → pytrilogy-0.0.3.96}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.95
3
+ Version: 0.0.3.96
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -136,11 +136,11 @@ Versus SQL, Trilogy aims to:
136
136
 
137
137
  | Backend | Status | Notes |
138
138
  |---------|--------|-------|
139
- | **BigQuery** | Core | Full support |
140
- | **DuckDB** | Core | Full support |
141
- | **Snowflake** | Core | Full support |
142
- | **SQL Server** | ⚠️ Experimental | Limited testing |
143
- | **Presto** | ⚠️ Experimental | Limited testing |
139
+ | **BigQuery** | Core | Full support |
140
+ | **DuckDB** | Core | Full support |
141
+ | **Snowflake** | Core | Full support |
142
+ | **SQL Server** | Experimental | Limited testing |
143
+ | **Presto** | Experimental | Limited testing |
144
144
 
145
145
  ## Examples
146
146
 
@@ -311,7 +311,30 @@ trilogy fmt <path to trilogy file>
311
311
  - [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
312
312
  - [Full documentation](https://trilogydata.dev/)
313
313
 
314
- ## Syntax Reference
314
+ ## Python API Integration
315
+
316
+ ### Root Imports
317
+
318
+ Are stable and should be sufficient for executing code from Trilogy as text.
319
+
320
+ ```python
321
+ from pytrilogy import Executor, Dialect
322
+ ```
323
+
324
+ ### Authoring Imports
325
+
326
+ Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
327
+ or need to process/transform parsed code in more complicated ways.
328
+
329
+ ```python
330
+ from pytrilogy.authoring import Concept, Function, ...
331
+ ```
332
+
333
+ ### Other Imports
334
+
335
+ Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
336
+
337
+ ## Trilogy Syntax Reference
315
338
 
316
339
  ### Import
317
340
  ```sql
@@ -98,11 +98,11 @@ Versus SQL, Trilogy aims to:
98
98
 
99
99
  | Backend | Status | Notes |
100
100
  |---------|--------|-------|
101
- | **BigQuery** | Core | Full support |
102
- | **DuckDB** | Core | Full support |
103
- | **Snowflake** | Core | Full support |
104
- | **SQL Server** | ⚠️ Experimental | Limited testing |
105
- | **Presto** | ⚠️ Experimental | Limited testing |
101
+ | **BigQuery** | Core | Full support |
102
+ | **DuckDB** | Core | Full support |
103
+ | **Snowflake** | Core | Full support |
104
+ | **SQL Server** | Experimental | Limited testing |
105
+ | **Presto** | Experimental | Limited testing |
106
106
 
107
107
  ## Examples
108
108
 
@@ -273,7 +273,30 @@ trilogy fmt <path to trilogy file>
273
273
  - [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
274
274
  - [Full documentation](https://trilogydata.dev/)
275
275
 
276
- ## Syntax Reference
276
+ ## Python API Integration
277
+
278
+ ### Root Imports
279
+
280
+ Are stable and should be sufficient for executing code from Trilogy as text.
281
+
282
+ ```python
283
+ from pytrilogy import Executor, Dialect
284
+ ```
285
+
286
+ ### Authoring Imports
287
+
288
+ Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
289
+ or need to process/transform parsed code in more complicated ways.
290
+
291
+ ```python
292
+ from pytrilogy.authoring import Concept, Function, ...
293
+ ```
294
+
295
+ ### Other Imports
296
+
297
+ Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
298
+
299
+ ## Trilogy Syntax Reference
277
300
 
278
301
  ### Import
279
302
  ```sql
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.95
3
+ Version: 0.0.3.96
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -136,11 +136,11 @@ Versus SQL, Trilogy aims to:
136
136
 
137
137
  | Backend | Status | Notes |
138
138
  |---------|--------|-------|
139
- | **BigQuery** | Core | Full support |
140
- | **DuckDB** | Core | Full support |
141
- | **Snowflake** | Core | Full support |
142
- | **SQL Server** | ⚠️ Experimental | Limited testing |
143
- | **Presto** | ⚠️ Experimental | Limited testing |
139
+ | **BigQuery** | Core | Full support |
140
+ | **DuckDB** | Core | Full support |
141
+ | **Snowflake** | Core | Full support |
142
+ | **SQL Server** | Experimental | Limited testing |
143
+ | **Presto** | Experimental | Limited testing |
144
144
 
145
145
  ## Examples
146
146
 
@@ -311,7 +311,30 @@ trilogy fmt <path to trilogy file>
311
311
  - [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
312
312
  - [Full documentation](https://trilogydata.dev/)
313
313
 
314
- ## Syntax Reference
314
+ ## Python API Integration
315
+
316
+ ### Root Imports
317
+
318
+ Are stable and should be sufficient for executing code from Trilogy as text.
319
+
320
+ ```python
321
+ from pytrilogy import Executor, Dialect
322
+ ```
323
+
324
+ ### Authoring Imports
325
+
326
+ Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
327
+ or need to process/transform parsed code in more complicated ways.
328
+
329
+ ```python
330
+ from pytrilogy.authoring import Concept, Function, ...
331
+ ```
332
+
333
+ ### Other Imports
334
+
335
+ Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
336
+
337
+ ## Trilogy Syntax Reference
315
338
 
316
339
  ### Import
317
340
  ```sql
@@ -36,7 +36,6 @@ tests/test_undefined_concept.py
36
36
  tests/test_user_functions.py
37
37
  tests/test_where_clause.py
38
38
  trilogy/__init__.py
39
- trilogy/compiler.py
40
39
  trilogy/constants.py
41
40
  trilogy/engine.py
42
41
  trilogy/executor.py
@@ -125,6 +124,7 @@ trilogy/dialect/config.py
125
124
  trilogy/dialect/dataframe.py
126
125
  trilogy/dialect/duckdb.py
127
126
  trilogy/dialect/enums.py
127
+ trilogy/dialect/metadata.py
128
128
  trilogy/dialect/postgres.py
129
129
  trilogy/dialect/presto.py
130
130
  trilogy/dialect/snowflake.py
@@ -2,7 +2,7 @@ from pathlib import Path
2
2
 
3
3
  from trilogy import Dialects, parse
4
4
  from trilogy.core.models.environment import Environment
5
- from trilogy.executor import MockResult, MockResultRow
5
+ from trilogy.dialect.metadata import MockResult, MockResultRow
6
6
 
7
7
 
8
8
  def test_file_parsing():
@@ -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.95"
7
+ __version__ = "0.0.3.96"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -60,68 +60,82 @@ from trilogy.core.statements.author import (
60
60
  RowsetDerivationStatement,
61
61
  SelectItem,
62
62
  SelectStatement,
63
+ ShowCategory,
64
+ ShowStatement,
65
+ ValidateStatement,
63
66
  )
64
67
  from trilogy.parsing.common import arbitrary_to_concept, arg_to_datatype
65
68
 
66
69
  __all__ = [
67
- "Concept",
68
- "Function",
69
- "WhereClause",
70
- "Comparison",
71
- "FilterItem",
72
- "CaseWhen",
73
- "CaseElse",
74
- "AggregateWrapper",
75
- "WindowItem",
76
- "WindowOrder",
77
- "WindowType",
78
- "WindowItemOrder",
79
- "WindowItemOver",
80
- "DataType",
81
- "StructType",
82
- "ArrayType",
83
- "NumericType",
84
- "Grain",
85
- "RowsetDerivationStatement",
86
- "MapType",
87
- "ListWrapper",
70
+ # trilogy.constants
71
+ "DEFAULT_NAMESPACE",
72
+ # trilogy.core.enums
73
+ "BooleanOperator",
74
+ "ComparisonOperator",
75
+ "FunctionClass",
88
76
  "FunctionType",
77
+ "InfiniteFunctionArgs",
78
+ "Ordering",
79
+ "Purpose",
80
+ # trilogy.core.functions
89
81
  "FunctionFactory",
90
- "ConceptDeclarationStatement",
91
- "ConceptTransform",
92
- "SelectItem",
93
- "SelectStatement",
94
- "Environment",
82
+ # trilogy.core.models.author
83
+ "AggregateWrapper",
84
+ "CaseElse",
85
+ "CaseWhen",
86
+ "Comparison",
87
+ "Concept",
95
88
  "ConceptRef",
89
+ "Conditional",
90
+ "FilterItem",
91
+ "Function",
92
+ "FunctionCallWrapper",
96
93
  "HavingClause",
97
94
  "MagicConstants",
98
95
  "Metadata",
96
+ "MultiSelectLineage",
99
97
  "OrderBy",
100
98
  "OrderItem",
101
99
  "Parenthetical",
100
+ "RowsetItem",
102
101
  "SubselectComparison",
103
- "Conditional",
104
- "BooleanOperator",
105
- "ComparisonOperator",
106
- "FunctionClass",
107
- "FunctionType",
108
- "InfiniteFunctionArgs",
109
- "Ordering",
110
- "Purpose",
111
- "DEFAULT_NAMESPACE",
112
- "arbitrary_to_concept",
113
- "arg_to_datatype",
114
- "MultiSelectStatement",
115
- "PersistStatement",
116
- "RawSQLStatement",
102
+ "WhereClause",
103
+ "WindowItem",
104
+ "WindowItemOrder",
105
+ "WindowItemOver",
106
+ "WindowOrder",
107
+ "WindowType",
108
+ # trilogy.core.models.core
109
+ "ArrayType",
110
+ "DataType",
111
+ "ListWrapper",
112
+ "MapType",
113
+ "NumericType",
114
+ "StructType",
115
+ "TraitDataType",
116
+ # trilogy.core.models.datasource
117
+ "Address",
117
118
  "Datasource",
118
119
  "DatasourceMetadata",
119
- "MultiSelectLineage",
120
- "RowsetItem",
121
- "FunctionCallWrapper",
120
+ # trilogy.core.models.environment
121
+ "Environment",
122
+ # trilogy.core.statements.author
123
+ "ConceptDeclarationStatement",
124
+ "ConceptTransform",
122
125
  "CopyStatement",
126
+ "Grain",
123
127
  "HasUUID",
124
128
  "ImportStatement",
125
- "Address",
126
- "TraitDataType",
129
+ "MultiSelectStatement",
130
+ "PersistStatement",
131
+ "RawSQLStatement",
132
+ "RowsetDerivationStatement",
133
+ "SelectItem",
134
+ "SelectStatement",
135
+ "ShowCategory",
136
+ "ShowStatement",
137
+ "ValidateStatement",
138
+ # trilogy.parsing.common
139
+ "arbitrary_to_concept",
140
+ "arg_to_datatype",
127
141
  ]
@@ -64,13 +64,13 @@ def datasource_to_node(input: BuildDatasource) -> str:
64
64
 
65
65
 
66
66
  class ReferenceGraph(nx.DiGraph):
67
- def __init__(self, *args, **kwargs):
67
+ def __init__(self, *args, **kwargs) -> None:
68
68
  super().__init__(*args, **kwargs)
69
69
  self.concepts: dict[str, BuildConcept] = {}
70
70
  self.datasources: dict[str, BuildDatasource] = {}
71
71
  self.pseudonyms: set[tuple[str, str]] = set()
72
72
 
73
- def copy(self):
73
+ def copy(self) -> "ReferenceGraph":
74
74
  g = ReferenceGraph()
75
75
  g.concepts = self.concepts.copy()
76
76
  g.datasources = self.datasources.copy()
@@ -83,7 +83,7 @@ class ReferenceGraph(nx.DiGraph):
83
83
  # g.add_edges_from(self.edges(data=True))
84
84
  return g
85
85
 
86
- def remove_node(self, n):
86
+ def remove_node(self, n) -> None:
87
87
  if n in self.concepts:
88
88
  del self.concepts[n]
89
89
  if n in self.datasources:
@@ -98,7 +98,7 @@ class ReferenceGraph(nx.DiGraph):
98
98
  self.datasources[node_name] = attr["datasource"]
99
99
  super().add_node(node_name, **attr)
100
100
 
101
- def add_datasource_node(self, node_name, datasource):
101
+ def add_datasource_node(self, node_name, datasource) -> None:
102
102
  self.datasources[node_name] = datasource
103
103
  super().add_node(node_name, datasource=datasource)
104
104
 
@@ -88,6 +88,8 @@ class ProcessedShowStatement:
88
88
  BuildConcept,
89
89
  BuildDatasource,
90
90
  ProcessedQuery,
91
+ ProcessedQueryPersist,
92
+ ProcessedCopyStatement,
91
93
  ProcessedValidateStatement,
92
94
  ProcessedStaticValueOutput,
93
95
  ]
@@ -27,7 +27,8 @@ class ExpectationType(Enum):
27
27
  @dataclass
28
28
  class ValidationTest:
29
29
  check_type: ExpectationType
30
- query: str | None = None
30
+ raw_query: ProcessedQuery | None = None
31
+ generated_query: str | None = None
31
32
  expected: str | None = None
32
33
  result: ModelValidationError | None = None
33
34
  ran: bool = True
@@ -1,4 +1,4 @@
1
- from trilogy import Executor
1
+ from trilogy import Environment, Executor
2
2
  from trilogy.core.enums import Derivation, Purpose
3
3
  from trilogy.core.exceptions import (
4
4
  ConceptModelValidationError,
@@ -12,64 +12,68 @@ from trilogy.core.validation.common import ExpectationType, ValidationTest, easy
12
12
 
13
13
 
14
14
  def validate_property_concept(
15
- concept: BuildConcept, generate_only: bool = False
15
+ concept: BuildConcept, exec: Executor | None = None
16
16
  ) -> list[ValidationTest]:
17
17
  return []
18
18
 
19
19
 
20
20
  def validate_key_concept(
21
21
  concept: BuildConcept,
22
+ env: Environment,
22
23
  build_env: BuildEnvironment,
23
- exec: Executor,
24
- generate_only: bool = False,
24
+ exec: Executor | None = None,
25
25
  ):
26
26
  results: list[ValidationTest] = []
27
- seen = {}
27
+ seen: dict[str, int] = {}
28
28
  for datasource in build_env.datasources.values():
29
29
  if concept.address in [c.address for c in datasource.concepts]:
30
30
  assignment = [
31
31
  x for x in datasource.columns if x.concept.address == concept.address
32
32
  ][0]
33
+ # if it's not a partial, skip it
34
+ if not assignment.is_complete:
35
+ continue
33
36
  type_query = easy_query(
34
37
  concepts=[
35
38
  # build_env.concepts[concept.address],
36
39
  build_env.concepts[f"grain_check_{concept.safe_address}"],
37
40
  ],
38
41
  datasource=datasource,
39
- env=exec.environment,
42
+ env=env,
40
43
  limit=1,
41
44
  )
42
- type_sql = exec.generate_sql(type_query)[-1]
45
+ if exec:
46
+ type_sql = exec.generate_sql(type_query)[-1]
43
47
 
44
- rows = exec.execute_raw_sql(type_sql).fetchall()
45
- if generate_only and assignment.is_complete:
48
+ rows = exec.execute_raw_sql(type_sql).fetchall()
49
+ seen[datasource.name] = rows[0][0] if rows else 0
50
+ else:
46
51
  results.append(
47
52
  ValidationTest(
48
- query=type_sql,
53
+ raw_query=type_query,
49
54
  check_type=ExpectationType.ROWCOUNT,
50
55
  expected=f"equal_max_{concept.safe_address}",
51
56
  result=None,
52
57
  ran=False,
53
58
  )
54
59
  )
55
- continue
56
- seen[datasource.name] = rows[0][0] if rows else None
57
- if generate_only:
60
+
61
+ if not exec:
58
62
  return results
59
- max_seen = max([v for v in seen.values() if v is not None], default=0)
63
+ max_seen: int = max([v for v in seen.values() if v is not None], default=0)
60
64
  for datasource in build_env.datasources.values():
61
65
  if concept.address in [c.address for c in datasource.concepts]:
62
66
  assignment = [
63
67
  x for x in datasource.columns if x.concept.address == concept.address
64
68
  ][0]
65
69
  err = None
66
- if (seen[datasource.name] or 0) < max_seen and assignment.is_complete:
70
+ datasource_count: int = seen.get(datasource.name, 0)
71
+ if datasource_count < max_seen and assignment.is_complete:
67
72
  err = DatasourceModelValidationError(
68
73
  f"Key concept {concept.address} is missing values in datasource {datasource.name} (max cardinality in data {max_seen}, datasource has {seen[datasource.name]} values) but is not marked as partial."
69
74
  )
70
75
  results.append(
71
76
  ValidationTest(
72
- query=None,
73
77
  check_type=ExpectationType.ROWCOUNT,
74
78
  expected=str(max_seen),
75
79
  result=err,
@@ -96,7 +100,6 @@ def validate_datasources(
96
100
  return []
97
101
  return [
98
102
  ValidationTest(
99
- query=None,
100
103
  check_type=ExpectationType.LOGICAL,
101
104
  expected=None,
102
105
  result=ConceptModelValidationError(
@@ -109,14 +112,14 @@ def validate_datasources(
109
112
 
110
113
  def validate_concept(
111
114
  concept: BuildConcept,
115
+ env: Environment,
112
116
  build_env: BuildEnvironment,
113
- exec: Executor,
114
- generate_only: bool = False,
117
+ exec: Executor | None = None,
115
118
  ) -> list[ValidationTest]:
116
119
  base: list[ValidationTest] = []
117
120
  base += validate_datasources(concept, build_env)
118
121
  if concept.purpose == Purpose.PROPERTY:
119
- base += validate_property_concept(concept, generate_only)
122
+ base += validate_property_concept(concept)
120
123
  elif concept.purpose == Purpose.KEY:
121
- base += validate_key_concept(concept, build_env, exec, generate_only)
124
+ base += validate_key_concept(concept, env, build_env, exec)
122
125
  return base
@@ -2,7 +2,7 @@ from datetime import date, datetime
2
2
  from decimal import Decimal
3
3
  from typing import Any
4
4
 
5
- from trilogy import Executor
5
+ from trilogy import Environment, Executor
6
6
  from trilogy.authoring import (
7
7
  ArrayType,
8
8
  DataType,
@@ -61,12 +61,12 @@ def type_check(
61
61
 
62
62
  def validate_datasource(
63
63
  datasource: BuildDatasource,
64
+ env: Environment,
64
65
  build_env: BuildEnvironment,
65
- exec: Executor,
66
- generate_only: bool = False,
66
+ exec: Executor | None = None,
67
67
  ) -> list[ValidationTest]:
68
68
  results: list[ValidationTest] = []
69
- # we might have merged concepts, where both wil lmap out to the same
69
+ # we might have merged concepts, where both will map out to the same
70
70
  unique_outputs = unique(
71
71
  [build_env.concepts[col.concept.address] for col in datasource.columns],
72
72
  "address",
@@ -74,18 +74,20 @@ def validate_datasource(
74
74
  type_query = easy_query(
75
75
  concepts=unique_outputs,
76
76
  datasource=datasource,
77
- env=exec.environment,
77
+ env=env,
78
78
  limit=100,
79
79
  )
80
- type_sql = exec.generate_sql(type_query)[-1]
80
+
81
81
  rows = []
82
- if not generate_only:
82
+ if exec:
83
+ type_sql = exec.generate_sql(type_query)[-1]
83
84
  try:
84
85
  rows = exec.execute_raw_sql(type_sql).fetchall()
85
86
  except Exception as e:
86
87
  results.append(
87
88
  ValidationTest(
88
- query=type_sql,
89
+ raw_query=type_query,
90
+ generated_query=type_sql,
89
91
  check_type=ExpectationType.LOGICAL,
90
92
  expected="valid_sql",
91
93
  result=DatasourceModelValidationError(
@@ -96,9 +98,10 @@ def validate_datasource(
96
98
  )
97
99
  return results
98
100
  else:
101
+
99
102
  results.append(
100
103
  ValidationTest(
101
- query=type_sql,
104
+ raw_query=type_query,
102
105
  check_type=ExpectationType.LOGICAL,
103
106
  expected="datatype_match",
104
107
  result=None,
@@ -117,7 +120,6 @@ def validate_datasource(
117
120
  cols_with_error = set()
118
121
  for row in rows:
119
122
  for col in datasource.columns:
120
-
121
123
  actual_address = build_env.concepts[col.concept.address].safe_address
122
124
  if actual_address in cols_with_error:
123
125
  continue
@@ -140,7 +142,6 @@ def validate_datasource(
140
142
  if failures:
141
143
  results.append(
142
144
  ValidationTest(
143
- query=None,
144
145
  check_type=ExpectationType.LOGICAL,
145
146
  expected="datatype_match",
146
147
  ran=True,
@@ -161,10 +162,10 @@ def validate_datasource(
161
162
  operator=ComparisonOperator.GT,
162
163
  ),
163
164
  )
164
- if generate_only:
165
+ if not exec:
165
166
  results.append(
166
167
  ValidationTest(
167
- query=exec.generate_sql(query)[-1],
168
+ raw_query=query,
168
169
  check_type=ExpectationType.ROWCOUNT,
169
170
  expected="0",
170
171
  result=None,
@@ -179,7 +180,8 @@ def validate_datasource(
179
180
  if rows:
180
181
  results.append(
181
182
  ValidationTest(
182
- query=sql,
183
+ raw_query=query,
184
+ generated_query=sql,
183
185
  check_type=ExpectationType.ROWCOUNT,
184
186
  expected="0",
185
187
  result=DatasourceModelValidationError(
@@ -12,12 +12,12 @@ from trilogy.parsing.common import function_to_concept
12
12
 
13
13
  def validate_environment(
14
14
  env: Environment,
15
- exec: Executor,
16
15
  scope: ValidationScope = ValidationScope.ALL,
17
16
  targets: list[str] | None = None,
18
- generate_only: bool = False,
17
+ exec: Executor | None = None,
19
18
  ) -> list[ValidationTest]:
20
19
  # avoid mutating the environment for validation
20
+ generate_only = exec is None
21
21
  env = env.duplicate()
22
22
  grain_check = function_to_concept(
23
23
  parent=Function(
@@ -51,13 +51,13 @@ def validate_environment(
51
51
  for datasource in build_env.datasources.values():
52
52
  if targets and datasource.name not in targets:
53
53
  continue
54
- results += validate_datasource(datasource, build_env, exec, generate_only)
54
+ results += validate_datasource(datasource, env, build_env, exec)
55
55
  if scope == ValidationScope.ALL or scope == ValidationScope.CONCEPTS:
56
56
 
57
57
  for bconcept in build_env.concepts.values():
58
58
  if targets and bconcept.address not in targets:
59
59
  continue
60
- results += validate_concept(bconcept, build_env, exec, generate_only)
60
+ results += validate_concept(bconcept, env, build_env, exec)
61
61
 
62
62
  # raise a nicely formatted union of all exceptions
63
63
  exceptions: list[ModelValidationError] = [e.result for e in results if e.result]
@@ -76,6 +76,7 @@ from trilogy.core.statements.author import (
76
76
  )
77
77
  from trilogy.core.statements.execute import (
78
78
  PROCESSED_STATEMENT_TYPES,
79
+ ProcessedCopyStatement,
79
80
  ProcessedQuery,
80
81
  ProcessedQueryPersist,
81
82
  ProcessedRawSQLStatement,
@@ -345,6 +346,7 @@ class BaseDialect:
345
346
  COMPLEX_DATATYPE_MAP = COMPLEX_DATATYPE_MAP
346
347
  UNNEST_MODE = UnnestMode.CROSS_APPLY
347
348
  GROUP_MODE = GroupMode.AUTO
349
+ EXPLAIN_KEYWORD = "EXPLAIN"
348
350
 
349
351
  def __init__(self, rendering: Rendering | None = None):
350
352
  self.rendering = rendering or CONFIG.rendering
@@ -1135,7 +1137,13 @@ class BaseDialect:
1135
1137
  query: PROCESSED_STATEMENT_TYPES,
1136
1138
  ) -> str:
1137
1139
  if isinstance(query, ProcessedShowStatement):
1138
- return ";\n".join([str(x) for x in query.output_values])
1140
+ return ";\n".join(
1141
+ [
1142
+ f'{self.EXPLAIN_KEYWORD} {self.compile_statement(x)}'
1143
+ for x in query.output_values
1144
+ if isinstance(x, (ProcessedQuery, ProcessedCopyStatement))
1145
+ ]
1146
+ )
1139
1147
  elif isinstance(query, ProcessedRawSQLStatement):
1140
1148
  return query.text
1141
1149