aglinxinyuan opened a new pull request, #8671:
URL: https://github.com/apache/texera/pull/8671

   ### What changes were proposed in this PR?
   
   Inside a control block (`Loop Start ... Loop End`), a text property of an 
operator whose Scala descriptor generates Python, such as a sklearn trainer's 
hyperparameter value, can now hold a loop-variable reference such as `$i`. 
Until now the compiler rejected every reference on these operators, because 
their code is generated before the loop runs.
   
   The generated code already decodes each text property when the operator 
runs. Inside a block, the decode of a `$i` text becomes a read of the 
iteration's state, which each Python operator registers before `process_state` 
(#8642):
   
   ```
   Loop Start (i = 1) -> SVR trainer (C = "$i") -> Loop End
   
   generated code, outside a block:  C = float 
(self.decode_python_template('JGk='))   # the text "$i"
   generated code, inside a block:   C = float (self.loop_variable_text('i'))   
       # 1, then 2, then 3
   ```
   
   | Piece | What it does |
   |---|---|
   | pybuilder `decoderExpression` | The exact expression pyb renders a text 
as; the renderer now uses it too, so the two cannot drift. |
   | `PythonOperatorDescriptor` | When the descriptor holds text references 
(only inside a block), each reference's decode expression in the generated code 
becomes `self.loop_variable_text('<name>')`. Nothing else in the code changes. |
   | `WorkflowCompiler` | A generated operator is accepted when every text 
reference was rewritten. The compile error now names each property it cannot 
bind and why: a numeric or boolean property (its value is written into the 
code), a text the code pastes in as it is, a text the code never reads, or a 
text that names a new output column. UDFs keep rejecting references, with their 
own reason. |
   | pyamber `Operator.loop_variable_text` | Returns the variable's current 
value as text, merged over every state message the operator received (the 
latest wins, as the JVM's `LateBoundExecutor` merges them), and spelled as the 
JVM binds a text property: `true` / `false`, and scalars only. It fails with a 
clear message when no state carried the name. |
   
   Outside every block nothing changes: the sidecar is empty, so the code is 
the same, and a text such as `$AAPL` stays text.
   
   **Dependencies.** This builds on #8639 (the compiler's references) and #8642 
(`loop_state`). Both are merged into this branch until they land on `main`. The 
change in this PR is the top commit, fd46a3f977.
   
   ### Any related issues, documentation, discussions?
   
   Closes #8669
   
   ### How was this PR tested?
   
   New and updated tests. Each new test was run red against the pre-change code 
first.
   
   | Spec | Covers |
   |---|---|
   | `PythonTemplateBuilderApiSpec` | `decoderExpression` equals what pyb 
renders for text, empty text, unicode, quotes, backslashes and newlines; 
different texts give different expressions. |
   | `StateReferenceModuleSpec` | `textReferences` keeps only the pointers 
whose value is the `$name` text; typed placeholders, other texts and missing 
pointers are dropped. |
   | `PythonOperatorDescriptorSpec` (new) | On the real SVC trainer: a text 
reference is rewritten everywhere it is decoded; an empty sidecar leaves the 
code as generated; other texts (`$j`, `$index`, `rbf`) stay decoded; two 
references; a typed placeholder is not rewritten; a failed generation keeps its 
error. |
   | `WorkflowCompilerSpec` | Sort and the SVR trainer bind a text reference 
inside a block; outside a block `$USD` stays decoded; each rejection reason, 
alone and together (typed, pasted, never read, names an output column); UDF, 
Java UDF and R UDF references stay rejected; a failed generation reports its 
own error. |
   | `LoopIntegrationSpec` | End to end: `Text Input (1.0, 2.0, 3.0) -> Loop 
Start (i = 1) -> SVR trainer (C = $i) -> Loop End` produces the fits `C = 1.0`, 
`C = 2.0`, `C = 3.0`, one per iteration. It spawns Python workers, so it runs 
in CI. |
   | `test_operator.py`, `test_data_processor.py`, `test_main_loop.py` | 
`loop_variable_text` for int, float, string and bool values; merged states; 
errors before any state, for a missing name and for a non-scalar; a snippet 
shaped like the generated code, run against a real operator. |
   
   ```
   sbt "WorkflowCompiler/test"                                                  
   # 97 passed
   sbt "PyBuilder/test"                                                         
   # 192 passed
   sbt "WorkflowOperator/testOnly 
org.apache.texera.amber.operator.PythonOperatorDescriptorSpec"  # 11 passed
   sbt "WorkflowCore/testOnly org.apache.texera.amber.core.state.*"             
    # 28 passed
   sbt "WorkflowCompiler/scalafmtCheckAll" "WorkflowCompiler/scalafixAll 
--check"  # clean (and the other changed modules)
   sbt "WorkflowExecutionService/Test/compile"                                  
   # LoopIntegrationSpec compiles
   cd amber && pytest src/test/python/core/models/test_operator.py \
     src/test/python/core/runnables/test_data_processor.py \
     src/test/python/core/runnables/test_main_loop.py                           
   # 131 passed
   ruff format --check src/main/python src/test/python && ruff check 
src/main/python src/test/python  # clean
   ```
   
   ### Was this PR authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Claude Opus 5.5)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to