pylegend 0.2.2__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
Files changed (123) hide show
  1. pylegend/__init__.py +9 -5
  2. pylegend/core/{databse → database}/sql_to_string/__init__.py +3 -3
  3. pylegend/core/{databse → database}/sql_to_string/db_extension.py +11 -5
  4. pylegend/core/{databse → database}/sql_to_string/generator.py +2 -2
  5. pylegend/core/language/__init__.py +10 -10
  6. pylegend/core/language/legacy_api/__init__.py +13 -0
  7. pylegend/core/language/{aggregate_specification.py → legacy_api/aggregate_specification.py} +10 -10
  8. pylegend/core/language/legacy_api/legacy_api_tds_row.py +32 -0
  9. pylegend/core/language/legendql_api/__init__.py +13 -0
  10. pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +541 -0
  11. pylegend/core/language/legendql_api/legendql_api_tds_row.py +292 -0
  12. pylegend/core/language/shared/__init__.py +13 -0
  13. pylegend/core/language/{column_expressions.py → shared/column_expressions.py} +32 -31
  14. pylegend/core/language/{expression.py → shared/expression.py} +8 -0
  15. pylegend/core/language/{functions.py → shared/functions.py} +3 -3
  16. pylegend/core/language/shared/helpers.py +75 -0
  17. pylegend/core/language/{literal_expressions.py → shared/literal_expressions.py} +39 -1
  18. pylegend/core/language/{operations → shared/operations}/binary_expression.py +34 -2
  19. pylegend/core/language/{operations → shared/operations}/boolean_operation_expressions.py +34 -6
  20. pylegend/core/language/{operations → shared/operations}/collection_operation_expressions.py +146 -26
  21. pylegend/core/language/{operations → shared/operations}/date_operation_expressions.py +164 -24
  22. pylegend/core/language/{operations → shared/operations}/float_operation_expressions.py +53 -8
  23. pylegend/core/language/{operations → shared/operations}/integer_operation_expressions.py +62 -9
  24. pylegend/core/language/{operations → shared/operations}/nullary_expression.py +9 -2
  25. pylegend/core/language/{operations → shared/operations}/number_operation_expressions.py +211 -30
  26. pylegend/core/language/{operations → shared/operations}/primitive_operation_expressions.py +42 -3
  27. pylegend/core/language/{operations → shared/operations}/string_operation_expressions.py +169 -21
  28. pylegend/core/language/{operations → shared/operations}/unary_expression.py +10 -2
  29. pylegend/core/language/{primitive_collection.py → shared/primitive_collection.py} +2 -2
  30. pylegend/core/language/{primitives → shared/primitives}/__init__.py +9 -9
  31. pylegend/core/language/{primitives → shared/primitives}/boolean.py +9 -5
  32. pylegend/core/language/{primitives → shared/primitives}/date.py +23 -15
  33. pylegend/core/language/{primitives → shared/primitives}/datetime.py +4 -5
  34. pylegend/core/language/{primitives → shared/primitives}/float.py +6 -6
  35. pylegend/core/language/{primitives → shared/primitives}/integer.py +6 -6
  36. pylegend/core/language/{primitives → shared/primitives}/number.py +16 -13
  37. pylegend/core/language/{primitives → shared/primitives}/primitive.py +25 -5
  38. pylegend/core/language/{primitives → shared/primitives}/strictdate.py +4 -5
  39. pylegend/core/language/{primitives → shared/primitives}/string.py +18 -19
  40. pylegend/core/language/{tds_row.py → shared/tds_row.py} +46 -16
  41. pylegend/core/request/__init__.py +8 -1
  42. pylegend/core/request/auth.py +89 -11
  43. pylegend/core/request/legend_client.py +32 -0
  44. pylegend/core/sql/metamodel_extension.py +16 -0
  45. pylegend/core/tds/abstract/__init__.py +13 -0
  46. pylegend/core/tds/abstract/frames/__init__.py +13 -0
  47. pylegend/core/tds/{legend_api/frames/legend_api_applied_function_tds_frame.py → abstract/frames/applied_function_tds_frame.py} +19 -13
  48. pylegend/core/tds/abstract/frames/base_tds_frame.py +125 -0
  49. pylegend/core/tds/{legend_api/frames/legend_api_input_tds_frame.py → abstract/frames/input_tds_frame.py} +9 -12
  50. pylegend/core/tds/{legend_api/frames/functions → abstract}/function_helpers.py +1 -1
  51. pylegend/core/tds/{legend_api/frames/functions/concatenate_function.py → legacy_api/frames/functions/legacy_api_concatenate_function.py} +25 -13
  52. pylegend/core/tds/{legend_api/frames/functions/distinct_function.py → legacy_api/frames/functions/legacy_api_distinct_function.py} +13 -8
  53. pylegend/core/tds/{legend_api/frames/functions/drop_function.py → legacy_api/frames/functions/legacy_api_drop_function.py} +13 -8
  54. pylegend/core/tds/{legend_api/frames/functions/extend_function.py → legacy_api/frames/functions/legacy_api_extend_function.py} +36 -16
  55. pylegend/core/tds/{legend_api/frames/functions/filter_function.py → legacy_api/frames/functions/legacy_api_filter_function.py} +25 -13
  56. pylegend/core/tds/{legend_api/frames/functions/group_by_function.py → legacy_api/frames/functions/legacy_api_group_by_function.py} +44 -17
  57. pylegend/core/tds/{legend_api/frames/functions/head_function.py → legacy_api/frames/functions/legacy_api_head_function.py} +13 -8
  58. pylegend/core/tds/{legend_api/frames/functions/join_by_columns_function.py → legacy_api/frames/functions/legacy_api_join_by_columns_function.py} +40 -13
  59. pylegend/core/tds/{legend_api/frames/functions/join_function.py → legacy_api/frames/functions/legacy_api_join_function.py} +44 -20
  60. pylegend/core/tds/{legend_api/frames/functions/rename_columns_function.py → legacy_api/frames/functions/legacy_api_rename_columns_function.py} +20 -8
  61. pylegend/core/tds/{legend_api/frames/functions/restrict_function.py → legacy_api/frames/functions/legacy_api_restrict_function.py} +17 -8
  62. pylegend/core/tds/{legend_api/frames/functions/slice_function.py → legacy_api/frames/functions/legacy_api_slice_function.py} +13 -8
  63. pylegend/core/tds/{legend_api/frames/functions/sort_function.py → legacy_api/frames/functions/legacy_api_sort_function.py} +19 -8
  64. pylegend/core/tds/legacy_api/frames/legacy_api_applied_function_tds_frame.py +37 -0
  65. pylegend/core/tds/legacy_api/frames/legacy_api_base_tds_frame.py +204 -0
  66. pylegend/core/tds/legacy_api/frames/legacy_api_input_tds_frame.py +51 -0
  67. pylegend/core/tds/{legend_api/frames/legend_api_tds_frame.py → legacy_api/frames/legacy_api_tds_frame.py} +28 -28
  68. pylegend/core/tds/legendql_api/__init__.py +13 -0
  69. pylegend/core/tds/legendql_api/frames/__init__.py +13 -0
  70. pylegend/core/tds/legendql_api/frames/functions/__init__.py +13 -0
  71. pylegend/core/tds/legendql_api/frames/functions/legendql_api_asofjoin_function.py +156 -0
  72. pylegend/core/tds/legendql_api/frames/functions/legendql_api_concatenate_function.py +139 -0
  73. pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +69 -0
  74. pylegend/core/tds/legendql_api/frames/functions/legendql_api_drop_function.py +74 -0
  75. pylegend/core/tds/legendql_api/frames/functions/legendql_api_extend_function.py +256 -0
  76. pylegend/core/tds/legendql_api/frames/functions/legendql_api_filter_function.py +121 -0
  77. pylegend/core/tds/legendql_api/frames/functions/legendql_api_function_helpers.py +137 -0
  78. pylegend/core/tds/legendql_api/frames/functions/legendql_api_groupby_function.py +256 -0
  79. pylegend/core/tds/legendql_api/frames/functions/legendql_api_head_function.py +74 -0
  80. pylegend/core/tds/legendql_api/frames/functions/legendql_api_join_function.py +214 -0
  81. pylegend/core/tds/legendql_api/frames/functions/legendql_api_project_function.py +169 -0
  82. pylegend/core/tds/legendql_api/frames/functions/legendql_api_rename_function.py +189 -0
  83. pylegend/core/tds/legendql_api/frames/functions/legendql_api_select_function.py +131 -0
  84. pylegend/core/tds/legendql_api/frames/functions/legendql_api_slice_function.py +82 -0
  85. pylegend/core/tds/legendql_api/frames/functions/legendql_api_sort_function.py +93 -0
  86. pylegend/core/tds/legendql_api/frames/functions/legendql_api_window_extend_function.py +283 -0
  87. pylegend/core/tds/legendql_api/frames/legendql_api_applied_function_tds_frame.py +37 -0
  88. pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +419 -0
  89. pylegend/core/tds/legendql_api/frames/legendql_api_input_tds_frame.py +50 -0
  90. pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +327 -0
  91. pylegend/core/tds/pandas_api/frames/functions/assign_function.py +6 -6
  92. pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +4 -0
  93. pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +11 -3
  94. pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +2 -2
  95. pylegend/core/tds/tds_frame.py +32 -2
  96. pylegend/extensions/database/vendors/postgres/postgres_sql_to_string.py +1 -1
  97. pylegend/extensions/tds/abstract/legend_function_input_frame.py +4 -0
  98. pylegend/extensions/tds/abstract/legend_service_input_frame.py +4 -0
  99. pylegend/extensions/tds/abstract/table_spec_input_frame.py +4 -0
  100. pylegend/extensions/tds/{legend_api/frames/legend_api_legend_function_input_frame.py → legacy_api/frames/legacy_api_legend_function_input_frame.py} +5 -5
  101. pylegend/extensions/tds/{legend_api/frames/legend_api_legend_service_input_frame.py → legacy_api/frames/legacy_api_legend_service_input_frame.py} +6 -6
  102. pylegend/extensions/tds/{legend_api/frames/legend_api_table_spec_input_frame.py → legacy_api/frames/legacy_api_table_spec_input_frame.py} +5 -5
  103. pylegend/extensions/tds/legendql_api/__init__.py +13 -0
  104. pylegend/extensions/tds/legendql_api/frames/__init__.py +13 -0
  105. pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_service_input_frame.py +46 -0
  106. pylegend/extensions/tds/legendql_api/frames/legendql_api_table_spec_input_frame.py +36 -0
  107. pylegend/{legend_api_tds_client.py → legacy_api_tds_client.py} +15 -15
  108. {pylegend-0.2.2.dist-info → pylegend-0.4.0.dist-info}/METADATA +7 -8
  109. pylegend-0.4.0.dist-info/NOTICE +5 -0
  110. pylegend-0.4.0.dist-info/RECORD +155 -0
  111. {pylegend-0.2.2.dist-info → pylegend-0.4.0.dist-info}/WHEEL +1 -1
  112. pylegend/core/tds/legend_api/frames/legend_api_base_tds_frame.py +0 -294
  113. pylegend-0.2.2.dist-info/RECORD +0 -115
  114. /pylegend/core/{databse → database}/__init__.py +0 -0
  115. /pylegend/core/{databse → database}/sql_to_string/config.py +0 -0
  116. /pylegend/core/language/{operations → shared/operations}/__init__.py +0 -0
  117. /pylegend/core/tds/{legend_api → legacy_api}/__init__.py +0 -0
  118. /pylegend/core/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
  119. /pylegend/core/tds/{legend_api → legacy_api}/frames/functions/__init__.py +0 -0
  120. /pylegend/extensions/tds/{legend_api → legacy_api}/__init__.py +0 -0
  121. /pylegend/extensions/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
  122. {pylegend-0.2.2.dist-info → pylegend-0.4.0.dist-info}/LICENSE +0 -0
  123. {pylegend-0.2.2.dist-info → pylegend-0.4.0.dist-info}/LICENSE.spdx +0 -0
