pytrilogy 0.0.3.88__tar.gz → 0.0.3.89__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 (151) hide show
  1. {pytrilogy-0.0.3.88/pytrilogy.egg-info → pytrilogy-0.0.3.89}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/__init__.py +1 -1
  4. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/functions.py +2 -1
  5. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/author.py +27 -1
  6. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/core.py +13 -0
  7. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/LICENSE.md +0 -0
  8. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/README.md +0 -0
  9. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pyproject.toml +0 -0
  10. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pytrilogy.egg-info/SOURCES.txt +0 -0
  11. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pytrilogy.egg-info/dependency_links.txt +0 -0
  12. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pytrilogy.egg-info/entry_points.txt +0 -0
  13. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pytrilogy.egg-info/requires.txt +0 -0
  14. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/pytrilogy.egg-info/top_level.txt +0 -0
  15. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/setup.cfg +0 -0
  16. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/setup.py +0 -0
  17. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_datatypes.py +0 -0
  18. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_declarations.py +0 -0
  19. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_derived_concepts.py +0 -0
  20. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_discovery_nodes.py +0 -0
  21. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_enums.py +0 -0
  22. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_environment.py +0 -0
  23. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_execute_models.py +0 -0
  24. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_executor.py +0 -0
  25. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_failure.py +0 -0
  26. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_functions.py +0 -0
  27. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_imports.py +0 -0
  28. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_metadata.py +0 -0
  29. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_models.py +0 -0
  30. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_multi_join_assignments.py +0 -0
  31. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_parse_engine.py +0 -0
  32. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_parsing.py +0 -0
  33. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_parsing_failures.py +0 -0
  34. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_partial_handling.py +0 -0
  35. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_query_processing.py +0 -0
  36. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_query_render.py +0 -0
  37. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_select.py +0 -0
  38. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_show.py +0 -0
  39. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_statements.py +0 -0
  40. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_typing.py +0 -0
  41. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_undefined_concept.py +0 -0
  42. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_user_functions.py +0 -0
  43. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/tests/test_where_clause.py +0 -0
  44. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/authoring/__init__.py +0 -0
  45. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/compiler.py +0 -0
  46. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/constants.py +0 -0
  47. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/__init__.py +0 -0
  48. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/constants.py +0 -0
  49. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/enums.py +0 -0
  50. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/env_processor.py +0 -0
  51. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/environment_helpers.py +0 -0
  52. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/ergonomics.py +0 -0
  53. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/exceptions.py +0 -0
  54. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/graph_models.py +0 -0
  55. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/internal.py +0 -0
  56. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/__init__.py +0 -0
  57. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/build.py +0 -0
  58. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/build_environment.py +0 -0
  59. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/datasource.py +0 -0
  60. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/environment.py +0 -0
  61. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/models/execute.py +0 -0
  62. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/optimization.py +0 -0
  63. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/optimizations/__init__.py +0 -0
  64. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/optimizations/base_optimization.py +0 -0
  65. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/optimizations/inline_datasource.py +0 -0
  66. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  67. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/__init__.py +0 -0
  68. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  69. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/discovery_loop.py +0 -0
  70. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/discovery_node_factory.py +0 -0
  71. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/discovery_utility.py +0 -0
  72. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/discovery_validation.py +0 -0
  73. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/graph_utils.py +0 -0
  74. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/__init__.py +0 -0
  75. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  76. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/common.py +0 -0
  77. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/constant_node.py +0 -0
  78. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  79. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/group_node.py +0 -0
  80. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  81. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  82. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  83. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
  84. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  85. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  86. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  87. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  88. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/select_node.py +0 -0
  89. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  90. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/union_node.py +0 -0
  91. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  92. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/node_generators/window_node.py +0 -0
  93. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/__init__.py +0 -0
  94. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/base_node.py +0 -0
  95. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/filter_node.py +0 -0
  96. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/group_node.py +0 -0
  97. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/merge_node.py +0 -0
  98. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/recursive_node.py +0 -0
  99. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  100. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/union_node.py +0 -0
  101. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  102. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/nodes/window_node.py +0 -0
  103. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/processing/utility.py +0 -0
  104. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/query_processor.py +0 -0
  105. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/statements/__init__.py +0 -0
  106. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/statements/author.py +0 -0
  107. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/statements/build.py +0 -0
  108. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/statements/common.py +0 -0
  109. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/statements/execute.py +0 -0
  110. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/core/utility.py +0 -0
  111. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/__init__.py +0 -0
  112. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/base.py +0 -0
  113. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/bigquery.py +0 -0
  114. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/common.py +0 -0
  115. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/config.py +0 -0
  116. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/dataframe.py +0 -0
  117. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/duckdb.py +0 -0
  118. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/enums.py +0 -0
  119. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/postgres.py +0 -0
  120. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/presto.py +0 -0
  121. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/snowflake.py +0 -0
  122. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/dialect/sql_server.py +0 -0
  123. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/engine.py +0 -0
  124. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/executor.py +0 -0
  125. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/hooks/__init__.py +0 -0
  126. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/hooks/base_hook.py +0 -0
  127. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/hooks/graph_hook.py +0 -0
  128. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/hooks/query_debugger.py +0 -0
  129. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/metadata/__init__.py +0 -0
  130. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parser.py +0 -0
  131. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/__init__.py +0 -0
  132. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/common.py +0 -0
  133. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/config.py +0 -0
  134. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/exceptions.py +0 -0
  135. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/helpers.py +0 -0
  136. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/parse_engine.py +1 -1
  137. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/render.py +0 -0
  138. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/parsing/trilogy.lark +0 -0
  139. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/py.typed +0 -0
  140. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/render.py +0 -0
  141. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/scripts/__init__.py +0 -0
  142. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/scripts/trilogy.py +0 -0
  143. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/__init__.py +0 -0
  144. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/date.preql +0 -0
  145. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/display.preql +0 -0
  146. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/geography.preql +0 -0
  147. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/money.preql +0 -0
  148. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/net.preql +0 -0
  149. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/ranking.preql +0 -0
  150. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/std/report.preql +0 -0
  151. {pytrilogy-0.0.3.88 → pytrilogy-0.0.3.89}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.88
