Skip to content
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f206864
v1
carloea2 Feb 26, 2026
299dc0f
Merge branch 'apache:main' into feat/ui-parameter
carloea2 Feb 27, 2026
5b29ad8
v2
carloea2 Feb 28, 2026
6258a99
Merge branch 'feat/ui-parameter' of https://github.com/carloea2/texer…
carloea2 Feb 28, 2026
3550ccd
v2
carloea2 Mar 2, 2026
7a210a7
py2udf
carloea2 Mar 4, 2026
4a56861
v2.1
carloea2 Mar 7, 2026
16f89ac
Merge branch 'feat/ui-parameter' of https://github.com/carloea2/texer…
carloea2 Mar 7, 2026
5aa3f4e
vnt
carloea2 Mar 7, 2026
1d82c9a
v2
carloea2 Mar 7, 2026
b36628f
v2.1
carloea2 Mar 7, 2026
dd46609
Update ui-udf-parameters.component.scss
carloea2 Mar 7, 2026
2c8167b
v2.1
carloea2 Mar 7, 2026
06d7f5a
v3
carloea2 Mar 7, 2026
ac2b265
v3
carloea2 Mar 7, 2026
c8239c2
Merge branch 'main' into feat/ui-parameter
carloea2 Mar 7, 2026
edd3869
Merge branch 'main' into feat/ui-parameter
carloea2 Mar 7, 2026
0c9f0cd
Merge branch 'main' into feat/ui-parameter
chenlica Mar 10, 2026
6ff1e86
v3CopilotVs
carloea2 Mar 16, 2026
ce96e19
Merge branch 'main' into feat/ui-parameter
carloea2 Mar 16, 2026
52845c3
Update PythonUdfUiParameterInjectorSpec.scala
carloea2 Mar 16, 2026
b863937
Merge branch 'feat/ui-parameter' of https://github.com/carloea2/texer…
carloea2 Mar 16, 2026
06ba53b
v4
carloea2 Mar 16, 2026
7d317a0
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 2, 2026
1aee56e
v10
carloea2 Apr 2, 2026
ed6e53c
Merge branch 'feat/ui-parameter' of https://github.com/carloea2/texer…
carloea2 Apr 2, 2026
9425340
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 2, 2026
16a9687
Merge branch 'main' into feat/ui-parameter
chenlica Apr 2, 2026
00c2deb
Handling empty values with defaults
carloea2 Apr 2, 2026
f8f8915
v21
carloea2 Apr 2, 2026
4634adc
Update ui-udf-parameters-sync.service.ts
carloea2 Apr 2, 2026
61e5bb9
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 2, 2026
08b1969
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
ff3a31a
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
775f005
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
6c45777
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
610c808
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
31548b3
Update coeditor-user-icon.component.css
carloea2 Apr 12, 2026
cd2f58a
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 12, 2026
cbab5be
formatting
carloea2 Apr 12, 2026
5ce49d2
Merge remote-tracking branch 'origin/feat/ui-parameter' into feat/ui-…
carloea2 Apr 12, 2026
0488dba
formatting
carloea2 Apr 12, 2026
d9ba912
??
carloea2 Apr 12, 2026
9d506ad
Merge branch 'main' into feat/ui-parameter
aglinxinyuan Apr 15, 2026
9f2bdbd
Remove FileSelectionComponent from app.module.ts
aglinxinyuan Apr 15, 2026
672ec4b
Update app.module.ts
aglinxinyuan Apr 15, 2026
f9eaf4f
fix fmt
aglinxinyuan Apr 15, 2026
4d0107c
v9
carloea2 Apr 15, 2026
670ec8c
Merge branch 'main' into feat/ui-parameter
chenlica Apr 16, 2026
086d781
Merge branch 'main' into feat/ui-parameter
kunwp1 Apr 17, 2026
4b2c25d
Merge branch 'main' into feat/ui-parameter
kunwp1 Apr 17, 2026
50e8aa2
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 18, 2026
606d241
v13
carloea2 Apr 18, 2026
496de38
Merge branch 'main' into feat/ui-parameter
aglinxinyuan Apr 18, 2026
7f7b5d0
Merge branch 'main' into feat/ui-parameter
carloea2 Apr 23, 2026
e65ea79
Merge branch 'main' into feat/ui-parameter
chenlica Apr 23, 2026
d010289
Merge branch 'main' into feat/ui-parameter
chenlica Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion amber/src/main/python/core/models/schema/attribute_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from pyarrow import lib
from core.models.type.large_binary import largebinary


