Live demo — data resets daily at 03:00 UTC. Nothing you enter is saved. Server UI →

product: assert audience: test-developer authority: normative

Python Dependencies and Offline Environments

Production test stations are frequently deployed in air-gapped networks with no internet access. The Python runner Docker image ships with only the gRPC transport libraries — nothing else. Third-party packages MUST be made available before a test executes.

How pipPackages works

Declare dependencies in package.json:

{
  "requirements": {
    "minPythonVersion": "3.11",
    "pipPackages": ["requests>=2.32.0"]
  }
}

The Python runner automatically installs these packages at startup by scanning every installed package and reading pipPackages from each package.json.

These are tried in order — they are a fallback chain, not alternatives:

Order Condition Action
1 Package has wheels/ with .whl files pip install --no-index --find-links wheels/ (offline)
2 Step 1 failed or there is no wheels/, and AllowOnlinePip is true (dev only) pip install from PyPI
3 Neither Warning logged; test fails with a pip dependencies … could not be installed error

Vendoring wheels for production

Step 1 — Download wheels on a machine with internet access:

# Helper script (reads pipPackages from package.json)
.\scripts\vendor-wheels.ps1 -PackagePath "path\to\my-package"
# Or manually
pip download requests>=2.32.0 \
  --dest wheels/ \
  --platform manylinux2014_x86_64 \
  --python-version 3.13 \
  --only-binary=:all:

The Python runner runs on python:3.13-slim (Debian Linux, x86_64). Always download wheels for manylinux2014_x86_64 and Python 3.13.

This matters for compiled wheels. pip silently ignores a wheel whose ABI tag (cp311, cp313, …) does not match the interpreter, then reports ERROR: Could not find a version that satisfies the requirement <pkg> (from versions: none) — which reads like the wheel was never vendored. Pure-Python wheels (py3-none-any) are unaffected, so a mismatch typically singles out one compiled package (numpy, matplotlib, pillow, cffi …). The runner detects this case and says so explicitly in the step error; re-vendor with the matching --python-version. Reinstalling the package will NOT fix it.

runner_type: python3.11 in test YAML is an unrelated routing token — it selects the Python runner, it does not pin an interpreter version.

Step 2 — Commit wheels into wheels/ in your package. The runner installs them automatically at startup — no manual docker exec required.

How the runner finds Python modules

The runner adds to sys.path at startup:

  1. /app/scripts — legacy mount for ad-hoc scripts.
  2. <package>/python_modules/ for every installed package — picked up automatically on every step invocation.

Development vs production

  • Development: Set the station config key AllowOnlinePip = true to install from PyPI. This is a station config key, not an environment variable — the runner reads it when the first step executes, so a station can be switched online without a restart.
  • Production: MUST vendor wheels. MUST NOT rely on internet access.
An unhandled error has occurred. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.