3
+ Version: 0.0.3.89
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.88
3
+ Version: 0.0.3.89
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -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.88"
7
+ __version__ = "0.0.3.89"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -92,7 +92,7 @@ def get_attr_datatype(
92
92
  lookup = args[1]
93
93
  datatype = arg_to_datatype(arg)
94
94
  if isinstance(datatype, StructType):
95
- return arg_to_datatype(datatype.fields_map[lookup])
95
+ return datatype.field_types[lookup]
96
96
  return datatype
97
97
 
98
98
 
@@ -935,6 +935,7 @@ class FunctionFactory:
935
935
  output_purpose = Purpose.METRIC
936
936
  else:
937
937
  output_purpose = Purpose.PROPERTY
938
+
938
939
  return Function(
939
940
  operator=operator,
940
941
  arguments=full_args, # type: ignore
@@ -172,11 +172,16 @@ class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
172
172
  for candidate in candidates:
173
173
  if not candidate.startswith(f"{source}."):
174
174
  continue
175
+ attribute = self.address.rsplit(".", 1)[1]
176
+ dtype = arg_to_datatype(target)
177
+ if not isinstance(dtype, StructType):
178
+ continue
179
+ output_type = dtype.field_types.get(attribute, DataType.UNKNOWN)
175
180
  return Function(
176
181
  arguments=[target, self.address.rsplit(".", 1)[1]],
177
182
  operator=FunctionType.ATTR_ACCESS,
178
183
  arg_count=2,
179
- output_datatype=arg_to_datatype(target),
184
+ output_datatype=output_type,
180
185
  output_purpose=Purpose.PROPERTY,
181
186
  )
182
187
  return self
@@ -1637,6 +1642,9 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1637
1642
  ] = None
1638
1643
  arguments: Sequence[FuncArgs]
1639
1644
 
1645
+ class Config:
1646
+ frozen = True
1647
+
1640
1648
  def __repr__(self):
1641
1649
  return f'{self.operator.value}({",".join([str(a) for a in self.arguments])})'
1642
1650
 
@@ -1647,6 +1655,16 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1647
1655
  def datatype(self):
1648
1656
  return self.output_datatype
1649
1657
 
1658
+ @field_validator("output_datatype")
1659
+ @classmethod
1660
+ def parse_output_datatype(cls, v, info: ValidationInfo):
1661
+ values = info.data
1662
+ if values.get("operator") == FunctionType.ATTR_ACCESS:
1663
+ print(v)
1664
+ if isinstance(v, StructType):
1665
+ raise SyntaxError
1666
+ return v
1667
+
1650
1668
  @field_validator("arguments", mode="before")
1651
1669
  @classmethod
1652
1670
  def parse_arguments(cls, v, info: ValidationInfo):
@@ -1745,8 +1763,16 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1745
1763
  ]
1746
1764
  if self.output_datatype == DataType.UNKNOWN:
1747
1765
  new_output = merge_datatypes([arg_to_datatype(x) for x in nargs])
1766
+
1767
+ if self.operator == FunctionType.ATTR_ACCESS:
1768
+ if isinstance(new_output, StructType):
1769
+ new_output = new_output.field_types[str(nargs[1])]
1748
1770
  else:
1749
1771
  new_output = self.output_datatype
1772
+ # this is not ideal - see hacky logic for datatypes above
1773
+ # we need to figure out how to patch properly
1774
+ # should use function factory, but does not have environment access
1775
+ # probably move all datatype resolution to build?
1750
1776
  return Function.model_construct(
1751
1777
  operator=self.operator,
1752
1778
  arguments=nargs,
@@ -244,6 +244,19 @@ class StructType(BaseModel):
244
244
  def value(self):
245
245
  return self.data_type.value
246
246
 
247
+ @property
248
+ def field_types(self) -> Dict[str, CONCRETE_TYPES]:
249
+ out: Dict[str, CONCRETE_TYPES] = {}
250
+ keys = list(self.fields_map.keys())
251
+ for idx, field in enumerate(self.fields):
252
+ if isinstance(field, StructComponent):
253
+ out[field.name] = arg_to_datatype(field.type)
254
+ elif isinstance(field, DataTyped):
255
+ out[keys[idx]] = field.output_datatype
256
+ else:
257
+ out[keys[idx]] = field
258
+ return out
259
+
247
260
  def __hash__(self):
248
261
  return hash(str(self))
249
262
 
File without changes
File without changes
File without changes
File without changes
@@ -701,6 +701,7 @@ class ParseToObjects(Transformer):
701
701
  environment=self.environment,
702
702
  metadata=metadata,
703
703
  )
704
+
704
705
  # let constant purposes exist to support round-tripping
705
706
  # as a build concept may end up with a constant based on constant inlining happening recursively
706
707
  if purpose == Purpose.KEY and concept.purpose != Purpose.KEY:
@@ -922,7 +923,6 @@ class ParseToObjects(Transformer):
922
923
  name=output,
923
924
  metadata=metadata,
924
925
  )
925
-
926
926
  return ConceptTransform(function=transformation, output=concept)
927
927
 
928
928
  @v_args(meta=True)