@@ -15,21 +15,21 @@
15
15
  from abc import ABCMeta, abstractmethod
16
16
  from pylegend._typing import (
17
17
  PyLegendSequence,
18
- PyLegendList,
19
18
  )
20
19
  from pylegend.core.sql.metamodel import QuerySpecification
21
20
  from pylegend.core.tds.tds_column import TdsColumn
22
21
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
23
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
22
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
23
+ from pylegend.core.tds.abstract.frames.base_tds_frame import BaseTdsFrame
24
24
 
25
25
 
26
26
  __all__: PyLegendSequence[str] = [
27
- "LegendApiAppliedFunctionTdsFrame",
28
- "LegendApiAppliedFunction",
27
+ "AppliedFunctionTdsFrame",
28
+ "AppliedFunction",
29
29
  ]
30
30
 
31
31
 
32
- class LegendApiAppliedFunction(metaclass=ABCMeta):
32
+ class AppliedFunction(metaclass=ABCMeta):
33
33
  @classmethod
34
34
  @abstractmethod
35
35
  def name(cls) -> str:
@@ -39,16 +39,19 @@ class LegendApiAppliedFunction(metaclass=ABCMeta):
39
39
  def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
40
40
  pass # pragma: no cover
41
41
 
42
+ def to_pure(self, config: FrameToPureConfig) -> str:
43
+ raise RuntimeError("PURE generation not supported for '" + self.name() + "' function")
44
+
42
45
  @abstractmethod
