pytrilogy 0.0.3.42__tar.gz → 0.0.3.44__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 (141) hide show
  1. {pytrilogy-0.0.3.42/pytrilogy.egg-info → pytrilogy-0.0.3.44}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pytrilogy.egg-info/SOURCES.txt +1 -0
  4. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_models.py +0 -3
  5. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_parsing.py +1 -101
  6. pytrilogy-0.0.3.44/tests/test_parsing_failures.py +143 -0
  7. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/__init__.py +1 -1
  8. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/author.py +12 -14
  9. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/build.py +52 -47
  10. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/common.py +1 -1
  11. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/window_node.py +19 -5
  12. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/bigquery.py +1 -1
  13. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/common.py +40 -25
  14. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/parse_engine.py +106 -19
  15. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/trilogy.lark +44 -34
  16. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/LICENSE.md +0 -0
  17. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/README.md +0 -0
  18. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pyproject.toml +0 -0
  19. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/setup.cfg +0 -0
  24. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/setup.py +0 -0
  25. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_datatypes.py +0 -0
  26. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_declarations.py +0 -0
  27. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_derived_concepts.py +0 -0
  28. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_discovery_nodes.py +0 -0
  29. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_enums.py +0 -0
  30. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_environment.py +0 -0
  31. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_executor.py +0 -0
  32. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_failure.py +0 -0
  33. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_functions.py +1 -1
  34. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_imports.py +0 -0
  35. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_metadata.py +0 -0
  36. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_multi_join_assignments.py +0 -0
  37. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_parse_engine.py +0 -0
  38. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_partial_handling.py +0 -0
  39. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_query_processing.py +0 -0
  40. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_query_render.py +0 -0
  41. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_select.py +0 -0
  42. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_show.py +0 -0
  43. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_statements.py +0 -0
  44. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_typing.py +0 -0
  45. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_undefined_concept.py +0 -0
  46. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_user_functions.py +0 -0
  47. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/tests/test_where_clause.py +0 -0
  48. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/authoring/__init__.py +0 -0
  49. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/compiler.py +0 -0
  50. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/constants.py +0 -0
  51. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/__init__.py +0 -0
  52. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/constants.py +0 -0
  53. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/enums.py +0 -0
  54. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/env_processor.py +0 -0
  55. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/environment_helpers.py +0 -0
  56. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/ergonomics.py +0 -0
  57. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/exceptions.py +0 -0
  58. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/functions.py +0 -0
  59. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/graph_models.py +0 -0
  60. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/internal.py +0 -0
  61. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/__init__.py +0 -0
  62. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/build_environment.py +0 -0
  63. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/core.py +0 -0
  64. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/datasource.py +0 -0
  65. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/environment.py +0 -0
  66. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/models/execute.py +0 -0
  67. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimization.py +0 -0
  68. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimizations/__init__.py +0 -0
  69. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimizations/base_optimization.py +0 -0
  70. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimizations/inline_constant.py +0 -0
  71. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimizations/inline_datasource.py +0 -0
  72. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  73. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/__init__.py +0 -0
  74. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  75. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/graph_utils.py +0 -0
  76. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/__init__.py +0 -0
  77. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  78. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  79. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/group_node.py +0 -0
  80. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  81. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  82. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  83. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  84. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  85. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  86. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  87. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/select_node.py +0 -0
  88. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  89. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/union_node.py +0 -0
  90. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  91. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/__init__.py +0 -0
  92. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/base_node.py +0 -0
  93. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/filter_node.py +0 -0
  94. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/group_node.py +0 -0
  95. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/merge_node.py +0 -0
  96. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  97. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/union_node.py +0 -0
  98. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  99. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/nodes/window_node.py +0 -0
  100. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/processing/utility.py +0 -0
  101. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/query_processor.py +0 -0
  102. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/statements/__init__.py +0 -0
  103. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/statements/author.py +0 -0
  104. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/statements/build.py +0 -0
  105. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/statements/common.py +0 -0
  106. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/core/statements/execute.py +0 -0
  107. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/__init__.py +0 -0
  108. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/base.py +0 -0
  109. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/common.py +0 -0
  110. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/config.py +0 -0
  111. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/dataframe.py +0 -0
  112. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/duckdb.py +0 -0
  113. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/enums.py +0 -0
  114. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/postgres.py +0 -0
  115. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/presto.py +0 -0
  116. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/snowflake.py +0 -0
  117. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/dialect/sql_server.py +0 -0
  118. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/engine.py +0 -0
  119. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/executor.py +0 -0
  120. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/hooks/__init__.py +0 -0
  121. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/hooks/base_hook.py +0 -0
  122. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/hooks/graph_hook.py +0 -0
  123. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/hooks/query_debugger.py +0 -0
  124. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/metadata/__init__.py +0 -0
  125. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parser.py +0 -0
  126. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/__init__.py +0 -0
  127. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/config.py +0 -0
  128. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/exceptions.py +0 -0
  129. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/helpers.py +0 -0
  130. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/parsing/render.py +0 -0
  131. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/py.typed +0 -0
  132. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/render.py +0 -0
  133. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/scripts/__init__.py +0 -0
  134. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/scripts/trilogy.py +0 -0
  135. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/__init__.py +0 -0
  136. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/date.preql +0 -0
  137. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/display.preql +0 -0
  138. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/geography.preql +0 -0
  139. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/money.preql +0 -0
  140. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/std/report.preql +0 -0
  141. {pytrilogy-0.0.3.42 → pytrilogy-0.0.3.44}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.42
