TTestFlow/Docs
Product guide/The builder/Code & the package
Watch insteadHow to Build a Test Sequence and Export a Standalone EXE | Electronics Test Automation9:40

The builder

Code and the package

TestFlow does not run a hidden interpreter over your workflow. It generates a complete, standalone Python application, and the Code pane is that application.

This matters for two reasons. You can read exactly what will touch your hardware before it does, and you are not locked in: the package runs on its own, with or without TestFlow.

The Code pane: a package file tree on the left listing run_workflow.py, Output, Script and libs, and the generated main.py open on the right with syntax highlighting.
The Code pane: your package on the left, the file you are reading on the right.
The Code pane's header: a sidebar toggle, a file icon, the path Script/main.py, and a Download button on the right.
The open file, and the Download that saves it.

What is in the package

package layout
<Project name>/
├── run_workflow.py            the single entry point
├── Script/
│   ├── main.py                the test sequence
│   ├── tf_runtime.py          VISA and serial I/O, CSV recording, limits, control
│   ├── instruments/           generated driver modules, when the bench needs them
│   ├── N1.py, N2.py …         your Python steps, in order
│   ├── requirements.txt       pip dependencies
│   └── <name>_workflow.json   the full workflow, exported
├── libs/                      shared instrument libraries
└── Output/                    results land here: CSV, log, report
FileWhat it isWhy you might read it
run_workflow.pyThe entry point. One command runs the whole test.This is what you invoke outside TestFlow.
Script/main.pyThe sequence: set-points, loops, delays, measurements, limits.To check what is actually being sent, and in what order.
Script/tf_runtime.pyThe runtime layer: opens instruments, sends and queries, records rows, evaluates limits, handles stop and pause.To understand how a measurement becomes a row with a verdict.
Script/instruments/Driver modules for instruments with no command catalog.Present only when your bench needs one.
requirements.txtThe pip dependencies, including anything your Python steps declared.Before running the package on another machine.
Output/Empty until a run. Then the results CSV, the full log, and the PDF report if enabled.This is where your data is.

What the generated code looks like

Three excerpts from a real main.py, in the order they appear in the file. Together they are the whole shape of a TestFlow package.

A block of generated Python headed with the comment Bench, instrument addresses, defining POWER_SUPPLY as a USB VISA resource string and OSCILLOSCOPE as another, each with a trailing comment naming the instrument.
The bench block. Every instrument's VISA address is a named constant at the top of the file, so moving the test to another bench is a one-line edit per instrument rather than a search through the sequence.
Generated Python declaring CSV_COLUMNS as a list of Iteration, vin and N4(Measurement), then a run function that calls tf.init with the output directory, a total step count and the CSV columns.
The columns the run will record are declared up front, and the runtime is initialised with them. This is the list the Report's columns are later validated against.
Generated Python calling tf.step with the label N1 Power Supply, then tf.cmd sending INSTrument:SELect CH1 with the trailing comment Select_Channel, then tf.cmd sending SOURce:CURRent 0.5 with the comment Set_Current.
Where an action becomes a command. The action you saw on the canvas, Select_Channel, is emitted as this model's real SCPI, and the action title is kept as the comment so the two are readable side by side.

Two ways the code gets written

Compiled, deterministically

Every instrument on the bench has a command catalog

  • A code emitter walks the workflow and writes the Python directly.
  • No model involved. Instant, free, and identical every time for the same workflow.
  • Set-points are collected into a parameters block at the top of main.py, so you can retune a sweep without touching the logic.

Written by the codegen agent

The bench contains an instrument with no catalog

  • A second, non-chat agent writes a driver module for that instrument.
  • It runs on a debounce after the workflow’s STRUCTURE changes, not on every keystroke.
  • Retuning a value never re-triggers it. Adding or removing a step does.

Generated code is not trusted on the model's word. Before a package is accepted, its Python is parsed for real, every runtime call is checked against the runtime's actual public API, it is checked for hard-coded instrument addresses (a generated driver has to take its address, not bake one in), and its connection setup is checked against the configured bench. Output that fails goes back for one repair attempt, and if it fails again the deterministic compiler takes over.

Reading and taking the code

  • Click any file in the tree to read it, with syntax highlighting.
  • The libs/ tree loads on expand, because it is a shared library and not part of your generated output.
  • Download saves the file you are currently reading, so you can drop main.py into an existing project or send one driver to a colleague.

Before it touches hardware

When a run would execute a generated driver against real instruments for the first time, Executor asks you to approve it and shows you the code first. Approval is remembered per workflow structure, so retuning values does not re-prompt, but changing the shape of the test does.