43
- def base_frame(self) -> LegendApiBaseTdsFrame:
46
+ def base_frame(self) -> BaseTdsFrame:
44
47
  pass # pragma: no cover
45
48
 
46
49
  @abstractmethod
47
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
50
+ def tds_frame_parameters(self) -> PyLegendSequence[BaseTdsFrame]:
48
51
  pass # pragma: no cover
49
52
 
50
53
  @abstractmethod
51
- def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
54
+ def calculate_columns(self) -> PyLegendSequence[TdsColumn]:
52
55
  pass # pragma: no cover
53
56
 
54
57
  @abstractmethod
@@ -56,10 +59,10 @@ class LegendApiAppliedFunction(metaclass=ABCMeta):
56
59
  pass # pragma: no cover
57
60
 
58
61
 
59
- class LegendApiAppliedFunctionTdsFrame(LegendApiBaseTdsFrame):
60
- __applied_function: LegendApiAppliedFunction
62
+ class AppliedFunctionTdsFrame(BaseTdsFrame, metaclass=ABCMeta):
63
+ __applied_function: AppliedFunction
61
64
 
62
- def __init__(self, applied_function: LegendApiAppliedFunction):
65
+ def __init__(self, applied_function: AppliedFunction):
63
66
  applied_function.validate()
64
67
  super().__init__(columns=applied_function.calculate_columns())
65
68
  self.__applied_function = applied_function
@@ -67,9 +70,12 @@ class LegendApiAppliedFunctionTdsFrame(LegendApiBaseTdsFrame):
67
70
  def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
68
71
  return self.__applied_function.to_sql(config)
69
72
 
