pytrilogy 0.0.3.100__tar.gz → 0.0.3.101__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 (158) hide show
  1. {pytrilogy-0.0.3.100/pytrilogy.egg-info → pytrilogy-0.0.3.101}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/SOURCES.txt +1 -0
  4. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parsing.py +1 -1
  5. pytrilogy-0.0.3.101/tests/test_validators.py +209 -0
  6. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/exceptions.py +1 -1
  8. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/functions.py +5 -2
  9. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/core.py +3 -0
  10. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/group_node.py +1 -0
  11. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/node_merge_node.py +0 -3
  12. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/datasource.py +30 -6
  13. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/base.py +1 -1
  14. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/render.py +5 -3
  15. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/trilogy.lark +6 -3
  16. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/LICENSE.md +0 -0
  17. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/README.md +0 -0
  18. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pyproject.toml +0 -0
  19. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/setup.cfg +0 -0
  24. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/setup.py +0 -0
  25. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_datatypes.py +0 -0
  26. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_declarations.py +0 -0
  27. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_derived_concepts.py +0 -0
  28. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_discovery_nodes.py +0 -0
  29. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_enums.py +0 -0
  30. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_environment.py +0 -0
  31. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_execute_models.py +0 -0
  32. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_executor.py +0 -0
  33. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_failure.py +0 -0
  34. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_functions.py +0 -0
  35. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_imports.py +0 -0
  36. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_metadata.py +0 -0
  37. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_models.py +0 -0
  38. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_multi_join_assignments.py +0 -0
  39. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parse_engine.py +0 -0
  40. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_parsing_failures.py +0 -0
  41. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_partial_handling.py +0 -0
  42. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_query_processing.py +0 -0
  43. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_query_render.py +0 -0
  44. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_select.py +0 -0
  45. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_show.py +0 -0
  46. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_statements.py +0 -0
  47. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_typing.py +0 -0
  48. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_undefined_concept.py +0 -0
  49. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_user_functions.py +0 -0
  50. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/tests/test_where_clause.py +0 -0
  51. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/authoring/__init__.py +0 -0
  52. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/constants.py +0 -0
  53. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/__init__.py +0 -0
  54. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/constants.py +0 -0
  55. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/enums.py +0 -0
  56. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/env_processor.py +0 -0
  57. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/environment_helpers.py +0 -0
  58. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/ergonomics.py +0 -0
  59. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/graph_models.py +0 -0
  60. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/internal.py +0 -0
  61. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/__init__.py +0 -0
  62. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/author.py +0 -0
  63. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/build.py +0 -0
  64. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/build_environment.py +0 -0
  65. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/datasource.py +0 -0
  66. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/environment.py +0 -0
  67. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/models/execute.py +0 -0
  68. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimization.py +0 -0
  69. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/__init__.py +0 -0
  70. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/base_optimization.py +0 -0
  71. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/inline_datasource.py +0 -0
  72. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  73. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/__init__.py +0 -0
  74. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  75. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_node_factory.py +0 -0
  76. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_utility.py +0 -0
  77. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/discovery_validation.py +0 -0
  78. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/graph_utils.py +0 -0
  79. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/__init__.py +0 -0
  80. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  81. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/common.py +0 -0
  82. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/constant_node.py +0 -0
  83. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  84. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  85. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  86. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
  87. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  88. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  89. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  90. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  91. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/select_node.py +0 -0
  92. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  93. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/union_node.py +0 -0
  94. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  95. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/node_generators/window_node.py +0 -0
  96. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/__init__.py +0 -0
  97. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/base_node.py +0 -0
  98. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/filter_node.py +0 -0
  99. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/group_node.py +0 -0
  100. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/merge_node.py +0 -0
  101. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/recursive_node.py +0 -0
  102. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  103. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/union_node.py +0 -0
  104. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  105. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/nodes/window_node.py +0 -0
  106. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/processing/utility.py +0 -0
  107. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/query_processor.py +0 -0
  108. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/__init__.py +0 -0
  109. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/author.py +0 -0
  110. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/build.py +0 -0
  111. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/common.py +0 -0
  112. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/statements/execute.py +0 -0
  113. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/utility.py +0 -0
  114. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/__init__.py +0 -0
  115. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/common.py +0 -0
  116. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/concept.py +0 -0
  117. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/environment.py +0 -0
  118. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/core/validation/fix.py +0 -0
  119. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/__init__.py +0 -0
  120. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/bigquery.py +0 -0
  121. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/common.py +0 -0
  122. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/config.py +0 -0
  123. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/dataframe.py +0 -0
  124. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/duckdb.py +0 -0
  125. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/enums.py +0 -0
  126. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/metadata.py +0 -0
  127. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/postgres.py +0 -0
  128. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/presto.py +0 -0
  129. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/snowflake.py +0 -0
  130. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/dialect/sql_server.py +0 -0
  131. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/engine.py +0 -0
  132. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/executor.py +0 -0
  133. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/__init__.py +0 -0
  134. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/base_hook.py +0 -0
  135. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/graph_hook.py +0 -0
  136. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/hooks/query_debugger.py +0 -0
  137. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/metadata/__init__.py +0 -0
  138. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parser.py +0 -0
  139. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/__init__.py +0 -0
  140. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/common.py +0 -0
  141. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/config.py +0 -0
  142. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/exceptions.py +0 -0
  143. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/helpers.py +0 -0
  144. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/parsing/parse_engine.py +0 -0
  145. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/py.typed +0 -0
  146. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/render.py +0 -0
  147. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/scripts/__init__.py +0 -0
  148. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/scripts/trilogy.py +0 -0
  149. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/__init__.py +0 -0
  150. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/date.preql +0 -0
  151. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/display.preql +0 -0
  152. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/geography.preql +0 -0
  153. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/metric.preql +0 -0
  154. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/money.preql +0 -0
  155. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/net.preql +0 -0
  156. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/ranking.preql +0 -0
  157. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/std/report.preql +0 -0
  158. {pytrilogy-0.0.3.100 → pytrilogy-0.0.3.101}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.100
