PythonRunner
Namespace: UTILSLIB · Library: Utilities Library
#include <utils/python_runner.h>
class UTILSLIB::PythonRunner
Standardized interface for launching Python scripts from C++.
Features:
- Auto-discovers Python via PATH or explicit config.
- Runs scripts with
-u(unbuffered) by default for real-time output. - Streams stdout/stderr line-by-line through optional callbacks.
- Parses a simple progress protocol: lines matching
[progress] <float>%or[progress] <float>% <message>. - Integrates with MNE-CPP's logging (qDebug/qWarning).
- Supports timeout, environment injection, and working directory.
No Python embedding or linkage is required.
Python script launcher with logging and progress support.
PythonRunner python; // "python3" from PATH; a venv is configured via PythonRunnerConfig
QStringList progress;
python.setProgressCallback([&](float pct, const QString& msg) { progress << QString("%1 %2").arg(pct).arg(msg); });
const PythonRunnerResult run = python.runCode("import sys\nprint('[progress] 50% half')\nprint(sum(map(int, sys.argv[1:])))", {"2", "3"});
Inheritance
Public Methods
PythonRunner(pParent)
Construct a PythonRunner with default configuration.
Parameters:
- pParent : *QObject ** Parent QObject (optional).
PythonRunner(config, pParent)
Construct a PythonRunner with explicit configuration.
Parameters:
-
config : const PythonRunnerConfig & Execution configuration.
-
pParent : *QObject ** Parent QObject (optional).
setConfig(config)
Set the configuration.
Parameters:
- config : const PythonRunnerConfig & Execution configuration.
config()
Get the current configuration.
Returns:
- const PythonRunnerConfig & — Current configuration.
setLineCallback(cb)
Set a callback invoked for every stdout/stderr line.
Parameters:
- cb : PythonLineCallback Line callback (channel, line).
setProgressCallback(cb)
Set a callback invoked when a progress line is detected.
Progress protocol: the script prints lines matching: [progress] 42.5% [progress] 42.5% Training epoch 10/50
Parameters:
- cb : PythonProgressCallback Progress callback (pct, msg).
run(scriptPath, args)
Run a Python script synchronously.
Parameters:
-
scriptPath : const QString & Path to the .py file.
-
args : const QStringList & Arguments forwarded to the script.
Returns:
- PythonRunnerResult —
PythonRunnerResultwith exit code, output, and progress.
runCode(code, args)
Run a Python one-liner synchronously (python -c "...").
Parameters:
-
code : const QString & Python code string.
-
args : const QStringList & Extra arguments appended after -c code.
Returns:
isPythonAvailable()
Check that the configured Python interpreter is reachable.
Returns:
- bool — True if
python --versionsucceeds.
pythonVersion()
Query the version string of the configured Python interpreter.
Returns:
- QString — Version string (e.g. "3.11.5"), or empty if unavailable.
isPackageAvailable(packageName)
Check whether a Python package is importable.
Parameters:
- packageName : const QString & Package name (e.g. "torch", "mne").
Returns:
- bool — True if the import succeeds.
ensureVenv()
Ensure a virtual environment exists, is up-to-date, and has all required packages installed.
Workflow:
If config().venvDir is empty, returns immediately (no-op).
Creates the venv if it doesn't exist yet (python -m venv ...).
Installs dependencies:
- If
config().packageDir is set (contains pyproject.toml): runspip install <packageDir>inside the venv. - Else if
config().requirementsFile is set: runspip install -r <file>inside the venv.
On success, updates the internal config so that subsequent run()/runCode() calls use the venv's Python interpreter.
Returns:
- PythonRunnerResult —
PythonRunnerResultdescribing the venv setup outcome.
runInVenv(scriptPath, args)
Convenience: ensure venv, then run a script inside it.
Equivalent to calling ensureVenv() + run(), but returns early with an error result if venv setup fails.
Parameters:
-
scriptPath : const QString & Path to the .py file.
-
args : const QStringList & Arguments forwarded to the script.
Returns:
venvPythonPath()
Get the Python interpreter path inside the configured venv.
Returns:
- QString — Absolute path to venv python, or empty if no venv configured.
Example
- Full source:
src/examples/ex_utils/main.cpp - Build target:
ex_utils(runs under CTest)
Used in examples
ex_utils
Utilities library: buffers, observers, logging, file formats, montages, layouts and reports.
PythonRunner python; // "python3" from PATH; a venv is configured via PythonRunnerConfig
QStringList progress;
python.setProgressCallback([&](float pct, const QString& msg) { progress << QString("%1 %2").arg(pct).arg(msg); });
const PythonRunnerResult run = python.runCode("import sys\nprint('[progress] 50% half')\nprint(sum(map(int, sys.argv[1:])))", {"2", "3"});
PythonTestHelper helper; // oracle access for tests
const MatrixXd fromNumpy = helper.evalMatrix("print('1 2'); print('3 4')"); // one line per row
if (python.isPythonAvailable()) {
ok &= expect(run.success && run.stdOut.trimmed().endsWith("5") && progress == QStringList({"50 half"}) && fromNumpy == (Matrix2d() << 1, 2, 3, 4).finished(),
"PythonRunner passes arguments and parses [progress] lines; PythonTestHelper reads a matrix");
} else {
qInfo().noquote() << " skip PythonRunner (python3 not found)";
Authors of this file
- Christoph Dinh <christoph.dinh@mne-cpp.org>