70
- def get_all_tds_frames(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
73
+ def to_pure(self, config: FrameToPureConfig) -> str:
74
+ return self.__applied_function.to_pure(config)
75
+
76
+ def get_all_tds_frames(self) -> PyLegendSequence[BaseTdsFrame]:
71
77
  return [
72
78
  y
73
- for x in [self.__applied_function.base_frame()] + self.__applied_function.tds_frame_parameters()
79
+ for x in [self.__applied_function.base_frame()] + list(self.__applied_function.tds_frame_parameters())
74
80
  for y in x.get_all_tds_frames()
75
81
  ] + [self]
@@ -0,0 +1,125 @@
1
+ # Copyright 2025 Goldman Sachs
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from abc import ABCMeta, abstractmethod
16
+ import pandas as pd
17
+ from pylegend._typing import (
18
+ PyLegendSequence,
19
+ PyLegendTypeVar,
20
+ PyLegendOptional,
21
+ )
22
+ from pylegend.core.sql.metamodel import QuerySpecification
23
+ from pylegend.core.database.sql_to_string import (
24
+ SqlToStringConfig,
25
+ SqlToStringFormat
26
+ )
27
+ from pylegend.core.tds.tds_column import TdsColumn
28
+ from pylegend.core.tds.tds_frame import FrameToSqlConfig, PyLegendTdsFrame
29
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
30
+ from pylegend.core.tds.result_handler import (
31
+ ResultHandler,
32
+ ToStringResultHandler,
33
+ )
34
+ from pylegend.extensions.tds.result_handler import (
35
+ ToPandasDfResultHandler,
36
+ PandasDfReadConfig,
37
+ )
38
+ __all__: PyLegendSequence[str] = [
39
+ "BaseTdsFrame"
40
+ ]
41
+
42
+ R = PyLegendTypeVar('R')
43
+
44
+
45
+ class BaseTdsFrame(PyLegendTdsFrame, metaclass=ABCMeta):
46
+ __columns: PyLegendSequence[TdsColumn]
47
+
48
+ def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
49
+ col_names = [c.get_name() for c in columns]
50
+ if len(col_names) != len(set(col_names)):
51
+ cols = "[" + ", ".join([str(c) for c in columns]) + "]"
52
+ raise ValueError(f"TdsFrame cannot have duplicated column names. Passed columns: {cols}")
53
+ self.__columns = [c.copy() for c in columns]
54
+
55
+ def columns(self) -> PyLegendSequence[TdsColumn]:
56
+ return [c.copy() for c in self.__columns]
57
+
58
+ @abstractmethod
59
+ def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
60
+ pass # pragma: no cover
61
+
62
+ @abstractmethod
63
+ def get_all_tds_frames(self) -> PyLegendSequence["BaseTdsFrame"]:
64
+ pass # pragma: no cover
65
+
66
+ def to_sql_query(self, config: FrameToSqlConfig = FrameToSqlConfig()) -> str:
67
+ query = self.to_sql_query_object(config)
68
+ sql_to_string_config = SqlToStringConfig(format_=SqlToStringFormat(pretty=config.pretty))
69
+ return config.sql_to_string_generator().generate_sql_string(query, sql_to_string_config)
70
+
71
+ @abstractmethod
72
+ def to_pure(self, config: FrameToPureConfig) -> str:
73
+ pass # pragma: no cover
74
+
75
+ def to_pure_query(self, config: FrameToPureConfig = FrameToPureConfig()) -> str:
76
+ return self.to_pure(config)
77
+
78
+ def execute_frame(
79
+ self,
80
+ result_handler: ResultHandler[R],
81
+ chunk_size: PyLegendOptional[int] = None
82
+ ) -> R:
83
+ from pylegend.core.tds.abstract.frames.input_tds_frame import InputTdsFrame, ExecutableInputTdsFrame
84
+
85
+ tds_frames = self.get_all_tds_frames()
86
+ input_frames = [x for x in tds_frames if isinstance(x, InputTdsFrame)]
87
+
88
+ non_exec_frames = [x for x in input_frames if not isinstance(x, ExecutableInputTdsFrame)]
89
+ if non_exec_frames:
90
+ raise ValueError(
91
+ "Cannot execute frame as its built on top of non-executable input frames: [" +
92
+ (", ".join([str(f) for f in non_exec_frames]) + "]")
93
+ )
94
+
95
+ exec_frames = [x for x in input_frames if isinstance(x, ExecutableInputTdsFrame)]
96
+
97
+ all_legend_clients = []
98
+ for e in exec_frames:
99
+ c = e.get_legend_client()
100
+ if c not in all_legend_clients:
101
+ all_legend_clients.append(c)
102
+ if len(all_legend_clients) > 1:
103
+ raise ValueError(
104
+ "Found tds frames with multiple legend_clients (which is not supported): [" +
105
+ (", ".join([str(f) for f in all_legend_clients]) + "]")
106
+ )
107
+ legend_client = all_legend_clients[0]
108
+ result = legend_client.execute_sql_string(self.to_sql_query(), chunk_size=chunk_size)
109
+ return result_handler.handle_result(self, result)
110
+
111
+ def execute_frame_to_string(
112
+ self,
113
+ chunk_size: PyLegendOptional[int] = None
114
+ ) -> str:
115
+ return self.execute_frame(ToStringResultHandler(), chunk_size)
116
+
117
+ def execute_frame_to_pandas_df(
118
+ self,
119
+ chunk_size: PyLegendOptional[int] = None,
120
+ pandas_df_read_config: PandasDfReadConfig = PandasDfReadConfig()
121
+ ) -> pd.DataFrame:
122
+ return self.execute_frame(
123
+ ToPandasDfResultHandler(pandas_df_read_config),
124
+ chunk_size
125
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Goldman Sachs
1
+ # Copyright 2025 Goldman Sachs
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -15,30 +15,28 @@
15
15
  from abc import ABCMeta
16
16
  from pylegend._typing import (
17
17
  PyLegendSequence,
18
- PyLegendList
19
18
  )
20
19
  from pylegend.core.tds.tds_column import TdsColumn
21
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
20
+ from pylegend.core.tds.abstract.frames.base_tds_frame import BaseTdsFrame
22
21
  from pylegend.core.request.legend_client import LegendClient
23
22
 
24
23
 
25
24
  __all__: PyLegendSequence[str] = [
26
- "LegendApiExecutableInputTdsFrame",
27
- "LegendApiNonExecutableInputTdsFrame",
28
- "LegendApiInputTdsFrame"
25
+ "ExecutableInputTdsFrame",
26
+ "NonExecutableInputTdsFrame",
27
+ "InputTdsFrame"
29
28
  ]
30
29
 
31
30
 
32
- class LegendApiInputTdsFrame(LegendApiBaseTdsFrame, metaclass=ABCMeta):
33
-
31
+ class InputTdsFrame(BaseTdsFrame, metaclass=ABCMeta):
34
32
  def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
35
33
  super().__init__(columns=columns)
36
34
 
37
- def get_all_tds_frames(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
35
+ def get_all_tds_frames(self) -> PyLegendSequence["BaseTdsFrame"]:
38
36
  return [self]
39
37
 
40
38
 
41
- class LegendApiExecutableInputTdsFrame(LegendApiInputTdsFrame, metaclass=ABCMeta):
39
+ class ExecutableInputTdsFrame(InputTdsFrame, metaclass=ABCMeta):
42
40
  __legend_client: LegendClient
43
41
 
44
42
  def __init__(self, legend_client: LegendClient, columns: PyLegendSequence[TdsColumn]) -> None:
@@ -49,7 +47,6 @@ class LegendApiExecutableInputTdsFrame(LegendApiInputTdsFrame, metaclass=ABCMeta
49
47
  return self.__legend_client
50
48
 
51
49
 
52
- class LegendApiNonExecutableInputTdsFrame(LegendApiInputTdsFrame, metaclass=ABCMeta):
53
-
50
+ class NonExecutableInputTdsFrame(InputTdsFrame, metaclass=ABCMeta):
54
51
  def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
55
52
  super().__init__(columns=columns)
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Goldman Sachs
1
+ # Copyright 2025 Goldman Sachs
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@ from pylegend._typing import (
16
16
  PyLegendList,
17
17
  PyLegendSequence
18
18
  )
19
- from pylegend.core.tds.legend_api.frames.legend_api_applied_function_tds_frame import LegendApiAppliedFunction
19
+ from pylegend.core.tds.legacy_api.frames.legacy_api_applied_function_tds_frame import LegacyApiAppliedFunction
20
20
  from pylegend.core.sql.metamodel import (
21
21
  QuerySpecification,
22
22
  Union,
@@ -28,29 +28,31 @@ from pylegend.core.sql.metamodel import (
28
28
  TableSubquery,
29
29
  Query,
30
30
  )
31
+ from pylegend.core.tds.sql_query_helpers import create_sub_query
31
32
  from pylegend.core.tds.tds_column import TdsColumn
32
33
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
33
- from pylegend.core.tds.legend_api.frames.legend_api_tds_frame import LegendApiTdsFrame
34
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
34
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
35
+ from pylegend.core.tds.legacy_api.frames.legacy_api_tds_frame import LegacyApiTdsFrame
36
+ from pylegend.core.tds.legacy_api.frames.legacy_api_base_tds_frame import LegacyApiBaseTdsFrame
35
37
 
36
38
 
37
39
  __all__: PyLegendSequence[str] = [
38
- "ConcatenateFunction"
40
+ "LegacyApiConcatenateFunction"
39
41
  ]
40
42
 
41
43
 
42
- class ConcatenateFunction(LegendApiAppliedFunction):
43
- __base_frame: LegendApiBaseTdsFrame
44
- __other_frame: LegendApiBaseTdsFrame
44
+ class LegacyApiConcatenateFunction(LegacyApiAppliedFunction):
45
+ __base_frame: LegacyApiBaseTdsFrame
46
+ __other_frame: LegacyApiBaseTdsFrame
45
47
 
46
48
  @classmethod
47
49
  def name(cls) -> str:
48
50
  return "concatenate"
49
51
 
50
- def __init__(self, base_frame: LegendApiBaseTdsFrame, other: LegendApiTdsFrame) -> None:
52
+ def __init__(self, base_frame: LegacyApiBaseTdsFrame, other: LegacyApiTdsFrame) -> None:
51
53
  self.__base_frame = base_frame
52
- if not isinstance(other, LegendApiBaseTdsFrame):
53
- raise ValueError("Expected LegendApiBaseTdsFrame") # pragma: no cover
54
+ if not isinstance(other, LegacyApiBaseTdsFrame):
55
+ raise ValueError("Expected LegacyApiBaseTdsFrame") # pragma: no cover
54
56
  self.__other_frame = other
55
57
 
56
58
  def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
@@ -76,7 +78,11 @@ class ConcatenateFunction(LegendApiAppliedFunction):
76
78
  AliasedRelation(
77
79
  relation=TableSubquery(
78
80
  query=Query(
79
- queryBody=Union(left=base_query, right=other_query, distinct=False),
81
+ queryBody=Union(
82
+ left=create_sub_query(base_query, config, "left"),
83
+ right=create_sub_query(other_query, config, "right"),
84
+ distinct=False
85
+ ),
80
86
  limit=None, offset=None, orderBy=[]
81
87
  )
82
88
  ),
@@ -92,10 +98,16 @@ class ConcatenateFunction(LegendApiAppliedFunction):
92
98
  offset=None
93
99
  )
94
100
 
95
- def base_frame(self) -> LegendApiBaseTdsFrame:
101
+ def to_pure(self, config: FrameToPureConfig) -> str:
102
+ return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
103
+ f"->concatenate({config.separator(2)}"
104
+ f"{self.__other_frame.to_pure(config.push_indent(2))}"
105
+ f"{config.separator(1)})")
106
+
107
+ def base_frame(self) -> LegacyApiBaseTdsFrame:
96
108
  return self.__base_frame
97
109
 
98
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
110
+ def tds_frame_parameters(self) -> PyLegendList["LegacyApiBaseTdsFrame"]:
99
111
  return [self.__other_frame]
100
112
 
101
113
  def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
@@ -16,29 +16,30 @@ from pylegend._typing import (
16
16
  PyLegendList,
17
17
  PyLegendSequence
18
18
  )
19
- from pylegend.core.tds.legend_api.frames.legend_api_applied_function_tds_frame import LegendApiAppliedFunction
19
+ from pylegend.core.tds.legacy_api.frames.legacy_api_applied_function_tds_frame import LegacyApiAppliedFunction
20
20
  from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
21
21
  from pylegend.core.sql.metamodel import (
22
22
  QuerySpecification,
23
23
  )
24
24
  from pylegend.core.tds.tds_column import TdsColumn
25
25
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
26
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
26
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
27
+ from pylegend.core.tds.legacy_api.frames.legacy_api_base_tds_frame import LegacyApiBaseTdsFrame
27
28
 
28
29
 
29
30
  __all__: PyLegendSequence[str] = [
30
- "DistinctFunction"
31
+ "LegacyApiDistinctFunction"
31
32
  ]
32
33
 
33
34
 
34
- class DistinctFunction(LegendApiAppliedFunction):
35
- __base_frame: LegendApiBaseTdsFrame
35
+ class LegacyApiDistinctFunction(LegacyApiAppliedFunction):
36
+ __base_frame: LegacyApiBaseTdsFrame
36
37
 
37
38
  @classmethod
38
39
  def name(cls) -> str:
39
40
  return "distinct"
40
41
 
41
- def __init__(self, base_frame: LegendApiBaseTdsFrame) -> None:
42
+ def __init__(self, base_frame: LegacyApiBaseTdsFrame) -> None:
42
43
  self.__base_frame = base_frame
43
44
 
44
45
  def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
@@ -51,10 +52,14 @@ class DistinctFunction(LegendApiAppliedFunction):
51
52
  new_query.select.distinct = True
52
53
  return new_query
53
54
 
54
- def base_frame(self) -> LegendApiBaseTdsFrame:
55
+ def to_pure(self, config: FrameToPureConfig) -> str:
56
+ return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
57
+ f"->distinct()")
58
+
59
+ def base_frame(self) -> LegacyApiBaseTdsFrame:
55
60
  return self.__base_frame
56
61
 
57
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
62
+ def tds_frame_parameters(self) -> PyLegendList["LegacyApiBaseTdsFrame"]:
58
63
  return []
59
64
 
60
65
  def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
@@ -16,7 +16,7 @@ from pylegend._typing import (
16
16
  PyLegendList,
17
17
  PyLegendSequence
18
18
  )
19
- from pylegend.core.tds.legend_api.frames.legend_api_applied_function_tds_frame import LegendApiAppliedFunction
19
+ from pylegend.core.tds.legacy_api.frames.legacy_api_applied_function_tds_frame import LegacyApiAppliedFunction
20
20
  from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
21
21
  from pylegend.core.sql.metamodel import (
22
22
  QuerySpecification,
@@ -24,23 +24,24 @@ from pylegend.core.sql.metamodel import (
24
24
  )
25
25
  from pylegend.core.tds.tds_column import TdsColumn
26
26
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
27
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
27
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
28
+ from pylegend.core.tds.legacy_api.frames.legacy_api_base_tds_frame import LegacyApiBaseTdsFrame
28
29
 
29
30
 
30
31
  __all__: PyLegendSequence[str] = [
31
- "DropFunction"
32
+ "LegacyApiDropFunction"
32
33
  ]
33
34
 
34
35
 
35
- class DropFunction(LegendApiAppliedFunction):
36
- __base_frame: LegendApiBaseTdsFrame
36
+ class LegacyApiDropFunction(LegacyApiAppliedFunction):
37
+ __base_frame: LegacyApiBaseTdsFrame
37
38
  __row_count: int
38
39
 
39
40
  @classmethod
40
41
  def name(cls) -> str:
41
42
  return "drop"
42
43
 
43
- def __init__(self, base_frame: LegendApiBaseTdsFrame, row_count: int) -> None:
44
+ def __init__(self, base_frame: LegacyApiBaseTdsFrame, row_count: int) -> None:
44
45
  self.__base_frame = base_frame
45
46
  self.__row_count = row_count
46
47
 
@@ -54,10 +55,14 @@ class DropFunction(LegendApiAppliedFunction):
54
55
  new_query.offset = LongLiteral(value=self.__row_count)
55
56
  return new_query
56
57
 
57
- def base_frame(self) -> LegendApiBaseTdsFrame:
58
+ def to_pure(self, config: FrameToPureConfig) -> str:
59
+ return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
60
+ f"->drop({self.__row_count})")
61
+
62
+ def base_frame(self) -> LegacyApiBaseTdsFrame:
58
63
  return self.__base_frame
59
64
 
60
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
65
+ def tds_frame_parameters(self) -> PyLegendList["LegacyApiBaseTdsFrame"]:
61
66
  return []
62
67
 
63
68
  def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
@@ -18,7 +18,8 @@ from pylegend._typing import (
18
18
  PyLegendSequence,
19
19
  PyLegendCallable,
20
20
  )
21
- from pylegend.core.tds.legend_api.frames.legend_api_applied_function_tds_frame import LegendApiAppliedFunction
21
+ from pylegend.core.tds.abstract.function_helpers import tds_column_for_primitive
22
+ from pylegend.core.tds.legacy_api.frames.legacy_api_applied_function_tds_frame import LegacyApiAppliedFunction
22
23
  from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
23
24
  from pylegend.core.sql.metamodel import (
24
25
  QuerySpecification,
@@ -26,23 +27,24 @@ from pylegend.core.sql.metamodel import (
26
27
  )
27
28
  from pylegend.core.tds.tds_column import TdsColumn
28
29
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
29
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
30
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
31
+ from pylegend.core.tds.legacy_api.frames.legacy_api_base_tds_frame import LegacyApiBaseTdsFrame
30
32
  from pylegend.core.language import (
31
- TdsRow,
33
+ LegacyApiTdsRow,
32
34
  PyLegendPrimitive,
33
35
  PyLegendPrimitiveOrPythonPrimitive,
34
36
  convert_literal_to_literal_expression,
35
37
  )
36
- from pylegend.core.tds.legend_api.frames.functions.function_helpers import tds_column_for_primitive
38
+ from pylegend.core.language.shared.helpers import generate_pure_lambda, escape_column_name
37
39
 
38
40
  __all__: PyLegendSequence[str] = [
39
- "ExtendFunction"
41
+ "LegacyApiExtendFunction"
40
42
  ]
41
43
 
42
44
 
43
- class ExtendFunction(LegendApiAppliedFunction):
44
- __base_frame: LegendApiBaseTdsFrame
45
- __functions_list: PyLegendList[PyLegendCallable[[TdsRow], PyLegendPrimitiveOrPythonPrimitive]]
45
+ class LegacyApiExtendFunction(LegacyApiAppliedFunction):
46
+ __base_frame: LegacyApiBaseTdsFrame
47
+ __functions_list: PyLegendList[PyLegendCallable[[LegacyApiTdsRow], PyLegendPrimitiveOrPythonPrimitive]]
46
48
  __column_names_list: PyLegendList[str]
47
49
 
48
50
  @classmethod
@@ -51,8 +53,8 @@ class ExtendFunction(LegendApiAppliedFunction):
51
53
 
52
54
  def __init__(
53
55
  self,
54
- base_frame: LegendApiBaseTdsFrame,
55
- functions_list: PyLegendList[PyLegendCallable[[TdsRow], PyLegendPrimitiveOrPythonPrimitive]],
56
+ base_frame: LegacyApiBaseTdsFrame,
57
+ functions_list: PyLegendList[PyLegendCallable[[LegacyApiTdsRow], PyLegendPrimitiveOrPythonPrimitive]],
56
58
  column_names_list: PyLegendList[str]
57
59
  ) -> None:
58
60
  self.__base_frame = base_frame
@@ -69,7 +71,7 @@ class ExtendFunction(LegendApiAppliedFunction):
69
71
  copy_query(base_query)
70
72
  )
71
73
 
72
- tds_row = TdsRow.from_tds_frame("frame", self.__base_frame)
74
+ tds_row = LegacyApiTdsRow.from_tds_frame("frame", self.__base_frame)
73
75
  for (func, name) in zip(self.__functions_list, self.__column_names_list):
74
76
  col_expr = func(tds_row)
75
77
 
@@ -87,16 +89,34 @@ class ExtendFunction(LegendApiAppliedFunction):
87
89
 
88
90
  return new_query
89
91
 
90
- def base_frame(self) -> LegendApiBaseTdsFrame:
92
+ def to_pure(self, config: FrameToPureConfig) -> str:
93
+ tds_row = LegacyApiTdsRow.from_tds_frame("r", self.__base_frame)
94
+ rendered_columns = []
95
+ for (func, col_name) in zip(self.__functions_list, self.__column_names_list):
96
+ col_expr = func(tds_row)
97
+ escaped_col_name = escape_column_name(col_name)
98
+ pure_expr = (col_expr.to_pure_expression(config) if isinstance(col_expr, PyLegendPrimitive) else
99
+ convert_literal_to_literal_expression(col_expr).to_pure_expression(config))
100
+ rendered_columns.append(f"{escaped_col_name}:{generate_pure_lambda('r', pure_expr)}")
101
+
102
+ return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}" +
103
+ (
104
+ (f"->extend(~{rendered_columns[0]})") if len(rendered_columns) == 1 else
105
+ (f"->extend(~[{config.separator(2)}"
106
+ f"{(',' + config.separator(2, True)).join(rendered_columns)}"
107
+ f"{config.separator(1)}])")
108
+ ))
109
+
110
+ def base_frame(self) -> LegacyApiBaseTdsFrame:
91
111
  return self.__base_frame
92
112
 
93
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
113
+ def tds_frame_parameters(self) -> PyLegendList["LegacyApiBaseTdsFrame"]:
94
114
  return []
95
115
 
96
116
  def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
97
117
  new_columns = [c.copy() for c in self.__base_frame.columns()]
98
118
 
99
- tds_row = TdsRow.from_tds_frame("frame", self.__base_frame)
119
+ tds_row = LegacyApiTdsRow.from_tds_frame("frame", self.__base_frame)
100
120
  for (func, name) in zip(self.__functions_list, self.__column_names_list):
101
121
  result = func(tds_row)
102
122
  new_columns.append(tds_column_for_primitive(name, result))
@@ -111,7 +131,7 @@ class ExtendFunction(LegendApiAppliedFunction):
111
131
  f"Column names: {len(self.__column_names_list)}"
112
132
  )
