Python

Last updated on June 17, 2026

You can run any Python function using the python parameter.

Let's start by creating a Python file in a new phases/ directory:

procedure.yaml
check_device.py

Then add a new phase referencing this module:

main:
  - name: Test Functionality
    python: phases.check_device
def check_device():
    pass  # Your code here

TofuPilot will manage your Python Environment, run your function, and provide access to framework features through Function Parameters.

File

You can specify which Python file to execute using dot notation. Paths are resolved relative to your procedure .yaml file.

For example, for this file tree:

procedure.yaml
foo.py
bar.py
baz.py

You can reference these files with:

main:
  - name: Foo
    python: phases.foo
  - name: Bar
    python: phases.nested.bar
  - name: Baz
    python: shared.baz

Function

You can specify which function to call using colon syntax: module:function.

TofuPilot defaults to the last component of the path as the function name.

main:
  - name: Calibration
    python: calibrate # Calls calibrate() function by default

  - name: Custom Function
    python: calibrate:run_calibration # Calls run_calibration() instead
def calibrate():
    pass  # Default function - called by first phase

def run_calibration():
    pass  # Custom function - called by second phase

Environment

Overview

TofuPilot automatically manages Python virtual environments and runs your code in isolation, ensuring consistent behavior across development and production.

As soon as you add a Python phase to your procedure, TofuPilot automatically:

  1. Looks for an existing venv directory in your procedure directory (or workspace root in a monorepo)
  2. If found: Uses it as-is, unless your dependencies or Python version changed
  3. If not found: Creates a venv directory with the specified Python version (defaults to 3.11)
  4. Installs dependencies from pyproject.toml or requirements.txt if either exists (the venv is created without dependencies if neither is present)
  5. Re-syncs dependencies whenever pyproject.toml, requirements.txt, or the Python version changes

Dependencies

Edit pyproject.toml in your procedure directory to manage dependencies:

[project]
name = "my-procedure"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "numpy>=1.24.0",
    "pandas>=2.0.0",
]

TofuPilot will automatically install these dependencies when you run your procedure.

Under the hood, TofuPilot uses UV, a Python package manager faster than pip, with full support for the PEP 508 dependency specification.

Python version

TofuPilot builds on the lowest supported Python that satisfies requires-python in your pyproject.toml. If you omit requires-python, the default is 3.12.

[project]
requires-python = ">=3.12"

>=3.12 builds on 3.12; >=3.13 builds on 3.13. The lowest satisfying version is chosen because packages drop support for older Python far more slowly than they add it for newer, so the floor maximizes dependency compatibility.

Selection happens at the minor level (3.12), not the patch (3.12.4). The station provisions a virtual environment on whichever compatible patch is available or fetchable, and the dependency wheels are compatible across patches of the same minor line.

Supported minor versions:

VersionNotes
3.10Oldest supported
3.11
3.12Default when requires-python is omitted
3.13
3.14Newest supported

Use a range, not an exact pin. An exact pin like ==3.13 or requires-python = "==3.13.4" matches a single patch that the build may not ship, and the build fails with "no supported Python satisfies requires-python". Pin a lower bound (and optionally an upper bound) instead:

[project]
requires-python = ">=3.13,<3.14"

If you must pin a single minor, use the wildcard form ==3.13.* (matches any 3.13 patch), not ==3.13.

For advanced use cases, you can configure private registries, local packages, Git dependencies, and platform-specific requirements directly in your pyproject.toml:

Function Parameters

TofuPilot automatically injects parameters based on your function signature. You can refer to each framework feature's documentation for Python function parameters usage.

How is this guide?

On this page