pytrilogy 0.0.3.24__tar.gz → 0.0.3.26__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pytrilogy might be problematic. Click here for more details.

Files changed (133) hide show
  1. {pytrilogy-0.0.3.24/pytrilogy.egg-info → pytrilogy-0.0.3.26}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/setup.py +1 -1
  4. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_functions.py +14 -1
  5. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_parsing.py +4 -1
  6. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/enums.py +1 -0
  8. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/environment_helpers.py +9 -10
  9. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/functions.py +37 -11
  10. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/author.py +13 -3
  11. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/core.py +13 -1
  12. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/base.py +1 -1
  13. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/parse_engine.py +31 -14
  14. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/trilogy.lark +1 -1
  15. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/LICENSE.md +0 -0
  16. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/README.md +0 -0
  17. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pyproject.toml +0 -0
  18. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pytrilogy.egg-info/SOURCES.txt +0 -0
  19. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/setup.cfg +0 -0
  24. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_datatypes.py +0 -0
  25. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_declarations.py +0 -0
  26. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_derived_concepts.py +0 -0
  27. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_discovery_nodes.py +0 -0
  28. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_enums.py +0 -0
  29. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_environment.py +0 -0
  30. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_executor.py +0 -0
  31. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_imports.py +0 -0
  32. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_metadata.py +0 -0
  33. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_models.py +0 -0
  34. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_multi_join_assignments.py +0 -0
  35. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_parse_engine.py +0 -0
  36. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_partial_handling.py +0 -0
  37. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_query_processing.py +0 -0
  38. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_query_render.py +0 -0
  39. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_select.py +0 -0
  40. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_show.py +0 -0
  41. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_statements.py +0 -0
  42. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_typing.py +0 -0
  43. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_undefined_concept.py +0 -0
  44. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_user_functions.py +0 -0
  45. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/tests/test_where_clause.py +0 -0
  46. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/authoring/__init__.py +0 -0
  47. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/compiler.py +0 -0
  48. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/constants.py +0 -0
  49. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/__init__.py +0 -0
  50. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/constants.py +0 -0
  51. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/env_processor.py +0 -0
  52. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/ergonomics.py +0 -0
  53. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/exceptions.py +0 -0
  54. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/graph_models.py +0 -0
  55. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/internal.py +0 -0
  56. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/__init__.py +0 -0
  57. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/build.py +0 -0
  58. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/build_environment.py +0 -0
  59. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/datasource.py +0 -0
  60. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/environment.py +0 -0
  61. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/models/execute.py +0 -0
  62. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimization.py +0 -0
  63. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimizations/__init__.py +0 -0
  64. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimizations/base_optimization.py +0 -0
  65. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimizations/inline_constant.py +0 -0
  66. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimizations/inline_datasource.py +0 -0
  67. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  68. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/__init__.py +0 -0
  69. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  70. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/graph_utils.py +0 -0
  71. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/__init__.py +0 -0
  72. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  73. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/common.py +0 -0
  74. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  75. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/group_node.py +0 -0
  76. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  77. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  78. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  79. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  80. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  81. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  82. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  83. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/select_node.py +0 -0
  84. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  85. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/union_node.py +0 -0
  86. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  87. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/node_generators/window_node.py +0 -0
  88. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/__init__.py +0 -0
  89. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/base_node.py +0 -0
  90. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/filter_node.py +0 -0
  91. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/group_node.py +0 -0
  92. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/merge_node.py +0 -0
  93. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  94. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/union_node.py +0 -0
  95. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  96. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/nodes/window_node.py +0 -0
  97. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/processing/utility.py +0 -0
  98. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/query_processor.py +0 -0
  99. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/statements/__init__.py +0 -0
  100. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/statements/author.py +0 -0
  101. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/statements/build.py +0 -0
  102. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/statements/common.py +0 -0
  103. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/core/statements/execute.py +0 -0
  104. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/__init__.py +0 -0
  105. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/bigquery.py +0 -0
  106. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/common.py +0 -0
  107. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/config.py +0 -0
  108. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/dataframe.py +0 -0
  109. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/duckdb.py +0 -0
  110. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/enums.py +0 -0
  111. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/postgres.py +0 -0
  112. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/presto.py +0 -0
  113. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/snowflake.py +0 -0
  114. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/dialect/sql_server.py +0 -0
  115. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/engine.py +0 -0
  116. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/executor.py +0 -0
  117. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/hooks/__init__.py +0 -0
  118. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/hooks/base_hook.py +0 -0
  119. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/hooks/graph_hook.py +0 -0
  120. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/hooks/query_debugger.py +0 -0
  121. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/metadata/__init__.py +0 -0
  122. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parser.py +0 -0
  123. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/__init__.py +0 -0
  124. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/common.py +0 -0
  125. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/config.py +0 -0
  126. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/exceptions.py +0 -0
  127. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/helpers.py +0 -0
  128. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/parsing/render.py +0 -0
  129. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/py.typed +0 -0
  130. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/render.py +0 -0
  131. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/scripts/__init__.py +0 -0
  132. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/scripts/trilogy.py +0 -0
  133. {pytrilogy-0.0.3.24 → pytrilogy-0.0.3.26}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.24
