-
Notifications
You must be signed in to change notification settings - Fork 116
feat(frontend): Support for UDF Ui Parameter #4268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f206864
299dc0f
5b29ad8
6258a99
3550ccd
7a210a7
4a56861
16f89ac
5aa3f4e
1d82c9a
b36628f
dd46609
2c8167b
06d7f5a
ac2b265
c8239c2
edd3869
0c9f0cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,12 +16,76 @@ | |
| # under the License. | ||
|
|
||
| from abc import abstractmethod | ||
| from typing import Iterator, Optional, Union | ||
|
|
||
| from pyamber import * | ||
| from typing import Any, Dict, Iterator, Optional, Union | ||
|
|
||
| import functools | ||
|
|
||
| class UDFOperatorV2(TupleOperatorV2): | ||
| from pyamber import * | ||
| from core.models.schema.attribute_type import AttributeType, FROM_STRING_PARSER_MAPPING | ||
|
|
||
| class _UiParameterSupport: | ||
| _ui_parameter_injected_values: Dict[str, Any] = {} | ||
| _ui_parameter_name_types: Dict[str, AttributeType] = {} | ||
|
|
||
| # Reserved hook name. Backend injector will generate this in the user's class. | ||
| def _texera_injected_ui_parameters(self) -> Dict[str, Any]: | ||
| return {} | ||
|
|
||
| def _texera_apply_injected_ui_parameters(self) -> None: | ||
| values = self._texera_injected_ui_parameters() | ||
| # Write to base class storage (not cls) because UiParameter reads from _UiParameterSupport directly | ||
| _UiParameterSupport._ui_parameter_injected_values = dict(values or {}) | ||
| _UiParameterSupport._ui_parameter_name_types = {} | ||
|
Comment on lines
+27
to
+38
|
||
|
|
||
| def __init_subclass__(cls, **kwargs): | ||
| super().__init_subclass__(**kwargs) | ||
|
|
||
| # Wrap only methods defined on this class (not inherited ones) | ||
| original_open = getattr(cls, "open", None) | ||
| if original_open is None: | ||
| return | ||
|
|
||
| # Avoid double wrapping | ||
| if getattr(original_open, "__texera_ui_params_wrapped__", False): | ||
| return | ||
|
|
||
| @functools.wraps(original_open) | ||
| def wrapped_open(self, *args, **kwargs): | ||
| self._texera_apply_injected_ui_parameters() | ||
| return original_open(self, *args, **kwargs) | ||
|
|
||
| setattr(wrapped_open, "__texera_ui_params_wrapped__", True) | ||
| cls.open = wrapped_open | ||
|
|
||
| class UiParameter: | ||
| def __init__(self, name: str, type: AttributeType): | ||
| if not isinstance(type, AttributeType): | ||
| raise TypeError( | ||
| f"UiParameter.type must be an AttributeType, got {type!r}." | ||
| ) | ||
|
|
||
| existing_type = _UiParameterSupport._ui_parameter_name_types.get(name) | ||
| if existing_type is not None and existing_type != type: | ||
| raise ValueError( | ||
| f"Duplicate UiParameter name '{name}' with conflicting types: " | ||
| f"{existing_type.name} vs {type.name}." | ||
| ) | ||
|
|
||
| _UiParameterSupport._ui_parameter_name_types[name] = type | ||
| raw_value = _UiParameterSupport._ui_parameter_injected_values.get(name) | ||
| self.name = name | ||
| self.type = type | ||
| self.value = _UiParameterSupport._parse(raw_value, type) | ||
|
Comment on lines
+61
to
+78
|
||
|
|
||
| @staticmethod | ||
| def _parse(value: Any, attr_type: AttributeType) -> Any: | ||
| if value is None: | ||
| return None | ||
|
|
||
| py_type = FROM_STRING_PARSER_MAPPING.get(attr_type) | ||
| return py_type(value) | ||
|
Comment on lines
+81
to
+86
|
||
|
|
||
|
Comment on lines
+26
to
+87
|
||
| class UDFOperatorV2(_UiParameterSupport, TupleOperatorV2): | ||
| """ | ||
| Base class for tuple-oriented user-defined operators. A concrete implementation must | ||
| be provided upon using. | ||
|
|
@@ -65,7 +129,7 @@ def close(self) -> None: | |
| pass | ||
|
|
||
|
|
||
| class UDFSourceOperator(SourceOperator): | ||
| class UDFSourceOperator(_UiParameterSupport, SourceOperator): | ||
| def open(self) -> None: | ||
| """ | ||
| Open a context of the operator. Usually can be used for loading/initiating some | ||
|
|
@@ -90,7 +154,7 @@ def close(self) -> None: | |
| pass | ||
|
|
||
|
|
||
| class UDFTableOperator(TableOperator): | ||
| class UDFTableOperator(_UiParameterSupport, TableOperator): | ||
| """ | ||
| Base class for table-oriented user-defined operators. A concrete implementation must | ||
| be provided upon using. | ||
|
|
@@ -123,7 +187,7 @@ def close(self) -> None: | |
| pass | ||
|
|
||
|
|
||
| class UDFBatchOperator(BatchOperator): | ||
| class UDFBatchOperator(_UiParameterSupport, BatchOperator): | ||
| """ | ||
| Base class for batch-oriented user-defined operators. A concrete implementation must | ||
| be provided upon using. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,10 @@ | |
|
|
||
| import com.fasterxml.jackson.annotation.JsonCreator; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import org.apache.texera.amber.pybuilder.EncodableStringAnnotation; | ||
| import org.apache.texera.amber.pybuilder.PyStringTypes; | ||
| import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableStringFactory$; | ||
|
Comment on lines
+25
to
+26
|
||
|
|
||
|
|
||
| import javax.validation.constraints.NotBlank; | ||
| import javax.validation.constraints.NotNull; | ||
|
|
@@ -49,6 +53,7 @@ public Attribute( | |
|
|
||
| @JsonProperty(value = "attributeName", required = true) | ||
| @NotBlank(message = "Attribute name is required") | ||
| @EncodableStringAnnotation | ||
| public String getName() { | ||
| return attributeName; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The boolean parser in
FROM_STRING_PARSER_MAPPINGforAttributeType.BOOLincludes"True"in the truthy set (line 85), but this is redundant since the lambda already callsstr(v).strip().lower()— after.lower(), no string can equal"True"(capital T). The"True"entry in the tuple is dead code and the set effectively only checks for"true","1", and"yes".