class AttributeType(Enum):
"""
Types supported by PyTexera & PyAmber.
Expand Down Expand Up @@ -78,6 +77,17 @@ class AttributeType(Enum):
}


FROM_STRING_PARSER_MAPPING = {
AttributeType.STRING: str,
AttributeType.INT: int,
AttributeType.LONG: int,
AttributeType.DOUBLE: float,
AttributeType.BOOL: lambda v: str(v).strip().lower() in ("True", "true", "1", "yes"),
Comment thread
carloea2 marked this conversation as resolved.
Outdated
AttributeType.BINARY: lambda v: v if isinstance(v, bytes) else str(v).encode(),
AttributeType.TIMESTAMP: lambda v: datetime.datetime.fromisoformat(v),
AttributeType.LARGE_BINARY: largebinary,
}
Comment thread
carloea2 marked this conversation as resolved.

# Only single-directional mapping.
TO_PYOBJECT_MAPPING = {
AttributeType.STRING: str,
Expand Down
2 changes: 2 additions & 0 deletions amber/src/main/python/pyamber/__init__.py
Comment thread
carloea2 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
SourceOperator,
TupleOperatorV2,
State,
AttributeType,
)

__all__ = [
Expand All @@ -41,4 +42,5 @@
"TupleOperatorV2",
"SourceOperator",
"State",
"AttributeType",
]
2 changes: 2 additions & 0 deletions amber/src/main/python/pytexera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
UDFSourceOperator,
)
from core.models.type.large_binary import largebinary
from core.models.schema.attribute_type import *

__all__ = [
"State",
Expand All @@ -53,4 +54,5 @@
"Iterator",
"Optional",
"Union",
"AttributeType",
]
78 changes: 71 additions & 7 deletions amber/src/main/python/pytexera/udf/udf_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 thread
carloea2 marked this conversation as resolved.
Outdated

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 thread
carloea2 marked this conversation as resolved.
Outdated

@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 thread
carloea2 marked this conversation as resolved.
Outdated

Comment thread
carloea2 marked this conversation as resolved.
class UDFOperatorV2(_UiParameterSupport, TupleOperatorV2):
"""
Base class for tuple-oriented user-defined operators. A concrete implementation must
be provided upon using.
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 thread
carloea2 marked this conversation as resolved.
Outdated
Comment thread
carloea2 marked this conversation as resolved.
Outdated


import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -49,6 +53,7 @@ public Attribute(

@JsonProperty(value = "attributeName", required = true)
@NotBlank(message = "Attribute name is required")
@EncodableStringAnnotation
public String getName() {
return attributeName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class DualInputPortsPythonUDFOpDescV2 extends LogicalOp {
)
var outputColumns: List[Attribute] = List()

@JsonProperty
@JsonSchemaTitle("Parameters")
@JsonPropertyDescription("Parameters inferred from self.UiParameter(...) in Python script")
var uiParameters: List[UiUDFParameter] = List()

override def getPhysicalOp(
workflowId: WorkflowIdentity,
executionId: ExecutionIdentity
Expand All @@ -88,7 +93,7 @@ class DualInputPortsPythonUDFOpDescV2 extends LogicalOp {
workflowId,
executionId,
operatorIdentifier,
OpExecWithCode(code, "python")
OpExecWithCode(PythonUdfUiParameterInjector.inject(code, uiParameters), "python")
)
.withParallelizable(true)
.withSuggestedWorkerNum(workers)
Expand All @@ -98,7 +103,7 @@ class DualInputPortsPythonUDFOpDescV2 extends LogicalOp {
workflowId,
executionId,
operatorIdentifier,
OpExecWithCode(code, "python")
OpExecWithCode(PythonUdfUiParameterInjector.inject(code, uiParameters), "python")
)
.withParallelizable(false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ class PythonUDFOpDescV2 extends LogicalOp {
)
var outputColumns: List[Attribute] = List()

@JsonProperty
@JsonSchemaTitle("Parameters")
@JsonPropertyDescription("Parameters inferred from self.UiParameter(...) in Python script")
var uiParameters: List[UiUDFParameter] = List()

override def getPhysicalOp(
workflowId: WorkflowIdentity,
executionId: ExecutionIdentity
Expand Down Expand Up @@ -118,7 +123,7 @@ class PythonUDFOpDescV2 extends LogicalOp {
workflowId,
executionId,
operatorIdentifier,
OpExecWithCode(code, "python")
OpExecWithCode(PythonUdfUiParameterInjector.inject(code, uiParameters), "python")
)
.withParallelizable(true)
.withSuggestedWorkerNum(workers)
Expand All @@ -128,7 +133,7 @@ class PythonUDFOpDescV2 extends LogicalOp {
workflowId,
executionId,
operatorIdentifier,
OpExecWithCode(code, "python")
OpExecWithCode(PythonUdfUiParameterInjector.inject(code, uiParameters), "python")
)
.withParallelizable(false)
}
Expand Down
Loading
Loading