3
+ Version: 0.0.3.44
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.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.42
3
+ Version: 0.0.3.44
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -23,6 +23,7 @@ tests/test_models.py
23
23
  tests/test_multi_join_assignments.py
24
24
  tests/test_parse_engine.py
25
25
  tests/test_parsing.py
26
+ tests/test_parsing_failures.py
26
27
  tests/test_partial_handling.py
27
28
  tests/test_query_processing.py
28
29
  tests/test_query_render.py
@@ -213,9 +213,6 @@ def test_comparison():
213
213
  with raises(SyntaxError):
214
214
  Comparison(left=1, right="abc", operator=ComparisonOperator.EQ)
215
215
 
216
- with raises(SyntaxError):
217
- Comparison(left=1, right=1, operator=ComparisonOperator.IN)
218
-
219
216
  # this should not error
220
217
  Comparison(left=1, right=[1, 2, 3], operator=ComparisonOperator.IN)
221
218
 
@@ -1,9 +1,6 @@
1
- from pytest import raises
2
-
3
1
  from trilogy import Dialects
4
- from trilogy.constants import MagicConstants, Parsing
2
+ from trilogy.constants import MagicConstants
5
3
  from trilogy.core.enums import BooleanOperator, ComparisonOperator, Purpose
6
- from trilogy.core.exceptions import InvalidSyntaxException
7
4
  from trilogy.core.functions import argument_to_purpose, function_args_to_output_purpose
8
5
  from trilogy.core.models.author import Comparison
