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
@@ -18,15 +18,15 @@ from pylegend._typing import (
18
18
  PyLegendUnion,
19
19
  TYPE_CHECKING,
20
20
  )
21
- from pylegend.core.language.primitives.number import PyLegendNumber
22
- from pylegend.core.language.expression import PyLegendExpressionFloatReturn, PyLegendExpression
23
- from pylegend.core.language.literal_expressions import PyLegendFloatLiteralExpression
21
+ from pylegend.core.language.shared.primitives.number import PyLegendNumber
22
+ from pylegend.core.language.shared.expression import PyLegendExpressionFloatReturn
23
+ from pylegend.core.language.shared.literal_expressions import PyLegendFloatLiteralExpression
24
24
  from pylegend.core.sql.metamodel import (
25
25
  Expression,
26
26
  QuerySpecification
27
27
  )
28
28
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
29
- from pylegend.core.language.operations.float_operation_expressions import (
29
+ from pylegend.core.language.shared.operations.float_operation_expressions import (
30
30
  PyLegendFloatAbsoluteExpression,
31
31
  PyLegendFloatAddExpression,
32
32
  PyLegendFloatNegativeExpression,
@@ -34,7 +34,7 @@ from pylegend.core.language.operations.float_operation_expressions import (
34
34
  PyLegendFloatMultiplyExpression,
35
35
  )
36
36
  if TYPE_CHECKING:
37
- from pylegend.core.language.primitives import PyLegendInteger
37
+ from pylegend.core.language.shared.primitives import PyLegendInteger
38
38
 
39
39
  __all__: PyLegendSequence[str] = [
40
40
  "PyLegendFloat"
@@ -141,7 +141,7 @@ class PyLegendFloat(PyLegendNumber):
141
141
  ) -> Expression:
142
142
  return super().to_sql_expression(frame_name_to_base_query_map, config)
143
143
 
144
- def value(self) -> PyLegendExpression:
144
+ def value(self) -> PyLegendExpressionFloatReturn:
145
145
  return self.__value_copy
146
146
 
147
147
  @staticmethod
@@ -18,15 +18,15 @@ from pylegend._typing import (
18
18
  PyLegendUnion,
19
19
  TYPE_CHECKING,
20
20
  )
21
- from pylegend.core.language.primitives.number import PyLegendNumber
22
- from pylegend.core.language.expression import PyLegendExpressionIntegerReturn, PyLegendExpression
23
- from pylegend.core.language.literal_expressions import PyLegendIntegerLiteralExpression
21
+ from pylegend.core.language.shared.primitives.number import PyLegendNumber
22
+ from pylegend.core.language.shared.expression import PyLegendExpressionIntegerReturn
23
+ from pylegend.core.language.shared.literal_expressions import PyLegendIntegerLiteralExpression
24
24
  from pylegend.core.sql.metamodel import (
25
25
  Expression,
26
26
  QuerySpecification
27
27
  )
28
28
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
29
- from pylegend.core.language.operations.integer_operation_expressions import (
29
+ from pylegend.core.language.shared.operations.integer_operation_expressions import (
30
30
  PyLegendIntegerAddExpression,
31
31
  PyLegendIntegerAbsoluteExpression,
32
32
  PyLegendIntegerNegativeExpression,
@@ -35,7 +35,7 @@ from pylegend.core.language.operations.integer_operation_expressions import (
35
35
  PyLegendIntegerModuloExpression,
36
36
  )
37
37
  if TYPE_CHECKING:
38
- from pylegend.core.language.primitives import PyLegendFloat
38
+ from pylegend.core.language.shared.primitives import PyLegendFloat
39
39
 
40
40
  __all__: PyLegendSequence[str] = [
41
41
  "PyLegendInteger"
@@ -59,7 +59,7 @@ class PyLegendInteger(PyLegendNumber):
59
59
  ) -> Expression:
60
60
  return super().to_sql_expression(frame_name_to_base_query_map, config)
61
61
 
62
- def value(self) -> PyLegendExpression:
62
+ def value(self) -> PyLegendExpressionIntegerReturn:
63
63
  return self.__value_copy
64
64
 
65
65
  def __add__(
@@ -19,17 +19,16 @@ from pylegend._typing import (
19
19
  PyLegendOptional,
20
20
  TYPE_CHECKING,
21
21
  )
22
- from pylegend.core.language.primitives.primitive import PyLegendPrimitive
23
- from pylegend.core.language.primitives.boolean import PyLegendBoolean
24
- from pylegend.core.language.expression import (
25
- PyLegendExpression,
22
+ from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitive
23
+ from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
24
+ from pylegend.core.language.shared.expression import (
26
25
  PyLegendExpressionNumberReturn,
27
26
  )
28
- from pylegend.core.language.literal_expressions import (
27
+ from pylegend.core.language.shared.literal_expressions import (
29
28
  PyLegendIntegerLiteralExpression,
30
29
  PyLegendFloatLiteralExpression,
31
30
  )
32
- from pylegend.core.language.operations.number_operation_expressions import (
31
+ from pylegend.core.language.shared.operations.number_operation_expressions import (
33
32
  PyLegendNumberAddExpression,
34
33
  PyLegendNumberMultiplyExpression,
35
34
  PyLegendNumberDivideExpression,
@@ -63,9 +62,10 @@ from pylegend.core.sql.metamodel import (
63
62
  QuerySpecification
64
63
  )
65
64
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
65
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
66
66
  if TYPE_CHECKING:
67
- from pylegend.core.language.primitives.integer import PyLegendInteger
68
- from pylegend.core.language.primitives.float import PyLegendFloat
67
+ from pylegend.core.language.shared.primitives.integer import PyLegendInteger
68
+ from pylegend.core.language.shared.primitives.float import PyLegendFloat
69
69
 
70
70
 
71
71
  __all__: PyLegendSequence[str] = [
@@ -89,7 +89,10 @@ class PyLegendNumber(PyLegendPrimitive):
89
89
  ) -> Expression:
90
90
  return self.__value.to_sql_expression(frame_name_to_base_query_map, config)
91
91
 
92
- def value(self) -> PyLegendExpression:
92
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
93
+ return self.__value.to_pure_expression(config)
94
+
95
+ def value(self) -> PyLegendExpressionNumberReturn:
93
96
  return self.__value
94
97
 
95
98
  def __add__(
@@ -214,14 +217,14 @@ class PyLegendNumber(PyLegendPrimitive):
214
217
  return PyLegendNumber(PyLegendNumberPowerExpression(other_op, self.__value))
215
218
 
216
219
  def ceil(self) -> "PyLegendInteger":
217
- from pylegend.core.language.primitives.integer import PyLegendInteger
220
+ from pylegend.core.language.shared.primitives.integer import PyLegendInteger
218
221
  return PyLegendInteger(PyLegendNumberCeilExpression(self.__value))
219
222
 
220
223
  def __ceil__(self) -> "PyLegendInteger":
221
224
  return self.ceil()
222
225
 
223
226
  def floor(self) -> "PyLegendInteger":
224
- from pylegend.core.language.primitives.integer import PyLegendInteger
227
+ from pylegend.core.language.shared.primitives.integer import PyLegendInteger
225
228
  return PyLegendInteger(PyLegendNumberFloorExpression(self.__value))
226
229
 
227
230
  def __floor__(self) -> "PyLegendInteger":
@@ -305,8 +308,8 @@ class PyLegendNumber(PyLegendPrimitive):
305
308
  param: PyLegendUnion[int, float, "PyLegendInteger", "PyLegendFloat", "PyLegendNumber"],
306
309
  desc: str
307
310
  ) -> None:
308
- from pylegend.core.language.primitives.integer import PyLegendInteger
309
- from pylegend.core.language.primitives.float import PyLegendFloat
311
+ from pylegend.core.language.shared.primitives.integer import PyLegendInteger
312
+ from pylegend.core.language.shared.primitives.float import PyLegendFloat
310
313
  if not isinstance(param, (int, float, PyLegendInteger, PyLegendFloat, PyLegendNumber)):
311
314
  raise TypeError(desc + " should be a int/float or a int/float/number expression"
312
315
  " (PyLegendInteger/PyLegendFloat/PyLegendNumber)."
@@ -25,14 +25,16 @@ from pylegend.core.sql.metamodel import (
25
25
  Expression,
26
26
  QuerySpecification
27
27
  )
28
- from pylegend.core.language.expression import PyLegendExpression
29
- from pylegend.core.language.literal_expressions import convert_literal_to_literal_expression
30
- from pylegend.core.language.operations.primitive_operation_expressions import (
28
+ from pylegend.core.language.shared.expression import PyLegendExpression
29
+ from pylegend.core.language.shared.literal_expressions import convert_literal_to_literal_expression
30
+ from pylegend.core.language.shared.operations.primitive_operation_expressions import (
31
31
  PyLegendPrimitiveEqualsExpression,
32
+ PyLegendPrimitiveNotEqualsExpression,
32
33
  )
33
34
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
35
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
34
36
  if TYPE_CHECKING:
35
- from pylegend.core.language.primitives.boolean import PyLegendBoolean
37
+ from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
36
38
 
37
39
  __all__: PyLegendSequence[str] = [
38
40
  "PyLegendPrimitive",
@@ -50,6 +52,10 @@ class PyLegendPrimitive(metaclass=ABCMeta):
50
52
  ) -> Expression:
51
53
  pass
52
54
 
55
+ @abstractmethod
56
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
57
+ pass
58
+
53
59
  def __eq__( # type: ignore
54
60
  self,
55
61
  other: "PyLegendUnion[int, float, bool, str, date, datetime, PyLegendPrimitive]"
@@ -61,9 +67,23 @@ class PyLegendPrimitive(metaclass=ABCMeta):
61
67
  else:
62
68
  other_op = other.value()
63
69
 
64
- from pylegend.core.language.primitives.boolean import PyLegendBoolean
70
+ from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
65
71
  return PyLegendBoolean(PyLegendPrimitiveEqualsExpression(self.value(), other_op))
66
72
 
73
+ def __ne__( # type: ignore
74
+ self,
75
+ other: "PyLegendUnion[int, float, bool, str, date, datetime, PyLegendPrimitive]"
76
+ ) -> "PyLegendBoolean":
77
+ PyLegendPrimitive.__validate_param_to_be_primitive(other, "Not Equals (!=) parameter")
78
+
79
+ if isinstance(other, (int, float, bool, str, date, datetime)):
80
+ other_op = convert_literal_to_literal_expression(other)
81
+ else:
82
+ other_op = other.value()
83
+
84
+ from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
85
+ return PyLegendBoolean(PyLegendPrimitiveNotEqualsExpression(self.value(), other_op))
86
+
67
87
  @staticmethod
68
88
  def __validate_param_to_be_primitive(
69
89
  param: "PyLegendUnion[int, float, bool, str, date, datetime, PyLegendPrimitive]",
@@ -18,12 +18,11 @@ from pylegend._typing import (
18
18
  PyLegendDict,
19
19
  PyLegendUnion,
20
20
  )
21
- from pylegend.core.language.primitives.date import PyLegendDate
22
- from pylegend.core.language.expression import (
23
- PyLegendExpression,
21
+ from pylegend.core.language.shared.primitives.date import PyLegendDate
22
+ from pylegend.core.language.shared.expression import (
24
23
  PyLegendExpressionStrictDateReturn,
25
24
  )
26
- from pylegend.core.language.literal_expressions import (
25
+ from pylegend.core.language.shared.literal_expressions import (
27
26
  PyLegendStrictDateLiteralExpression,
28
27
  )
29
28
  from pylegend.core.sql.metamodel import (
@@ -55,7 +54,7 @@ class PyLegendStrictDate(PyLegendDate):
55
54
  ) -> Expression:
56
55
  return self.__value.to_sql_expression(frame_name_to_base_query_map, config)
57
56
 
58
- def value(self) -> PyLegendExpression:
57
+ def value(self) -> PyLegendExpressionStrictDateReturn:
59
58
  return self.__value
60
59
 
61
60
  @staticmethod
@@ -17,20 +17,23 @@ from pylegend._typing import (
17
17
  PyLegendDict,
18
18
  PyLegendUnion,
19
19
  )
20
- from pylegend.core.language.primitives.primitive import PyLegendPrimitive
21
- from pylegend.core.language.primitives.integer import PyLegendInteger
22
- from pylegend.core.language.primitives.float import PyLegendFloat
23
- from pylegend.core.language.primitives.boolean import PyLegendBoolean
24
- from pylegend.core.language.expression import PyLegendExpressionStringReturn, PyLegendExpression
25
- from pylegend.core.language.literal_expressions import PyLegendStringLiteralExpression
20
+ from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitive
21
+ from pylegend.core.language.shared.primitives.integer import PyLegendInteger
22
+ from pylegend.core.language.shared.primitives.float import PyLegendFloat
23
+ from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
24
+ from pylegend.core.language.shared.expression import PyLegendExpressionStringReturn
25
+ from pylegend.core.language.shared.literal_expressions import PyLegendStringLiteralExpression
26
26
  from pylegend.core.sql.metamodel import (
27
27
  Expression,
28
28
  QuerySpecification
29
29
  )
30
30
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
31
- from pylegend.core.language.operations.string_operation_expressions import (
31
+ from pylegend.core.tds.tds_frame import FrameToPureConfig
32
+ from pylegend.core.language.shared.operations.string_operation_expressions import (
32
33
  PyLegendStringLengthExpression,
33
- PyLegendStringLikeExpression,
34
+ PyLegendStringStartsWithExpression,
35
+ PyLegendStringEndsWithExpression,
36
+ PyLegendStringContainsExpression,
34
37
  PyLegendStringUpperExpression,
35
38
  PyLegendStringLowerExpression,
36
39
  PyLegendStringLTrimExpression,
@@ -69,23 +72,20 @@ class PyLegendString(PyLegendPrimitive):
69
72
 
70
73
  def startswith(self, prefix: str) -> PyLegendBoolean:
71
74
  PyLegendString.__validate_param_to_be_str(prefix, "startswith prefix parameter")
72
- escaped_prefix = PyLegendString.__escape_like_param(prefix)
73
75
  return PyLegendBoolean(
74
- PyLegendStringLikeExpression(self.__value, PyLegendStringLiteralExpression(escaped_prefix + '%'))
76
+ PyLegendStringStartsWithExpression(self.__value, PyLegendStringLiteralExpression(prefix))
75
77
  )
76
78
 
77
79
  def endswith(self, suffix: str) -> PyLegendBoolean:
78
80
  PyLegendString.__validate_param_to_be_str(suffix, "endswith suffix parameter")
79
- escaped_suffix = PyLegendString.__escape_like_param(suffix)
80
81
  return PyLegendBoolean(
81
- PyLegendStringLikeExpression(self.__value, PyLegendStringLiteralExpression("%" + escaped_suffix))
82
+ PyLegendStringEndsWithExpression(self.__value, PyLegendStringLiteralExpression(suffix))
82
83
  )
83
84
 
84
85
  def contains(self, other: str) -> PyLegendBoolean:
85
86
  PyLegendString.__validate_param_to_be_str(other, "contains/in other parameter")
86
- escaped_other = PyLegendString.__escape_like_param(other)
87
87
  return PyLegendBoolean(
88
- PyLegendStringLikeExpression(self.__value, PyLegendStringLiteralExpression("%" + escaped_other + "%"))
88
+ PyLegendStringContainsExpression(self.__value, PyLegendStringLiteralExpression(other))
89
89
  )
90
90
 
91
91
  def upper(self) -> "PyLegendString":
@@ -157,12 +157,11 @@ class PyLegendString(PyLegendPrimitive):
157
157
  ) -> Expression:
158
158
  return self.__value.to_sql_expression(frame_name_to_base_query_map, config)
159
159
 
160
- def value(self) -> PyLegendExpression:
161
- return self.__value
160
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
161
+ return self.__value.to_pure_expression(config)
162
162
 
163
- @staticmethod
164
- def __escape_like_param(param: str) -> str:
165
- return param.replace("_", "\\_").replace("%", "\\%")
163
+ def value(self) -> PyLegendExpressionStringReturn:
164
+ return self.__value
166
165
 
167
166
  @staticmethod
168
167
  def __validate_param_to_be_str_or_str_expr(param: PyLegendUnion[str, "PyLegendString"], desc: str) -> None:
@@ -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.
@@ -12,10 +12,21 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from abc import ABCMeta
15
16
  from pylegend._typing import (
16
17
  PyLegendSequence,
18
+ PyLegendDict,
19
+ )
20
+ from pylegend.core.sql.metamodel import (
21
+ QuerySpecification,
22
+ Expression,
23
+ SingleColumn,
24
+ )
25
+ from pylegend.core.tds.tds_frame import (
26
+ PyLegendTdsFrame,
27
+ FrameToPureConfig,
28
+ FrameToSqlConfig,
17
29
  )
18
- from pylegend.core.tds.tds_frame import PyLegendTdsFrame
19
30
  from pylegend.core.tds.tds_column import TdsColumn, PrimitiveTdsColumn
20
31
  from pylegend.core.language import (
21
32
  PyLegendColumnExpression,
@@ -39,11 +50,11 @@ from pylegend.core.language import (
39
50
  )
40
51
 
41
52
  __all__: PyLegendSequence[str] = [
42
- "TdsRow",
53
+ "AbstractTdsRow",
43
54
  ]
44
55
 
45
56
 
46
- class TdsRow:
57
+ class AbstractTdsRow(metaclass=ABCMeta):
47
58
  __frame_name: str
48
59
  __columns: PyLegendSequence[TdsColumn]
49
60
 
@@ -51,10 +62,6 @@ class TdsRow:
51
62
  self.__frame_name = frame_name
52
63
  self.__columns = frame.columns()
53
64
 
54
- @staticmethod
55
- def from_tds_frame(frame_name: str, frame: PyLegendTdsFrame) -> "TdsRow":
56
- return TdsRow(frame_name=frame_name, frame=frame)
57
-
58
65
  def get_boolean(self, column: str) -> PyLegendBoolean:
59
66
  col_expr = self.__get_col(column)
60
67
  if not isinstance(col_expr, PyLegendBooleanColumnExpression):
@@ -161,21 +168,21 @@ class TdsRow:
161
168
  if base_col.get_name() == column:
162
169
  if isinstance(base_col, PrimitiveTdsColumn):
163
170
  if base_col.get_type() == "Boolean":
164
- return PyLegendBooleanColumnExpression(self.__frame_name, column)
171
+ return PyLegendBooleanColumnExpression(self, column)
165
172
  if base_col.get_type() == "String":
166
- return PyLegendStringColumnExpression(self.__frame_name, column)
173
+ return PyLegendStringColumnExpression(self, column)
167
174
  if base_col.get_type() == "Number":
168
- return PyLegendNumberColumnExpression(self.__frame_name, column)
175
+ return PyLegendNumberColumnExpression(self, column)
169
176
  if base_col.get_type() == "Integer":
170
- return PyLegendIntegerColumnExpression(self.__frame_name, column)
177
+ return PyLegendIntegerColumnExpression(self, column)
171
178
  if base_col.get_type() == "Float":
172
- return PyLegendFloatColumnExpression(self.__frame_name, column)
179
+ return PyLegendFloatColumnExpression(self, column)
173
180
  if base_col.get_type() == "Date":
174
- return PyLegendDateColumnExpression(self.__frame_name, column)
181
+ return PyLegendDateColumnExpression(self, column)
175
182
  if base_col.get_type() == "DateTime":
176
- return PyLegendDateTimeColumnExpression(self.__frame_name, column)
183
+ return PyLegendDateTimeColumnExpression(self, column)
177
184
  if base_col.get_type() == "StrictDate":
178
- return PyLegendStrictDateColumnExpression(self.__frame_name, column)
185
+ return PyLegendStrictDateColumnExpression(self, column)
179
186
 
180
187
  raise RuntimeError(f"Column '{column}' of type {base_col.get_type()} not supported yet")
181
188
 
@@ -183,3 +190,26 @@ class TdsRow:
183
190
  f"Column - '{column}' doesn't exist in the current frame. "
184
191
  f"Current frame columns: {[x.get_name() for x in self.__columns]}"
185
192
  )
193
+
194
+ def get_frame_name(self) -> str:
195
+ return self.__frame_name
196
+
197
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
198
+ return f"${self.__frame_name}"
199
+
200
+ def column_sql_expression(
201
+ self,
202
+ column: str,
203
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
204
+ config: FrameToSqlConfig
205
+ ) -> Expression:
206
+ query = frame_name_to_base_query_map[self.__frame_name]
207
+ db_extension = config.sql_to_string_generator().get_db_extension()
208
+ filtered = [
209
+ s for s in query.select.selectItems
210
+ if (isinstance(s, SingleColumn) and
211
+ s.alias == db_extension.quote_identifier(column))
212
+ ]
213
+ if len(filtered) == 0:
214
+ raise RuntimeError("Cannot find column: " + column) # pragma: no cover
215
+ return filtered[0].expression
@@ -16,7 +16,12 @@ from pylegend._typing import (
16
16
  PyLegendSequence,
17
17
  )
18
18
  from pylegend.core.request.legend_client import LegendClient
19
- from pylegend.core.request.auth import AuthScheme, LocalhostEmptyAuthScheme
19
+ from pylegend.core.request.auth import (
20
+ AuthScheme,
21
+ LocalhostEmptyAuthScheme,
22
+ HeaderTokenAuthScheme,
23
+ CookieAuthScheme,
24
+ )
20
25
  from pylegend.core.request.response_reader import ResponseReader
21
26
 
22
27
  __all__: PyLegendSequence[str] = [
@@ -24,6 +29,8 @@ __all__: PyLegendSequence[str] = [
24
29
 
25
30
  "AuthScheme",
26
31
  "LocalhostEmptyAuthScheme",
32
+ "HeaderTokenAuthScheme",
33
+ "CookieAuthScheme",
27
34
 
28
35
  "ResponseReader"
29
36
  ]
@@ -15,17 +15,21 @@
15
15
  from abc import ABCMeta, abstractmethod
16
16
 
17
17
  from requests import PreparedRequest
18
+ from requests.cookies import create_cookie
18
19
 
19
20
  from pylegend._typing import (
20
21
  PyLegendSequence,
21
22
  PyLegendOptional,
23
+ PyLegendCallable,
24
+ PyLegendDict,
22
25
  )
23
26
  from requests.auth import AuthBase
24
27
 
25
28
  __all__: PyLegendSequence[str] = [
26
29
  "AuthScheme",
27
30
  "LocalhostEmptyAuthScheme",
28
- "BearerAuthScheme"
31
+ "HeaderTokenAuthScheme",
32
+ "CookieAuthScheme",
29
33
  ]
30
34
 
31
35
 
@@ -42,20 +46,94 @@ class LocalhostEmptyAuthScheme(AuthScheme):
42
46
  return None
43
47
 
44
48
 
45
- class BearerAuth(AuthBase):
46
- def __init__(self, headerName: str, token: str) -> None:
47
- self.headerName = headerName
48
- self.token = token
49
+ class HeaderTokenAuth(AuthBase):
50
+ __header_name: str
51
+ __token_provider: PyLegendCallable[[], str]
52
+ __query_params: PyLegendOptional[PyLegendDict[str, str]]
53
+
54
+ def __init__(
55
+ self,
56
+ header_name: str,
57
+ token_provider: PyLegendCallable[[], str],
58
+ query_params: PyLegendOptional[PyLegendDict[str, str]] = None
59
+ ) -> None:
60
+ self.__header_name = header_name
61
+ self.__token_provider = token_provider
62
+ self.__query_params = query_params
63
+
64
+ def __call__(self, r: PreparedRequest) -> PreparedRequest:
65
+ if self.__query_params:
66
+ r.prepare_url(r.url, self.__query_params)
67
+ r.headers[self.__header_name] = self.__token_provider()
68
+ return r
69
+
70
+
71
+ class HeaderTokenAuthScheme(AuthScheme):
72
+ __header_name: str
73
+ __token_provider: PyLegendCallable[[], str]
74
+ __query_params: PyLegendOptional[PyLegendDict[str, str]]
75
+
76
+ def __init__(
77
+ self,
78
+ header_name: str,
79
+ token_provider: PyLegendCallable[[], str],
80
+ query_params: PyLegendOptional[PyLegendDict[str, str]] = None
81
+ ) -> None:
82
+ self.__header_name = header_name
83
+ self.__token_provider = token_provider
84
+ self.__query_params = query_params
85
+
86
+ def get_auth_base(self) -> PyLegendOptional[AuthBase]:
87
+ return HeaderTokenAuth(self.__header_name, self.__token_provider, self.__query_params)
88
+
89
+
90
+ class CookieAuth(AuthBase):
91
+ __cookie_name: str
92
+ __cookie_provider: PyLegendCallable[[], str]
93
+ __query_params: PyLegendOptional[PyLegendDict[str, str]]
94
+
95
+ def __init__( # type: ignore
96
+ self,
97
+ cookie_name: str,
98
+ cookie_provider: PyLegendCallable[[], str],
99
+ query_params: PyLegendOptional[PyLegendDict[str, str]] = None,
100
+ **extra_cookie_params
101
+ ) -> None:
102
+ self.__cookie_name = cookie_name
103
+ self.__cookie_provider = cookie_provider
104
+ self.__query_params = query_params
105
+ self.__extra_cookie_params = extra_cookie_params
49
106
 
50
107
  def __call__(self, r: PreparedRequest) -> PreparedRequest:
51
- r.headers[self.headerName] = self.token
108
+ if self.__query_params:
109
+ r.prepare_url(r.url, self.__query_params)
110
+ r.headers.pop("Cookie", None)
111
+ new_cookie = create_cookie( # type: ignore
112
+ name=self.__cookie_name,
113
+ value=self.__cookie_provider(),
114
+ **self.__extra_cookie_params
115
+ )
116
+ r._cookies.set_cookie(new_cookie) # type: ignore
117
+ r.prepare_cookies(r._cookies) # type: ignore
52
118
  return r
53
119
 
54
120
 
55
- class BearerAuthScheme(AuthScheme):
56
- def __init__(self, headerName: str, token: str) -> None:
57
- self.headerName = headerName
58
- self.token = token
121
+ class CookieAuthScheme(AuthScheme):
122
+ __cookie_name: str
123
+ __cookie_provider: PyLegendCallable[[], str]
124
+ __query_params: PyLegendOptional[PyLegendDict[str, str]]
125
+
126
+ def __init__( # type: ignore
127
+ self,
128
+ cookie_name: str,
129
+ cookie_provider: PyLegendCallable[[], str],
130
+ query_params: PyLegendOptional[PyLegendDict[str, str]] = None,
131
+ **extra_cookie_params
132
+ ) -> None:
133
+ self.__cookie_name = cookie_name
134
+ self.__cookie_provider = cookie_provider
135
+ self.__query_params = query_params
136
+ self.__extra_cookie_params = extra_cookie_params
59
137
 
60
138
  def get_auth_base(self) -> PyLegendOptional[AuthBase]:
61
- return BearerAuth(self.headerName, self.token)
139
+ return CookieAuth(self.__cookie_name, self.__cookie_provider, self.__query_params, **self.__extra_cookie_params)
@@ -77,6 +77,38 @@ class LegendClient(ServiceClient):
77
77
  ).iter_content(chunk_size=chunk_size)
78
78
  return ResponseReader(iter_content)
79
79
 
80
+ def parse_model(
81
+ self,
82
+ model_code: str,
83
+ return_source_information: bool = False
84
+ ) -> str:
85
+ response = super()._execute_service(
86
+ method=RequestMethod.POST,
87
+ path="pure/v1/grammar/grammarToJson/model",
88
+ data=model_code,
89
+ headers={"Content-Type": "text/plain"},
90
+ query_params=[("returnSourceInformation", "true" if return_source_information else "false")]
91
+ )
92
+ return response.text
93
+
94
+ def compile_model(
95
+ self,
96
+ model_json: str
97
+ ) -> None:
98
+ super()._execute_service(
99
+ method=RequestMethod.POST,
100
+ path="pure/v1/compilation/compile",
101
+ data=model_json,
102
+ headers={"Content-Type": "application/json"}
103
+ )
104
+
105
+ def parse_and_compile_model(
106
+ self,
107
+ model_code: str
108
+ ) -> None:
109
+ parse_response = self.parse_model(model_code)
110
+ self.compile_model(parse_response)
111
+
80
112
  def __eq__(self, other: 'object') -> bool:
81
113
  if self is other:
82
114
  return True
@@ -19,6 +19,7 @@ from pylegend._typing import (
19
19
  )
20
20
  from pylegend.core.sql.metamodel import (
21
21
  Expression,
22
+ Window,
22
23
  )
23
24
 
24
25
  __all__: PyLegendSequence[str] = [
@@ -78,6 +79,7 @@ __all__: PyLegendSequence[str] = [
78
79
  "MinuteExpression",
79
80
  "SecondExpression",
80
81
  "EpochExpression",
82
+ "WindowExpression",
81
83
  ]
82
84
 
83
85
 
@@ -717,3 +719,17 @@ class EpochExpression(Expression):
717
719
  ) -> None:
718
720
  super().__init__(_type="epochExpression")
719
721
  self.value = value
722
+
723
+
724
+ class WindowExpression(Expression):
725
+ nested: "Expression"
726
+ window: "Window"
727
+
728
+ def __init__(
729
+ self,
730
+ nested: "Expression",
731
+ window: "Window",
732
+ ) -> None:
733
+ super().__init__(_type="windowExpression")
734
+ self.nested = nested
735
+ self.window = window
@@ -0,0 +1,13 @@
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.
@@ -0,0 +1,13 @@
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.