113
133
 
114
- tds_row = TdsRow.from_tds_frame("frame", self.__base_frame)
134
+ tds_row = LegacyApiTdsRow.from_tds_frame("frame", self.__base_frame)
115
135
  index = 0
116
136
  for (func, name) in zip(self.__functions_list, self.__column_names_list):
117
137
  copy = func # For MyPy
@@ -134,7 +154,7 @@ class ExtendFunction(LegendApiAppliedFunction):
134
154
  f"Error occurred while evaluating. Message: {str(e)}"
135
155
  ) from e
136
156
 
137
- if not isinstance(result, (int, float, bool, str, PyLegendPrimitive)):
157
+ if not isinstance(result, (int, float, bool, str, date, datetime, PyLegendPrimitive)):
138
158
  raise ValueError(
139
159
  f"Extend function at index {index} (0-indexed) incompatible. "
140
160
  f"Returns non-primitive - {str(type(result))}"
@@ -18,7 +18,7 @@ from pylegend._typing import (
18
18
  PyLegendCallable,
19
19
  PyLegendUnion,
20
20
  )
21
- from pylegend.core.tds.legend_api.frames.legend_api_applied_function_tds_frame import LegendApiAppliedFunction
21
+ from pylegend.core.tds.legacy_api.frames.legacy_api_applied_function_tds_frame import LegacyApiAppliedFunction
22
22
  from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
23
23
  from pylegend.core.sql.metamodel import (
24
24
  QuerySpecification,
@@ -27,21 +27,25 @@ from pylegend.core.sql.metamodel import (
27
27
  )
28
28
  from pylegend.core.tds.tds_column import TdsColumn
29
29
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
30
- from pylegend.core.tds.legend_api.frames.legend_api_base_tds_frame import LegendApiBaseTdsFrame
30
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
31
+ from pylegend.core.tds.legacy_api.frames.legacy_api_base_tds_frame import LegacyApiBaseTdsFrame
31
32
  from pylegend.core.language import (
32
- TdsRow,
33
+ LegacyApiTdsRow,
33
34
  PyLegendBoolean,
34
35
  PyLegendBooleanLiteralExpression,
36
+ PyLegendPrimitive,
37
+ convert_literal_to_literal_expression,
35
38
  )
39
+ from pylegend.core.language.shared.helpers import generate_pure_lambda
36
40
 
37
41
  __all__: PyLegendSequence[str] = [
38
- "FilterFunction"
42
+ "LegacyApiFilterFunction"
39
43
  ]
40
44
 
41
45
 
42
- class FilterFunction(LegendApiAppliedFunction):
43
- __base_frame: LegendApiBaseTdsFrame
44
- __filter_function: PyLegendCallable[[TdsRow], PyLegendUnion[bool, PyLegendBoolean]]
46
+ class LegacyApiFilterFunction(LegacyApiAppliedFunction):
47
+ __base_frame: LegacyApiBaseTdsFrame
48
+ __filter_function: PyLegendCallable[[LegacyApiTdsRow], PyLegendUnion[bool, PyLegendBoolean]]
45
49
 
46
50
  @classmethod
47
51
  def name(cls) -> str:
@@ -49,8 +53,8 @@ class FilterFunction(LegendApiAppliedFunction):
49
53
 
50
54
  def __init__(
51
55
  self,
52
- base_frame: LegendApiBaseTdsFrame,
53
- filter_function: PyLegendCallable[[TdsRow], PyLegendUnion[bool, PyLegendBoolean]]
56
+ base_frame: LegacyApiBaseTdsFrame,
57
+ filter_function: PyLegendCallable[[LegacyApiTdsRow], PyLegendUnion[bool, PyLegendBoolean]]
54
58
  ) -> None:
55
59
  self.__base_frame = base_frame
56
60
  self.__filter_function = filter_function
@@ -64,7 +68,7 @@ class FilterFunction(LegendApiAppliedFunction):
64
68
  copy_query(base_query)
65
69
  )
66
70
 
67
- tds_row = TdsRow.from_tds_frame("frame", self.__base_frame)
71
+ tds_row = LegacyApiTdsRow.from_tds_frame("frame", self.__base_frame)
68
72
  filter_expr = self.__filter_function(tds_row)
69
73
  if isinstance(filter_expr, bool):
70
74
  filter_expr = PyLegendBoolean(PyLegendBooleanLiteralExpression(filter_expr))
@@ -80,17 +84,25 @@ class FilterFunction(LegendApiAppliedFunction):
80
84
 
81
85
  return new_query
82
86
 
83
- def base_frame(self) -> LegendApiBaseTdsFrame:
87
+ def to_pure(self, config: FrameToPureConfig) -> str:
88
+ tds_row = LegacyApiTdsRow.from_tds_frame("r", self.__base_frame)
89
+ filter_expr = self.__filter_function(tds_row)
90
+ filter_expr_string = (filter_expr.to_pure_expression(config) if isinstance(filter_expr, PyLegendPrimitive) else
91
+ convert_literal_to_literal_expression(filter_expr).to_pure_expression(config))
92
+ return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}" +
93
+ f"->filter({generate_pure_lambda('r', filter_expr_string)})")
94
+
95
+ def base_frame(self) -> LegacyApiBaseTdsFrame:
84
96
  return self.__base_frame
85
97
 
86
- def tds_frame_parameters(self) -> PyLegendList["LegendApiBaseTdsFrame"]:
98
+ def tds_frame_parameters(self) -> PyLegendList["LegacyApiBaseTdsFrame"]:
87
99
  return []
88
100
 
89
101
  def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
90
102
  return [c.copy() for c in self.__base_frame.columns()]
91
103
 
92
104
  def validate(self) -> bool:
93
- tds_row = TdsRow.from_tds_frame("frame", self.__base_frame)
105
+ tds_row = LegacyApiTdsRow.from_tds_frame("frame", self.__base_frame)
94
106
 
95
107
  copy = self.__filter_function # For MyPy
96
108
  if not isinstance(copy, type(lambda x: 0)) or (copy.__code__.co_argcount != 1):