3
+ Version: 0.0.3.26
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.24
3
+ Version: 0.0.3.26
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -40,7 +40,7 @@ setuptools.setup(
40
40
  ]
41
41
  ),
42
42
  package_data={
43
- "": ["*.tf", "*.jinja", "py.typed", "*.lark"],
43
+ "": ["*.tf", "*.jinja", "py.typed", "*.lark", "*.preql"],
44
44
  },
45
45
  install_requires=install_requires,
46
46
  extras_require={
@@ -122,8 +122,21 @@ def test_date_functions(test_environment):
122
122
  quarter(order_timestamp) -> order_quarter,
123
123
  year(order_timestamp) -> order_year,
124
124
  date_trunc(order_timestamp, month) -> order_month_trunc,
125
- date_part(order_timestamp, month) -> order_month_part,
126
125
  date_add(order_timestamp, month, 1) -> one_month_post_order,
126
+ date_trunc(order_timestamp, day) -> order_day_trunc,
127
+ date_trunc(order_timestamp, year) -> order_year_trunc,
128
+ date_trunc(order_timestamp, hour) -> order_hour_trunc,
129
+ date_trunc(order_timestamp, minute) -> order_minute_trunc,
130
+ date_trunc(order_timestamp, second) -> order_second_trunc,
131
+ date_part(order_timestamp, month) -> order_month_part,
132
+ date_part(order_timestamp, day) -> order_day_part,
133
+ date_part(order_timestamp, year) -> order_year_part,
134
+ date_part(order_timestamp, hour) -> order_hour_part,
135
+ date_part(order_timestamp, minute) -> order_minute_part,
136
+ date_part(order_timestamp, second) -> order_second_part,
137
+ date_part(order_timestamp, quarter) -> order_quarter_part,
138
+ date_part(order_timestamp, week) -> order_week_part,
139
+ date_part(order_timestamp, day_of_week) -> order_day_of_week_part,
127
140
  ;
128
141
 
129
142
 
@@ -657,7 +657,10 @@ key x int;
657
657
  assert isinstance(env.config.import_resolver, DictImportResolver)
658
658
  env.parse(
659
659
  """
660
- import test;
660
+ import std.geography;
661
+ import test;
662
+
663
+ key fun_lat float::latitude;
661
664
 
662
665
  select x % 10 -> x_mod_10;
663
666
 
@@ -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.24"
7
+ __version__ = "0.0.3.26"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -298,6 +298,7 @@ class DatePart(Enum):
298
298
  HOUR = "hour"
299
299
  MINUTE = "minute"
300
300
  SECOND = "second"
301
+ DAY_OF_WEEK = "day_of_week"
301
302
 
302
303
  @classmethod
303
304
  def _missing_(cls, value):
@@ -1,6 +1,6 @@
1
1
  from trilogy.constants import DEFAULT_NAMESPACE
2
2
  from trilogy.core.enums import ConceptSource, FunctionType, Purpose
3
- from trilogy.core.functions import AttrAccess
3
+ from trilogy.core.functions import AttrAccess, FunctionFactory
4
4
  from trilogy.core.models.author import Concept, Function, Metadata
5
5
  from trilogy.core.models.core import DataType, StructType, arg_to_datatype
6
6
  from trilogy.core.models.environment import Environment
@@ -19,6 +19,7 @@ FUNCTION_DESCRIPTION_MAPS = {
19
19
 
20
20
 
21
21
  def generate_date_concepts(concept: Concept, environment: Environment):
22
+ factory = FunctionFactory(environment=environment)
22
23
  if concept.metadata and concept.metadata.description:
23
24
  base_description = concept.metadata.description
24
25
  else:
@@ -40,17 +41,15 @@ def generate_date_concepts(concept: Concept, environment: Environment):
40
41
  if concept.purpose == Purpose.CONSTANT
41
42
  else Purpose.PROPERTY
42
43
  )
43
- const_function: Function = Function(
44
+ function = factory.create_function(
44
45
  operator=ftype,
45
- output_datatype=DataType.INTEGER,
46
- output_purpose=default_type,
47
- arguments=[concept],
46
+ args=[concept],
48
47
  )
49
48
  new_concept = Concept(
50
49
  name=f"{concept.name}.{fname}",
51
50
  datatype=DataType.INTEGER,
52
51
  purpose=default_type,
53
- lineage=const_function,
52
+ lineage=function,
54
53
  grain=concept.grain,
55
54
  namespace=concept.namespace,
56
55
  keys=set(
@@ -68,6 +67,7 @@ def generate_date_concepts(concept: Concept, environment: Environment):
68
67
 
69
68
 
70
69
  def generate_datetime_concepts(concept: Concept, environment: Environment):
70
+ factory = FunctionFactory(environment=environment)
71
71
  if concept.metadata and concept.metadata.description:
72
72
  base_description = concept.metadata.description
73
73
  else:
@@ -88,11 +88,10 @@ def generate_datetime_concepts(concept: Concept, environment: Environment):
88
88
  if concept.purpose == Purpose.CONSTANT
89
89
  else Purpose.PROPERTY
90
90
  )
91
- const_function: Function = Function(
91
+
92
+ const_function = factory.create_function(
92
93
  operator=ftype,
93
- output_datatype=DataType.INTEGER,
94
- output_purpose=default_type,
95
- arguments=[concept],
94
+ args=[concept],
96
95
  )
97
96
  new_concept = Concept(
98
97
  name=f"{concept.name}.{fname}",
@@ -43,7 +43,9 @@ class FunctionConfig:
43
43
  arg_count: int = 1
44
44
  valid_inputs: set[DataType] | list[set[DataType]] | None = None
45
45
  output_purpose: Purpose | None = None
46
- output_type: DataType | ListType | MapType | StructType | NumericType | None = None
46
+ output_type: (
47
+ DataType | ListType | MapType | StructType | NumericType | TraitDataType | None
48
+ ) = None
47
49
  output_type_function: Optional[Callable] = None
48
50
 
49
51
 
@@ -124,6 +126,30 @@ def create_struct_output(
124
126
  return StructType(fields=types, fields_map=zipped)
125
127
 
126
128
 
129
+ def get_date_part_output(args: list[Any]):
130
+ target = args[1]
131
+ if target == DatePart.YEAR:
132
+ return TraitDataType(type=DataType.INTEGER, traits=["year"])
133
+ elif target == DatePart.MONTH:
134
+ return TraitDataType(type=DataType.INTEGER, traits=["month"])
135
+ elif target == DatePart.DAY:
136
+ return TraitDataType(type=DataType.INTEGER, traits=["day"])
137
+ elif target == DatePart.HOUR:
138
+ return TraitDataType(type=DataType.INTEGER, traits=["hour"])
139
+ elif target == DatePart.MINUTE:
140
+ return TraitDataType(type=DataType.INTEGER, traits=["minute"])
141
+ elif target == DatePart.SECOND:
142
+ return TraitDataType(type=DataType.INTEGER, traits=["second"])
143
+ elif target == DatePart.WEEK:
144
+ return TraitDataType(type=DataType.INTEGER, traits=["week"])
145
+ elif target == DatePart.QUARTER:
146
+ return TraitDataType(type=DataType.INTEGER, traits=["quarter"])
147
+ elif target == DatePart.DAY_OF_WEEK:
148
+ return TraitDataType(type=DataType.INTEGER, traits=["day_of_week"])
149
+ else:
150
+ raise InvalidSyntaxException(f"Date part not supported for {target}")
151
+
152
+
127
153
  def get_date_trunc_output(
128
154
  args: list[Any],
129
155
  ):
@@ -360,7 +386,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
360
386
  {DataType.DATE_PART},
361
387
  ],
362
388
  output_purpose=Purpose.PROPERTY,
363
- output_type=DataType.INTEGER,
389
+ output_type_function=get_date_part_output,
364
390
  arg_count=2,
365
391
  ),
366
392
  FunctionType.DATE_ADD: FunctionConfig(
@@ -443,7 +469,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
443
469
  DataType.STRING,
444
470
  },
445
471
  output_purpose=Purpose.PROPERTY,
446
- output_type=DataType.INTEGER,
472
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["second"]),
447
473
  arg_count=1,
448
474
  ),
449
475
  FunctionType.MINUTE: FunctionConfig(
@@ -454,7 +480,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
454
480
  DataType.STRING,
455
481
  },
456
482
  output_purpose=Purpose.PROPERTY,
457
- output_type=DataType.INTEGER,
483
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["minute"]),
458
484
  arg_count=1,
459
485
  ),
460
486
  FunctionType.HOUR: FunctionConfig(
@@ -465,7 +491,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
465
491
  DataType.STRING,
466
492
  },
467
493
  output_purpose=Purpose.PROPERTY,
468
- output_type=DataType.INTEGER,
494
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["hour"]),
469
495
  arg_count=1,
470
496
  ),
471
497
  FunctionType.DAY: FunctionConfig(
@@ -476,7 +502,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
476
502
  DataType.STRING,
477
503
  },
478
504
  output_purpose=Purpose.PROPERTY,
479
- output_type=DataType.INTEGER,
505
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["day"]),
480
506
  arg_count=1,
481
507
  ),
482
508
  FunctionType.WEEK: FunctionConfig(
@@ -487,7 +513,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
487
513
  DataType.STRING,
488
514
  },
489
515
  output_purpose=Purpose.PROPERTY,
490
- output_type=DataType.INTEGER,
516
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["week"]),
491
517
  arg_count=1,
492
518
  ),
493
519
  FunctionType.MONTH: FunctionConfig(
@@ -498,7 +524,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
498
524
  DataType.STRING,
499
525
  },
500
526
  output_purpose=Purpose.PROPERTY,
501
- output_type=DataType.INTEGER,
527
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["month"]),
502
528
  arg_count=1,
503
529
  ),
504
530
  FunctionType.QUARTER: FunctionConfig(
@@ -509,7 +535,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
509
535
  DataType.STRING,
510
536
  },
511
537
  output_purpose=Purpose.PROPERTY,
512
- output_type=DataType.INTEGER,
538
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["quarter"]),
513
539
  arg_count=1,
514
540
  ),
515
541
  FunctionType.YEAR: FunctionConfig(
@@ -520,7 +546,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
520
546
  DataType.STRING,
521
547
  },
522
548
  output_purpose=Purpose.PROPERTY,
523
- output_type=DataType.INTEGER,
549
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["year"]),
524
550
  arg_count=1,
525
551
  ),
526
552
  FunctionType.DAY_OF_WEEK: FunctionConfig(
@@ -531,7 +557,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
531
557
  DataType.STRING,
532
558
  },
533
559
  output_purpose=Purpose.PROPERTY,
534
- output_type=DataType.INTEGER,
560
+ output_type=TraitDataType(type=DataType.INTEGER, traits=["day_of_week"]),
535
561
  arg_count=1,
536
562
  ),
537
563
  FunctionType.ADD: FunctionConfig(
@@ -255,7 +255,7 @@ class Parenthetical(
255
255
  return arg_to_datatype(self.content)
256
256
 
257
257
 
258
- class Conditional(Mergeable, ConceptArgs, Namespaced, BaseModel):
258
+ class Conditional(Mergeable, ConceptArgs, Namespaced, DataTyped, BaseModel):
259
259
  left: Expr
260
260
  right: Expr
261
261
  operator: BooleanOperator
@@ -357,6 +357,11 @@ class Conditional(Mergeable, ConceptArgs, Namespaced, BaseModel):
357
357
  output += self.right.existence_arguments
358
358
  return output
359
359
 
360
+ @property
361
+ def output_datatype(self):
362
+ # a conditional is always a boolean
363
+ return DataType.BOOL
364
+
360
365
  def decompose(self):
361
366
  chunks = []
362
367
  if self.operator == BooleanOperator.AND:
@@ -545,7 +550,7 @@ class Grain(Namespaced, BaseModel):
545
550
  return self.__add__(other)
546
551
 
547
552
 
548
- class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
553
+ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
549
554
  left: Union[
550
555
  int,
551
556
  str,
@@ -745,6 +750,11 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
745
750
  output += self.right.existence_arguments
746
751
  return output
747
752
 
753
+ @property
754
+ def output_datatype(self):
755
+ # a conditional is always a boolean
756
+ return DataType.BOOL
757
+
748
758
 
749
759
  class SubselectComparison(Comparison):
750
760
  def __eq__(self, other):
@@ -1401,7 +1411,7 @@ def get_basic_type(
1401
1411
  if isinstance(type, NumericType):
1402
1412
  return DataType.NUMERIC
1403
1413
  if isinstance(type, TraitDataType):
1404
- return type.type
1414
+ return get_basic_type(type.type)
1405
1415
  return type
1406
1416
 
1407
1417
 
@@ -105,7 +105,7 @@ class DataType(Enum):
105
105
 
106
106
 
107
107
  class TraitDataType(BaseModel):
108
- type: DataType
108
+ type: DataType | NumericType | StructType | ListType | MapType
109
109
  traits: list[str]
110
110
 
111
111
  def __hash__(self):
@@ -134,6 +134,9 @@ class NumericType(BaseModel):
134
134
  precision: int = 20
135
135
  scale: int = 5
136
136
 
137
+ def __str__(self) -> str:
138
+ return f"Numeric({self.precision},{self.scale})"
139
+
137
140
  @property
138
141
  def data_type(self):
139
142
  return DataType.NUMERIC
@@ -356,12 +359,21 @@ def merge_datatypes(
356
359
 
357
360
  def is_compatible_datatype(left, right):
358
361
  # for unknown types, we can't make any assumptions
362
+ if all(
363
+ isinstance(x, NumericType)
364
+ or x in (DataType.INTEGER, DataType.FLOAT, DataType.NUMERIC)
365
+ for x in (left, right)
366
+ ):
367
+ return True
368
+ elif isinstance(left, NumericType) or isinstance(right, NumericType):
369
+ return False
359
370
  if right == DataType.UNKNOWN or left == DataType.UNKNOWN:
360
371
  return True
361
372
  if left == right:
362
373
  return True
363
374
  if {left, right} == {DataType.NUMERIC, DataType.FLOAT}:
364
375
  return True
376
+
365
377
  if {left, right} == {DataType.NUMERIC, DataType.INTEGER}:
366
378
  return True
367
379
  if {left, right} == {DataType.FLOAT, DataType.INTEGER}:
@@ -680,7 +680,7 @@ class BaseDialect:
680
680
  elif isinstance(e, datetime):
681
681
  return self.FUNCTION_MAP[FunctionType.DATETIME_LITERAL](e)
682
682
  elif isinstance(e, TraitDataType):
683
- return self.DATATYPE_MAP.get(e.type, e.type.value)
683
+ return self.render_expr(e.type, cte=cte, cte_map=cte_map)
684
684
  else:
685
685
  raise ValueError(f"Unable to render type {type(e)} {e}")
686
686
 
@@ -91,6 +91,7 @@ from trilogy.core.models.core import (
91
91
  TupleWrapper,
92
92
  arg_to_datatype,
93
93
  dict_to_map_wrapper,
94
+ is_compatible_datatype,
94
95
  list_to_wrapper,
95
96
  tuple_to_wrapper,
96
97
  )
@@ -146,6 +147,8 @@ SELF_LABEL = "root"
146
147
 
147
148
  MAX_PARSE_DEPTH = 10
148
149
 
150
+ STDLIB_ROOT = Path(__file__).parent.parent
151
+
149
152
 
150
153
  @dataclass
151
154
  class WholeGrainWrapper:
@@ -449,20 +452,27 @@ class ParseToObjects(Transformer):
449
452
  ) -> DataType | TraitDataType | ListType | StructType | MapType | NumericType:
450
453
  resolved = args[0]
451
454
  traits = args[2:]
455
+ base: DataType | TraitDataType | ListType | StructType | MapType | NumericType
452
456
  if isinstance(resolved, StructType):
453
- return resolved
457
+ base = resolved
454
458
  elif isinstance(resolved, ListType):
455
- return resolved
459
+ base = resolved
456
460
  elif isinstance(resolved, NumericType):
457
- return resolved
461
+ base = resolved
458
462
  elif isinstance(resolved, MapType):
459
- return resolved
460
- base = DataType(args[0].lower())
463
+ base = resolved
464
+ else:
465
+ base = DataType(args[0].lower())
461
466
  if traits:
462
467
  for trait in traits:
463
468
  if trait not in self.environment.data_types:
464
469
  raise ParseError(
465
- f"Invalid type trait {trait} for {base}, line {meta.line}"
470
+ f"Invalid trait (type) {trait} for {base}, line {meta.line}."
471
+ )
472
+ matched = self.environment.data_types[trait]
473
+ if not is_compatible_datatype(matched.type, base):
474
+ raise ParseError(
475
+ f"Invalid trait (type) {trait} for {base}, line {meta.line}. Trait expects type {matched.type}, has {base}"
466
476
  )
467
477
  return TraitDataType(type=base, traits=traits)
468
478
 
@@ -936,9 +946,12 @@ class ParseToObjects(Transformer):
936
946
  select=args[-1],
937
947
  )
938
948
 
939
- def resolve_import_address(self, address) -> str:
940
- if isinstance(
941
- self.environment.config.import_resolver, FileSystemImportResolver
949
+ def resolve_import_address(self, address, is_stdlib: bool = False) -> str:
950
+ if (
951
+ isinstance(
952
+ self.environment.config.import_resolver, FileSystemImportResolver
953
+ )
954
+ or is_stdlib
942
955
  ):
943
956
  with open(address, "r", encoding="utf-8") as f:
944
957
  text = f.read()
@@ -946,7 +959,7 @@ class ParseToObjects(Transformer):
946
959
  lookup = address
947
960
  if lookup not in self.environment.config.import_resolver.content:
948
961
  raise ImportError(
949
- f"Unable to import file {lookup}, not found in import resolver"
962
+ f"Unable to import file {lookup}, not found in imsport resolver"
950
963
  )
951
964
  text = self.environment.config.import_resolver.content[lookup]
952
965
  else:
@@ -964,13 +977,17 @@ class ParseToObjects(Transformer):
964
977
  cache_key = args[0]
965
978
  input_path = args[0]
966
979
  path = input_path.split(".")
967
-
968
- if isinstance(
980
+ is_stdlib = False
981
+ if path[0] == "std":
982
+ is_stdlib = True
983
+ target = join(STDLIB_ROOT, *path) + ".preql"
984
+ token_lookup: Path | str = Path(target)
985
+ elif isinstance(
969
986
  self.environment.config.import_resolver, FileSystemImportResolver
970
987
  ):
971
988
  target = join(self.environment.working_path, *path) + ".preql"
972
989
  # tokens + text are cached by path
973
- token_lookup: Path | str = Path(target)
990
+ token_lookup = Path(target)
974
991
  elif isinstance(self.environment.config.import_resolver, DictImportResolver):
975
992
  target = ".".join(path)
976
993
  token_lookup = target
@@ -991,7 +1008,7 @@ class ParseToObjects(Transformer):
991
1008
  raw_tokens = self.tokens[token_lookup]
992
1009
  text = self.text_lookup[token_lookup]
993
1010
  else:
994
- text = self.resolve_import_address(target)
1011
+ text = self.resolve_import_address(target, is_stdlib)
995
1012
  self.text_lookup[token_lookup] = text
996
1013
 
997
1014
  try:
@@ -287,7 +287,7 @@
287
287
  _YEAR.1: "year("i
288
288
  fyear: _YEAR expr ")"
289
289
 
290
- DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i
290
+ DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i | "DAY_OF_WEEK"i
291
291
  _DATE_TRUNC.1: "date_trunc("i
292
292
  fdate_trunc: _DATE_TRUNC expr "," DATE_PART ")"
293
293
  _DATE_PART.1: "date_part("i
File without changes
File without changes
File without changes