This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-7260-6daa84641094185690ec1d721f91de29635ebd93
in repository https://gitbox.apache.org/repos/asf/texera.git

commit a96bf3e70c0e9d5bf72402637633b36e99049a25
Author: Kary Zheng <[email protected]>
AuthorDate: Fri Aug 7 12:09:08 2026 -0700

    fix(visualization): define the render_error the table charts already call 
(#7260)
    
    ### What changes were proposed in this PR?
    
    `TablesPlotOpDesc` and `FigureFactoryTableOpDesc` both generate a
    `TableChartOperator` that calls `self.render_error(...)` on two
    branches, but neither generated class defines that method. Each now
    defines it, in the same shape the other visualization operators use.
    
    ### Why are the changes needed?
    
    Both branches are reachable — an empty input table, and a value column
    left with only non-positive or null values. Executing each operator's
    generated module against an empty frame raises `AttributeError:
    'TableChartOperator' object has no attribute 'render_error'` instead of
    showing the message the code was written to show. With the definition
    added, the same run yields `Tables Plot is not available. Reason is:
    input table is empty.`
    
    ### Any related issues, documentation, discussions?
    
    Closes #7244
    
    ### How was this PR tested?
    
    `WorkflowOperator/scalafmtCheckAll` and both operators' descriptor specs
    (14 tests). Each spec now asserts its generated class defines
    `render_error` and formats the operator's name. Separately, each
    generated module was run locally against an empty frame before and after
    the fix, with the `pytexera` and `plotly` imports stubbed — the before
    run reproduces the `AttributeError`, the after run returns `Tables Plot
    is not available. Reason is: input table is empty.`
    
    ### Does this PR introduce any user-facing change?
    
    Yes. Those two cases now render the intended message instead of failing
    the operator. Nothing changes on the path that produces a chart.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 5)
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
    Co-authored-by: Xuan Gu <[email protected]>
---
 .../figureFactoryTable/FigureFactoryTableOpDesc.scala         |  3 +++
 .../operator/visualization/tablesChart/TablesPlotOpDesc.scala |  3 +++
 .../figureFactoryTable/FigureFactoryTableOpDescSpec.scala     | 11 +++++++++++
 .../visualization/tablesChart/TablesPlotOpDescSpec.scala      | 11 +++++++++++
 4 files changed, 28 insertions(+)

diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala
index ae9f9073e0..c1c94f7b12 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala
@@ -104,6 +104,9 @@ class FigureFactoryTableOpDesc extends 
PythonOperatorDescriptor {
        |
        |class TableChartOperator(UDFTableOperator):
        |
+       |    def render_error(self, error_msg) -> str:
+       |        return f"<h1>Figure Factory Table is not 
available.</h1><p>Reason is: {error_msg}</p>"
+       |
        |    def process_table(self, table: Table, port: int) -> 
Iterator[Optional[TableLike]]:
        |
        |        if table.empty:
diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala
index bf23016275..7290c057dd 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala
@@ -76,6 +76,9 @@ class TablesPlotOpDesc extends PythonOperatorDescriptor {
        |import plotly.io
        |class TableChartOperator(UDFTableOperator):
        |
+       |    def render_error(self, error_msg) -> str:
+       |        return f"<h1>Tables Plot is not available.</h1><p>Reason is: 
{error_msg}</p>"
+       |
        |    def process_table(self, table: Table, port: int) -> 
Iterator[Optional[TableLike]]:
        |
        |        if table.empty:
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDescSpec.scala
index 83eeed1f96..9d564fd358 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDescSpec.scala
@@ -99,4 +99,15 @@ class FigureFactoryTableOpDescSpec extends AnyFlatSpec with 
BeforeAndAfter with
     assert(carries(code, "col_two"))
     code should include("class TableChartOperator(UDFTableOperator)")
   }
+
+  it should "define the render_error the empty-table branches call" in {
+    // Both empty-table branches call self.render_error; without the 
definition they
+    // raised AttributeError instead of rendering the message.
+    withColumns()
+    val code = opDesc.generatePythonCode()
+    code should include("def render_error(self, error_msg) -> str:")
+    code should include(
+      """return f"<h1>Figure Factory Table is not available.</h1><p>Reason is: 
{error_msg}</p>""""
+    )
+  }
 }
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDescSpec.scala
index cc19113906..c088cacc67 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDescSpec.scala
@@ -88,4 +88,15 @@ class TablesPlotOpDescSpec extends AnyFlatSpec with 
BeforeAndAfter with Matchers
     )
     code should not include "')','"
   }
+
+  it should "define the render_error the empty-table branches call" in {
+    // Both empty-table branches call self.render_error; without the 
definition they
+    // raised AttributeError instead of rendering the message.
+    opDesc.includedColumns = List(column("col_one"), column("col_two"))
+    val code = opDesc.generatePythonCode()
+    code should include("def render_error(self, error_msg) -> str:")
+    code should include(
+      """return f"<h1>Tables Plot is not available.</h1><p>Reason is: 
{error_msg}</p>""""
+    )
+  }
 }

Reply via email to