9
6
  from trilogy.core.models.core import (
@@ -20,7 +17,6 @@ from trilogy.core.statements.author import SelectStatement, ShowStatement
20
17
  from trilogy.core.statements.execute import ProcessedQuery
21
18
  from trilogy.dialect.base import BaseDialect
22
19
  from trilogy.parsing.parse_engine import (
23
- ParseError,
24
20
  arg_to_datatype,
25
21
  parse_text,
26
22
  )
@@ -668,99 +664,3 @@ select x % 10 -> x_mod_10;
668
664
 
669
665
  """
670
666
  )
671
-
672
-
673
- def test_import_shows_source():
674
-
675
- env = Environment(
676
- config=EnvironmentOptions(
677
- import_resolver=DictImportResolver(
678
- content={
679
- "test": """
680
- import test_dep as test_dep;
681
- key x int;
682
- datasource test (
683
- x: x)
684
- grain(x)
685
- query '''
686
- select 1 as x
687
- union all
688
- select 11 as x
689
- ''' TYPO
690
- """,
691
- "test_dep": """
692
- key x int;
693
- """,
694
- }
695
- )
696
- )
697
- )
698
- assert isinstance(env.config.import_resolver, DictImportResolver)
699
-
700
- with raises(Exception, match="Unable to import 'test', parsing error") as e:
701
- env.parse(
702
- """
703
- import test;
704
-
705
- select x % 10 -> x_mod_10;
706
-
707
-
708
- """
709
- )
710
- assert "TYPO" in str(e.value)
711
- assert 1 == 0
712
-
713
-
714
- def test_concept_shadow_warning():
715
- x = """
716
- key scalar int;
717
- property scalar.int_array list<int>;
718
-
719
- key split <- unnest(int_array);
720
-
721
- datasource avalues (
722
- int_array: int_array,
723
- scalar: scalar
724
- )
725
- grain (scalar)
726
- query '''(
727
- select [1,2,3,4] as int_array, 2 as scalar
728
- union all
729
- select [5,6,7,8] as int_array, 4 as scalar
730
- )''';
731
-
732
- SELECT
733
- int_array,
734
- 1+2->scalar
735
- ;
736
- """
737
- with raises(ParseError):
738
- env, parsed = parse_text(
739
- x, parse_config=Parsing(strict_name_shadow_enforcement=True)
740
- )
741
- x = """
742
- key scalar int;
743
- property scalar.int_array list<int>;
744
-
745
- key split <- unnest(int_array);
746
-
747
- datasource avalues (
748
- int_array: int_array,
749
- scalar: scalar
750
- )
751
- grain (scalar)
752
- query '''(
753
- select [1,2,3,4] as int_array, 2 as scalar
754
- union all
755
- select [5,6,7,8] as int_array, 4 as scalar
756
- )''';
757
-
758
- SELECT
759
- int_array,
760
- sum(scalar)->scalar
761
- ;
762
- """
763
- with raises(InvalidSyntaxException):
764
- env, parsed = parse_text(
765
- x, parse_config=Parsing(strict_name_shadow_enforcement=True)
766
- )
@@ -0,0 +1,143 @@
1
+ from pytest import raises
2
+
3
+ from trilogy.constants import Parsing
4
+ from trilogy.core.exceptions import InvalidSyntaxException
5
+ from trilogy.core.models.environment import (
6
+ DictImportResolver,
7
+ Environment,
8
+ EnvironmentOptions,
9
+ )
10
+ from trilogy.parsing.parse_engine import (
11
+ ParseError,
12
+ parse_text,
13
+ )
14
+
15
+
16
+ def test_import_shows_source():
17
+
18
+ env = Environment(
19
+ config=EnvironmentOptions(
20
+ import_resolver=DictImportResolver(
21
+ content={
22
+ "test": """
23
+ import test_dep as test_dep;
24
+ key x int;
25
+ datasource test (
26
+ x: x)
27
+ grain(x)
28
+ query '''
29
+ select 1 as x
30
+ union all
31
+ select 11 as x
32
+ ''' TYPO
33
+ """,
34
+ "test_dep": """
35
+ key x int;
36
+ """,
37
+ }
38
+ )
39
+ )
40
+ )
41
+ assert isinstance(env.config.import_resolver, DictImportResolver)
42
+
43
+ with raises(Exception, match="Unable to import 'test', parsing error") as e:
44
+ env.parse(
45
+ """
46
+ import test;
47
+
48
+ select x % 10 -> x_mod_10;
49
+
50
+
51
+ """
52
+ )
53
+ assert "TYPO" in str(e.value)
54
+ assert 1 == 0
55
+
56
+
57
+ def test_concept_shadow_warning():
58
+ x = """
59
+ key scalar int;
60
+ property scalar.int_array list<int>;
61
+
62
+ key split <- unnest(int_array);
63
+
64
+ datasource avalues (
65
+ int_array: int_array,
66
+ scalar: scalar
67
+ )
68
+ grain (scalar)
69
+ query '''(
70
+ select [1,2,3,4] as int_array, 2 as scalar
71
+ union all
72
+ select [5,6,7,8] as int_array, 4 as scalar
73
+ )''';
74
+
75
+ SELECT
76
+ int_array,
77
+ 1+2->scalar
78
+ ;
79
+ """
80
+ with raises(ParseError) as e:
81
+ env, parsed = parse_text(
82
+ x, parse_config=Parsing(strict_name_shadow_enforcement=True)
83
+ )
84
+ assert "abc" in str(e)
85
+ x = """
86
+ key scalar int;
87
+ property scalar.int_array list<int>;
88
+
89
+ key split <- unnest(int_array);
90
+
91
+ datasource avalues (
92
+ int_array: int_array,
93
+ scalar: scalar
94
+ )
95
+ grain (scalar)
96
+ query '''(
97
+ select [1,2,3,4] as int_array, 2 as scalar
98
+ union all
99
+ select [5,6,7,8] as int_array, 4 as scalar
100
+ )''';
101
+
102
+ SELECT
103
+ int_array,
104
+ sum(scalar)->scalar
105
+ ;
106
+ """
107
+ with raises(InvalidSyntaxException):
108
+ env, parsed = parse_text(
109
+ x, parse_config=Parsing(strict_name_shadow_enforcement=True)
110
+ )
111
+
112
+
113
+ def test_parsing_bad_order():
114
+ x = """
115
+ key scalar int;
116
+ property scalar.int_array list<int>;
117
+
118
+ key split <- unnest(int_array);
119
+
120
+ datasource avalues (
121
+ int_array: int_array,
122
+ scalar: scalar
123
+ )
124
+ grain (scalar)
125
+ query '''(
126
+ select [1,2,3,4] as int_array, 2 as scalar
127
+ union all
128
+ select [5,6,7,8] as int_array, 4 as scalar
129
+ )''';
130
+
131
+ SELECT
132
+ int_array,
133
+ sum(scalar)->scalar
134
+ order
135
+ int_array asc
136
+ ;
137
+ """
138
+ with raises(InvalidSyntaxException) as e:
139
+
140
+ env, parsed = parse_text(
141
+ x, parse_config=Parsing(strict_name_shadow_enforcement=True)
142
+ )
143
+ assert "^" in str(e)
@@ -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.42"
7
+ __version__ = "0.0.3.44"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -629,18 +629,8 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
629
629
  )
630
630
  elif self.operator in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
631
631
  right_type = arg_to_datatype(self.right)
632
- if not any(
633
- [
634
- isinstance(self.right, ConceptRef),
635
- right_type in (DataType.LIST,),
636
- isinstance(right_type, (ListType, ListWrapper, TupleWrapper)),
637
- ]
638
- ):
639
- raise SyntaxError(
640
- f"Cannot use {self.operator.value} with non-list, non-tuple, non-concept object {self.right} in {str(self)}"
641
- )
642
632
 
643
- elif isinstance(right_type, ListType) and not is_compatible_datatype(
633
+ if isinstance(right_type, ListType) and not is_compatible_datatype(
644
634
  arg_to_datatype(self.left), right_type.value_data_type
645
635
  ):
646
636
  raise SyntaxError(
@@ -1916,7 +1906,7 @@ class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel)
1916
1906
 
1917
1907
 
1918
1908
  class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
1919
- content: ConceptRef
1909
+ content: Expr
1920
1910
  where: "WhereClause"
1921
1911
 
1922
1912
  @field_validator("content", mode="before")
@@ -1932,13 +1922,21 @@ class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
1932
1922
  self, source: Concept, target: Concept, modifiers: List[Modifier]
1933
1923
  ) -> "FilterItem":
1934
1924
  return FilterItem.model_construct(
1935
- content=self.content.with_merge(source, target, modifiers),
1925
+ content=(
1926
+ self.content.with_merge(source, target, modifiers)
1927
+ if isinstance(self.content, Mergeable)
1928
+ else self.content
1929
+ ),
1936
1930
  where=self.where.with_merge(source, target, modifiers),
1937
1931
  )
1938
1932
 
1939
1933
  def with_namespace(self, namespace: str) -> "FilterItem":
1940
1934
  return FilterItem.model_construct(
1941
- content=self.content.with_namespace(namespace),
1935
+ content=(
1936
+ self.content.with_namespace(namespace)
1937
+ if isinstance(self.content, Namespaced)
1938
+ else self.content
1939
+ ),
1942
1940
  where=self.where.with_namespace(namespace),
1943
1941
  )
1944
1942
 
@@ -1496,22 +1496,43 @@ class Factory:
1496
1496
  ):
1497
1497
  return base
1498
1498
 
1499
+ def instantiate_concept(
1500
+ self,
1501
+ arg: (
1502
+ AggregateWrapper
1503
+ | FunctionCallWrapper
1504
+ | WindowItem
1505
+ | FilterItem
1506
+ | Function
1507
+ | ListWrapper[Any]
1508
+ | MapWrapper[Any, Any]
1509
+ | int
1510
+ | float
1511
+ | str
1512
+ ),
1513
+ ) -> tuple[Concept, BuildConcept]:
1514
+ from trilogy.parsing.common import arbitrary_to_concept
1515
+
1516
+ new = arbitrary_to_concept(
1517
+ arg,
1518
+ environment=self.environment,
1519
+ )
1520
+ built = self.build(new)
1521
+ self.local_concepts[new.address] = built
1522
+ return new, built
1523
+
1499
1524
  @build.register
1500
1525
  def _(self, base: None) -> None:
1501
1526
  return base
1502
1527
 
1503
1528
  @build.register
1504
1529
  def _(self, base: Function) -> BuildFunction | BuildAggregateWrapper:
1505
- from trilogy.parsing.common import arbitrary_to_concept
1506
1530
 
1507
1531
  raw_args: list[Concept | FuncArgs] = []
1508
1532
  for arg in base.arguments:
1509
1533
  # to do proper discovery, we need to inject virtual intermediate ocncepts
1510
1534
  if isinstance(arg, (AggregateWrapper, FilterItem, WindowItem)):
1511
- narg = arbitrary_to_concept(
1512
- arg,
1513
- environment=self.environment,
1514
- )
1535
+ narg, _ = self.instantiate_concept(arg)
1515
1536
  raw_args.append(narg)
1516
1537
  else:
1517
1538
  raw_args.append(arg)
@@ -1532,24 +1553,16 @@ class Factory:
1532
1553
  for x in arguments:
1533
1554
  if isinstance(x, (ConceptRef, Concept)):
1534
1555
  final_args.append(x)
1535
- elif isinstance(x, (AggregateWrapper, FilterItem, WindowItem)):
1536
- newx = arbitrary_to_concept(
1537
- x,
1538
- environment=self.environment,
1539
- )
1540
- final_args.append(newx)
1541
1556
  else:
1542
1557
  # constants, etc, can be ignored for group
1543
1558
  continue
1544
- group_base = arbitrary_to_concept(
1559
+ _, rval = self.instantiate_concept(
1545
1560
  AggregateWrapper(
1546
1561
  function=group_base.lineage.function,
1547
1562
  by=final_args,
1548
- ),
1549
- environment=self.environment,
1563
+ )
1550
1564
  )
1551
1565
 
1552
- rval = self.build(group_base)
1553
1566
  return BuildFunction.model_construct(
1554
1567
  operator=base.operator,
1555
1568
  arguments=[rval, *[self.build(c) for c in raw_args[1:]]],
@@ -1580,20 +1593,13 @@ class Factory:
1580
1593
 
1581
1594
  @build.register
1582
1595
  def _(self, base: CaseWhen) -> BuildCaseWhen:
1583
- from trilogy.parsing.common import arbitrary_to_concept
1584
1596
 
1585
1597
  comparison = base.comparison
1586
1598
  if isinstance(comparison, (AggregateWrapper, FilterItem, WindowItem)):
1587
- comparison = arbitrary_to_concept(
1588
- comparison,
1589
- environment=self.environment,
1590
- )
1599
+ comparison, _ = self.instantiate_concept(comparison)
1591
1600
  expr: Concept | FuncArgs = base.expr
1592
1601
  if isinstance(expr, (AggregateWrapper, FilterItem, WindowItem)):
1593
- expr = arbitrary_to_concept(
1594
- expr,
1595
- environment=self.environment,
1596
- )
1602
+ expr, _ = self.instantiate_concept(expr)
1597
1603
  return BuildCaseWhen.model_construct(
1598
1604
  comparison=self.build(comparison),
1599
1605
  expr=self.build(expr),
@@ -1615,6 +1621,7 @@ class Factory:
1615
1621
  else:
1616
1622
  build_lineage = None
1617
1623
  derivation = Concept.calculate_derivation(build_lineage, base.purpose)
1624
+
1618
1625
  granularity = Concept.calculate_granularity(
1619
1626
  derivation, final_grain, build_lineage
1620
1627
  )
@@ -1675,16 +1682,12 @@ class Factory:
1675
1682
 
1676
1683
  @build.register
1677
1684
  def _(self, base: OrderItem) -> BuildOrderItem:
1678
- from trilogy.parsing.common import arbitrary_to_concept
1679
1685
 
1680
1686
  bexpr: Any
1681
1687
  if isinstance(base.expr, (AggregateWrapper, WindowItem, FilterItem)) or (
1682
1688
  isinstance(base.expr, Function) and base.expr.operator == FunctionType.GROUP
1683
1689
  ):
1684
- bexpr = arbitrary_to_concept(
1685
- base.expr,
1686
- environment=self.environment,
1687
- )
1690
+ bexpr, _ = self.instantiate_concept(base.expr)
1688
1691
  else:
1689
1692
  bexpr = base.expr
1690
1693
  return BuildOrderItem.model_construct(
@@ -1707,15 +1710,10 @@ class Factory:
1707
1710
 
1708
1711
  @build.register
1709
1712
  def _(self, base: WindowItem) -> BuildWindowItem:
1710
- # to do proper discovery, we need to inject virtual intermediate ocncepts
1711
- from trilogy.parsing.common import arbitrary_to_concept
1712
1713
 
1713
1714
  content: Concept | FuncArgs = base.content
1714
1715
  if isinstance(content, (AggregateWrapper, FilterItem, WindowItem)):
1715
- content = arbitrary_to_concept(
1716
- content,
1717
- environment=self.environment,
1718
- )
1716
+ content, _ = self.instantiate_concept(content)
1719
1717
  final_by = []
1720
1718
  for x in base.order_by:
1721
1719
  if (
@@ -1743,30 +1741,26 @@ class Factory:
1743
1741
 
1744
1742
  @build.register
1745
1743
  def _(self, base: SubselectComparison) -> BuildSubselectComparison:
1744
+ right: Any = base.right
1745
+ if isinstance(base.right, (AggregateWrapper, WindowItem, FilterItem, Function)):
1746
+ right_c, _ = self.instantiate_concept(base.right)
1747
+ right = right_c
1746
1748
  return BuildSubselectComparison.model_construct(
1747
- left=(self.build(base.left)),
1748
- right=(self.build(base.right)),
1749
+ left=self.build(base.left),
1750
+ right=self.build(right),
1749
1751
  operator=base.operator,
1750
1752
  )
1751
1753
 
1752
1754
  @build.register
1753
1755
  def _(self, base: Comparison) -> BuildComparison:
1754
- from trilogy.parsing.common import arbitrary_to_concept
1755
1756
 
1756
1757
  left = base.left
1757
1758
  if isinstance(left, (AggregateWrapper, WindowItem, FilterItem)):
1758
- left_c = arbitrary_to_concept(
1759
- left,
1760
- environment=self.environment,
1761
- )
1759
+ left_c, _ = self.instantiate_concept(left)
1762
1760
  left = left_c # type: ignore
1763
1761
  right = base.right
1764
1762
  if isinstance(right, (AggregateWrapper, WindowItem, FilterItem)):
1765
- right_c = arbitrary_to_concept(
1766
- right,
1767
- environment=self.environment,
1768
- )
1769
-
1763
+ right_c, _ = self.instantiate_concept(right)
1770
1764
  right = right_c # type: ignore
1771
1765
  return BuildComparison.model_construct(
1772
1766
  left=(self.build(left)),
@@ -1826,6 +1820,13 @@ class Factory:
1826
1820
 
1827
1821
  @build.register
1828
1822
  def _(self, base: FilterItem) -> BuildFilterItem:
1823
+ if isinstance(
1824
+ base.content, (Function, AggregateWrapper, WindowItem, FilterItem)
1825
+ ):
1826
+ _, built = self.instantiate_concept(base.content)
1827
+ return BuildFilterItem.model_construct(
1828
+ content=built, where=self.build(base.where)
1829
+ )
1829
1830
  return BuildFilterItem.model_construct(
1830
1831
  content=self.build(base.content), where=self.build(base.where)
1831
1832
  )
@@ -1969,6 +1970,10 @@ class Factory:
1969
1970
  new.datasources[k] = self.build(d)
1970
1971
  for k, a in base.alias_origin_lookup.items():
1971
1972
  new.alias_origin_lookup[k] = self.build(a)
1973
+ # add in anything that was built as a side-effect
1974
+ for bk, bv in self.local_concepts.items():
1975
+ if bk not in new.concepts:
1976
+ new.concepts[bk] = bv
1972
1977
  new.gen_concept_list_caches()
1973
1978
  return new
1974
1979
 
@@ -178,7 +178,7 @@ def gen_enrichment_node(
178
178
  for x in extra_required
179
179
  ):
180
180
  log_lambda(
181
- f"{str(type(base_node).__name__)} returning property optimized enrichment node"
181
+ f"{str(type(base_node).__name__)} returning property optimized enrichment node for {extra_required[0].keys}"
182
182
  )
183
183
  return gen_property_enrichment_node(
184
184
  base_node,
@@ -32,7 +32,9 @@ def resolve_window_parent_concepts(
32
32
  if concept.lineage.order_by:
33
33
  for item in concept.lineage.order_by:
34
34
  base += item.concept_arguments
35
-
35
+ if concept.grain:
36
+ for gitem in concept.grain.components:
37
+ base.append(environment.concepts[gitem])
36
38
  return concept.lineage.content, unique(base, "address")
37
39
 
38
40
 
@@ -61,6 +63,9 @@ def gen_window_node(
61
63
  # append in keys to get the right grain
62
64
  if concept.keys:
63
65
  for item in concept.keys:
66
+ logger.info(
67
+ f"{padding(depth)}{LOGGER_PREFIX} appending search for key {item}"
68
+ )
64
69
  targets.append(environment.concepts[item])
65
70
  additional_outputs = []
66
71
  if equivalent_optional:
@@ -72,10 +77,15 @@ def gen_window_node(
72
77
  additional_outputs.append(x)
73
78
 
74
79
  grain_equivalents = [
75
- x for x in local_optional if x.keys and all([key in targets for key in x.keys])
80
+ x
81
+ for x in local_optional
82
+ if x.keys
83
+ and all([key in targets for key in x.keys])
84
+ and x.grain == concept.grain
76
85
  ]
77
86
 
78
87
  for x in grain_equivalents:
88
+ logger.info("Appending grain equivalent %s", x)
79
89
  targets.append(x)
80
90
 
81
91
  # finally, the ones we'll need to enrich
@@ -123,20 +133,24 @@ def gen_window_node(
123
133
  )
124
134
  _window_node.rebuild_cache()
125
135
  _window_node.resolve()
136
+
126
137
  window_node = StrategyNode(
127
138
  input_concepts=[concept] + additional_outputs + parent_concepts + targets,
128
139
  output_concepts=[concept] + additional_outputs + parent_concepts + targets,
129
140
  environment=environment,
130
141
  parents=[_window_node],
131
142
  preexisting_conditions=conditions.conditional if conditions else None,
132
- # hidden_concepts=[
133
- # x.address for x in parent_concepts if x.address not in local_optional
134
- # ],
143
+ grain=BuildGrain.from_concepts(
144
+ concepts=[concept] + additional_outputs + parent_concepts + targets,
145
+ environment=environment,
146
+ ),
135
147
  )
136
148
  if not non_equivalent_optional:
137
149
  logger.info(
138
150
  f"{padding(depth)}{LOGGER_PREFIX} no optional concepts, returning window node"
139
151
  )
152
+ # prune outputs if we don't need join keys
153
+ window_node.set_output_concepts([concept] + additional_outputs + targets)
140
154
  return window_node
141
155
 
142
156
  missing_optional = [
@@ -34,7 +34,7 @@ FUNCTION_MAP = {
34
34
 
35
35
  FUNCTION_GRAIN_MATCH_MAP = {
36
36
  **FUNCTION_MAP,
37
- FunctionType.COUNT: lambda args: f"{args[0]}",
37
+ FunctionType.COUNT: lambda args: "1",
38
38
  FunctionType.SUM: lambda args: f"{args[0]}",
39
39
  FunctionType.AVG: lambda args: f"{args[0]}",
40
40
  }