3
+ Version: 0.0.3.101
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.100
3
+ Version: 0.0.3.101
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -34,6 +34,7 @@ tests/test_statements.py
34
34
  tests/test_typing.py
35
35
  tests/test_undefined_concept.py
36
36
  tests/test_user_functions.py
37
+ tests/test_validators.py
37
38
  tests/test_where_clause.py
38
39
  trilogy/__init__.py
39
40
  trilogy/constants.py
@@ -552,7 +552,7 @@ const labels <- '';
552
552
 
553
553
  def test_struct_attr_access():
554
554
  text = """
555
- const labels <- struct(a->1, b->2, c->3);
555
+ const labels <- struct(1->a, 2->b, 3->c);
556
556
 
557
557
 
558
558
  select
@@ -0,0 +1,209 @@
1
+ from datetime import date, datetime
2
+ from decimal import Decimal
3
+
4
+ from trilogy.core.models.core import (
5
+ ArrayType,
6
+ DataType,
7
+ MapType,
8
+ NumericType,
9
+ StructType,
10
+ TraitDataType,
11
+ )
12
+ from trilogy.core.validation.datasource import type_check
13
+
14
+
15
+ def test_type_check():
16
+ # Basic string tests
17
+ assert type_check("hello", DataType.STRING)
18
+ assert not type_check(123, DataType.STRING)
19
+ assert type_check("", DataType.STRING) # empty string
20
+
21
+ # Integer tests
22
+ assert type_check(123, DataType.INTEGER)
23
+ assert type_check(0, DataType.INTEGER)
24
+ assert type_check(-123, DataType.INTEGER)
25
+ assert not type_check("123", DataType.INTEGER)
26
+ assert not type_check(123.0, DataType.INTEGER) # float should not match int
27
+
28
+ # BIGINT tests (same as INTEGER in implementation)
29
+ assert type_check(123, DataType.BIGINT)
30
+ assert type_check(2**63 - 1, DataType.BIGINT) # large integer
31
+ assert not type_check("123", DataType.BIGINT)
32
+ assert not type_check(123.0, DataType.BIGINT)
33
+
34
+ # Float tests
35
+ assert type_check(123.45, DataType.FLOAT)
36
+ assert type_check(123, DataType.FLOAT) # int should match float
37
+ assert type_check(0.0, DataType.FLOAT)
38
+ assert type_check(-123.45, DataType.FLOAT)
39
+ assert not type_check("123.45", DataType.FLOAT)
40
+
41
+ # Decimal support in float
42
+ decimal_val = Decimal("123.45")
43
+ assert type_check(decimal_val, DataType.FLOAT)
44
+
45
+ # NumericType tests
46
+ numeric_type = NumericType() # Assuming NumericType can be instantiated
47
+ assert type_check(123.45, numeric_type)
48
+ assert type_check(123, numeric_type)
49
+ assert type_check(decimal_val, numeric_type)
50
+ assert not type_check("123", numeric_type)
51
+
52
+ # NUMBER and NUMERIC tests (both handle int, float, Decimal)
53
+ assert type_check(123, DataType.NUMBER)
54
+ assert type_check(123.45, DataType.NUMBER)
55
+ assert type_check(decimal_val, DataType.NUMBER)
56
+ assert not type_check("123", DataType.NUMBER)
57
+
58
+ assert type_check(123, DataType.NUMERIC)
59
+ assert type_check(123.45, DataType.NUMERIC)
60
+ assert type_check(decimal_val, DataType.NUMERIC)
61
+ assert not type_check("123", DataType.NUMERIC)
62
+
63
+ # Boolean tests
64
+ assert type_check(True, DataType.BOOL)
65
+ assert type_check(False, DataType.BOOL)
66
+ assert not type_check(1, DataType.BOOL) # int should not match bool
67
+ assert not type_check(0, DataType.BOOL)
68
+ assert not type_check("true", DataType.BOOL)
69
+
70
+ # Date tests
71
+ test_date = date(2023, 12, 25)
72
+ assert type_check(test_date, DataType.DATE)
73
+ assert not type_check("2023-12-25", DataType.DATE)
74
+ assert not type_check(
75
+ datetime.now(), DataType.DATE
76
+ ) # datetime should not match date
77
+
78
+ # DateTime and Timestamp tests
79
+ test_datetime = datetime(2023, 12, 25, 15, 30, 45)
80
+ assert type_check(test_datetime, DataType.DATETIME)
81
+ assert type_check(test_datetime, DataType.TIMESTAMP)
82
+ assert not type_check("2023-12-25 15:30:45", DataType.DATETIME)
83
+ assert not type_check(
84
+ test_date, DataType.DATETIME
85
+ ) # date should not match datetime
86
+
87
+ # Unix seconds tests
88
+ assert type_check(1640995200, DataType.UNIX_SECONDS) # int timestamp
89
+ assert type_check(1640995200.123, DataType.UNIX_SECONDS) # float timestamp
90
+ assert not type_check("1640995200", DataType.UNIX_SECONDS)
91
+
92
+ # Date part tests
93
+ assert type_check("year", DataType.DATE_PART)
94
+ assert type_check("month", DataType.DATE_PART)
95
+ assert type_check("day", DataType.DATE_PART)
96
+ assert not type_check(2023, DataType.DATE_PART)
97
+
98
+ # Array tests
99
+ assert type_check([1, 2, 3], DataType.ARRAY)
100
+ assert type_check([], DataType.ARRAY) # empty array
101
+ assert type_check(["a", "b"], DataType.ARRAY)
102
+ assert not type_check("not a list", DataType.ARRAY)
103
+ assert not type_check({"key": "value"}, DataType.ARRAY)
104
+
105
+ # ArrayType tests
106
+ array_type = ArrayType(type=DataType.INTEGER)
107
+ assert type_check([1, 2, 3], array_type)
108
+ assert not type_check("not a list", array_type)
109
+
110
+ # Map tests
111
+ assert type_check({"key": "value"}, DataType.MAP)
112
+ assert type_check({}, DataType.MAP) # empty dict
113
+ assert type_check({"a": 1, "b": 2}, DataType.MAP)
114
+ assert not type_check([1, 2, 3], DataType.MAP)
115
+ assert not type_check("not a dict", DataType.MAP)
116
+
117
+ # MapType tests
118
+ map_type = MapType(key_type=DataType.STRING, value_type=DataType.STRING)
119
+ assert type_check({"key": "value"}, map_type)
120
+ assert not type_check([1, 2, 3], map_type)
121
+
122
+ # Struct tests
123
+ assert type_check({"field1": "value1"}, DataType.STRUCT)
124
+ assert type_check({}, DataType.STRUCT)
125
+ assert not type_check([1, 2, 3], DataType.STRUCT)
126
+
127
+ # StructType tests
128
+ struct_type = StructType(
129
+ fields=[DataType.STRING, DataType.STRING],
130
+ fields_map={"field1": DataType.STRING, "field2": DataType.STRING},
131
+ )
132
+ assert not type_check("not a dict", struct_type)
133
+
134
+ # NULL tests
135
+ assert type_check(None, DataType.NULL)
136
+ assert not type_check("", DataType.NULL)
137
+ assert not type_check(0, DataType.NULL)
138
+ assert not type_check(False, DataType.NULL)
139
+
140
+ # UNKNOWN tests (should accept anything)
141
+ assert type_check("anything", DataType.UNKNOWN)
142
+ assert type_check(123, DataType.UNKNOWN)
143
+ assert type_check(None, DataType.UNKNOWN)
144
+ assert type_check([1, 2, 3], DataType.UNKNOWN)
145
+ assert type_check({"key": "value"}, DataType.UNKNOWN)
146
+
147
+ # Nullable tests
148
+ assert type_check(None, DataType.STRING) # nullable by default
149
+ assert type_check(None, DataType.INTEGER)
150
+ assert type_check(None, DataType.FLOAT)
151
+ assert type_check(None, DataType.BOOL)
152
+
153
+ # Non-nullable tests
154
+ assert not type_check(None, DataType.STRING, nullable=False)
155
+ assert not type_check(None, DataType.INTEGER, nullable=False)
156
+ assert not type_check(None, DataType.FLOAT, nullable=False)
157
+ assert not type_check(None, DataType.BOOL, nullable=False)
158
+
159
+ # TraitDataType tests (recursive handling)
160
+ # Assuming TraitDataType wraps another DataType
161
+ trait_string_type = TraitDataType(type=DataType.STRING, traits=[])
162
+ assert type_check("hello", trait_string_type)
163
+ assert not type_check(123, trait_string_type)
164
+ assert type_check(None, trait_string_type) # nullable by default
165
+ assert not type_check(None, trait_string_type, nullable=False)
166
+
167
+ # Nested TraitDataType
168
+ nested_trait_type = TraitDataType(type=DataType.STRING, traits=[])
169
+ assert type_check("hello", nested_trait_type)
170
+ assert not type_check(123, nested_trait_type)
171
+
172
+ # Edge cases and invalid types
173
+ # Test with unsupported/custom types should return False
174
+ class CustomType:
175
+ pass
176
+
177
+ custom_obj = CustomType()
178
+ # These should all return False as they don't match any known type
179
+ assert not type_check(custom_obj, DataType.STRING)
180
+ assert not type_check(custom_obj, DataType.INTEGER)
181
+ assert not type_check(custom_obj, DataType.ARRAY)
182
+
183
+
184
+ def test_type_check_edge_cases():
185
+ """Additional edge case tests"""
186
+
187
+ # Test very large numbers
188
+ large_int = 2**100
189
+ assert type_check(large_int, DataType.INTEGER)
190
+ assert type_check(large_int, DataType.BIGINT)
191
+ assert type_check(large_int, DataType.FLOAT) # int should match float
192
+
193
+ # Test special float values
194
+ assert type_check(float("inf"), DataType.FLOAT)
195
+ assert type_check(float("-inf"), DataType.FLOAT)
196
+ # Note: NaN might need special handling depending on requirements
197
+ # assert type_check(float('nan'), DataType.FLOAT)
198
+
199
+ # Test empty collections
200
+ assert type_check([], DataType.ARRAY)
201
+ assert type_check({}, DataType.MAP)
202
+ assert type_check({}, DataType.STRUCT)
203
+
204
+ # Test nested collections
205
+ nested_list = [[1, 2], [3, 4]]
206
+ nested_dict = {"outer": {"inner": "value"}}
207
+ assert type_check(nested_list, DataType.ARRAY)
208
+ assert type_check(nested_dict, DataType.MAP)
209
+ assert type_check(nested_dict, DataType.STRUCT)
@@ -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.100"
7
+ __version__ = "0.0.3.101"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -69,7 +69,7 @@ class DatasourceColumnBindingData:
69
69
  actual_modifiers: List[Modifier]
70
70
 
71
71
  def format_failure(self):
72
- return f"Concept {self.address} value '{self.value}' with type {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
72
+ return f"Concept {self.address} value '{self.value}' with type {self.value_type} and {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
73
73
 
74
74
  def is_modifier_issue(self) -> bool:
75
75
  return len(self.value_modifiers) > 0 and any(
@@ -18,6 +18,7 @@ from trilogy.core.models.author import (
18
18
  AggregateWrapper,
19
19
  Concept,
20
20
  ConceptRef,
21
+ Conditional,
21
22
  Function,
22
23
  Parenthetical,
23
24
  UndefinedConcept,
@@ -129,8 +130,8 @@ def validate_case_output(
129
130
  def create_struct_output(
130
131
  args: list[Any],
131
132
  ) -> StructType:
132
- zipped = dict(zip(args[::2], args[1::2]))
133
- types = [arg_to_datatype(x) for x in args[1::2]]
133
+ zipped = dict(zip(args[1::2], args[::2]))
134
+ types = [arg_to_datatype(x) for x in args[::2]]
134
135
  return StructType(fields=types, fields_map=zipped)
135
136
 
136
137
 
@@ -997,6 +998,8 @@ def argument_to_purpose(arg) -> Purpose:
997
998
  return argument_to_purpose(arg.content)
998
999
  elif isinstance(arg, WindowItem):
999
1000
  return Purpose.PROPERTY
1001
+ elif isinstance(arg, Conditional):
1002
+ return Purpose.PROPERTY
1000
1003
  elif isinstance(arg, Concept):
1001
1004
  base = arg.purpose
1002
1005
  if (
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from abc import ABC
4
4
  from collections import UserDict, UserList
5
5
  from datetime import date, datetime
6
+ from decimal import Decimal
6
7
  from enum import Enum
7
8
  from typing import (
8
9
  Any,
@@ -448,6 +449,8 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
448
449
  return DataType.STRING
449
450
  elif isinstance(arg, float):
450
451
  return DataType.FLOAT
452
+ elif isinstance(arg, Decimal):
453
+ return DataType.NUMERIC
451
454
  elif isinstance(arg, DataType):
452
455
  return arg
453
456
  elif isinstance(arg, NumericType):
@@ -28,6 +28,7 @@ def get_aggregate_grain(
28
28
  parent_concepts: List[BuildConcept] = unique(
29
29
  resolve_function_parent_concepts(concept, environment=environment), "address"
30
30
  )
31
+
31
32
  if (
32
33
  concept.grain
33
34
  and len(concept.grain.components) > 0
@@ -164,9 +164,6 @@ def reinject_common_join_keys_v2(
164
164
  reduced = BuildGrain.from_concepts(concrete_concepts).components
165
165
  existing_addresses = set()
166
166
  for concrete in concrete_concepts:
167
- logger.debug(
168
- f"looking at column {concrete.address} with pseudonyms {concrete.pseudonyms}"
169
- )
170
167
  cnode = concept_to_node(concrete.with_default_grain())
171
168
  if cnode in final.nodes:
172
169
  existing_addresses.add(concrete.address)
@@ -36,31 +36,51 @@ def type_check(
36
36
  ) -> bool:
37
37
  if input is None and nullable:
38
38
  return True
39
+
39
40
  target_type = expected_type
40
41
  while isinstance(target_type, TraitDataType):
41
42
  return type_check(input, target_type.data_type, nullable)
43
+
42
44
  if target_type == DataType.STRING:
43
45
  return isinstance(input, str)
44
46
  if target_type == DataType.INTEGER:
45
47
  return isinstance(input, int)
48
+ if target_type == DataType.BIGINT:
49
+ return isinstance(input, int) # or check for larger int if needed
46
50
  if target_type == DataType.FLOAT or isinstance(target_type, NumericType):
47
51
  return (
48
52
  isinstance(input, float)
49
53
  or isinstance(input, int)
50
54
  or isinstance(input, Decimal)
51
55
  )
56
+ if target_type == DataType.NUMBER:
57
+ return isinstance(input, (int, float, Decimal))
58
+ if target_type == DataType.NUMERIC:
59
+ return isinstance(input, (int, float, Decimal))
52
60
  if target_type == DataType.BOOL:
53
61
  return isinstance(input, bool)
54
62
  if target_type == DataType.DATE:
55
- return isinstance(input, date)
63
+ return isinstance(input, date) and not isinstance(input, datetime)
56
64
  if target_type == DataType.DATETIME:
57
65
  return isinstance(input, datetime)
66
+ if target_type == DataType.TIMESTAMP:
67
+ return isinstance(input, datetime) # or timestamp type if you have one
68
+ if target_type == DataType.UNIX_SECONDS:
69
+ return isinstance(input, (int, float)) # Unix timestamps are numeric
70
+ if target_type == DataType.DATE_PART:
71
+ return isinstance(
72
+ input, str
73
+ ) # assuming date parts are strings like "year", "month"
58
74
  if target_type == DataType.ARRAY or isinstance(target_type, ArrayType):
59
75
  return isinstance(input, list)
60
76
  if target_type == DataType.MAP or isinstance(target_type, MapType):
61
77
  return isinstance(input, dict)
62
78
  if target_type == DataType.STRUCT or isinstance(target_type, StructType):
63
79
  return isinstance(input, dict)
80
+ if target_type == DataType.NULL:
81
+ return input is None
82
+ if target_type == DataType.UNKNOWN:
83
+ return True
64
84
  return False
65
85
 
66
86
 
@@ -125,15 +145,19 @@ def validate_datasource(
125
145
  rval = row[actual_address]
126
146
  passed = type_check(rval, col.concept.datatype, col.is_nullable)
127
147
  if not passed:
148
+ value_type = (
149
+ arg_to_datatype(rval) if rval is not None else col.concept.datatype
150
+ )
151
+ traits = None
152
+ if isinstance(col.concept.datatype, TraitDataType):
153
+ traits = col.concept.datatype.traits
154
+ if traits and not isinstance(value_type, TraitDataType):
155
+ value_type = TraitDataType(type=value_type, traits=traits)
128
156
  failures.append(
129
157
  DatasourceColumnBindingData(
130
158
  address=col.concept.address,
131
159
  value=rval,
132
- value_type=(
133
- arg_to_datatype(rval)
134
- if rval is not None
135
- else col.concept.datatype
136
- ),
160
+ value_type=value_type,
137
161
  value_modifiers=[Modifier.NULLABLE] if rval is None else [],
138
162
  actual_type=col.concept.datatype,
139
163
  actual_modifiers=col.concept.modifiers,
@@ -163,7 +163,7 @@ def render_case(args):
163
163
 
164
164
 
165
165
  def struct_arg(args):
166
- return [f"{x[0]}: {x[1]}" for x in zip(args[::2], args[1::2])]
166
+ return [f"{x[1]}: {x[0]}" for x in zip(args[::2], args[1::2])]
167
167
 
168
168
 
169
169
  FUNCTION_MAP = {
@@ -349,7 +349,8 @@ class Renderer:
349
349
  else:
350
350
  output = f"{concept.purpose.value} {namespace}{concept.name} <- {self.to_string(concept.lineage)};"
351
351
  if base_description:
352
- output += f" #{base_description}"
352
+ lines = "\n#".join(base_description.split("\n"))
353
+ output += f" #{lines}"
353
354
  return output
354
355
 
355
356
  @to_string.register
@@ -439,7 +440,7 @@ class Renderer:
439
440
 
440
441
  @to_string.register
441
442
  def _(self, arg: "Conditional"):
442
- return f"({self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)})"
443
+ return f"{self.to_string(arg.left)} {arg.operator.value} {self.to_string(arg.right)}"
443
444
 
444
445
  @to_string.register
445
446
  def _(self, arg: "SubselectComparison"):
@@ -451,7 +452,8 @@ class Renderer:
451
452
 
452
453
  @to_string.register
453
454
  def _(self, arg: "Comment"):
454
- return f"{arg.text}"
455
+ lines = "\n#".join(arg.text.split("\n"))
456
+ return f"{lines}"
455
457
 
456
458
  @to_string.register
457
459
  def _(self, arg: "WindowItem"):
@@ -134,10 +134,12 @@
134
134
  metadata: "metadata" "(" IDENTIFIER "=" string_lit ")"
135
135
 
136
136
  limit: "LIMIT"i /[0-9]+/
137
+
138
+ _order_atom: expr ordering
137
139
 
138
- order_list: expr ordering ("," expr ordering)* ","?
140
+ order_list: _order_atom ("," _order_atom)* ","?
139
141
 
140
- over_list: concept_lit ("," concept_lit )* ","?
142
+ over_list: (concept_lit ",")* concept_lit ","?
141
143
 
142
144
  ORDERING_DIRECTION: /ASC|DESC/i
143
145
 
@@ -433,7 +435,8 @@
433
435
  map_lit: "{" (literal ":" literal ",")* literal ":" literal ","? "}"
434
436
 
435
437
  _STRUCT.1: "struct("i
436
- struct_lit: _STRUCT (IDENTIFIER "->" expr ",")* IDENTIFIER "->" expr ","? ")"
438
+ _BINDING.1: "->"
439
+ struct_lit: _STRUCT expr _BINDING IDENTIFIER ( "," expr _BINDING IDENTIFIER )* ","? ")"
437
440
 
438
441
  !bool_lit: "True"i | "False"i
439
442
 
File without changes
File without changes
File without changes
File without changes