Populate PluginInfo::tritonVersion + CI smoke test#79
Conversation
…t Triton) Triton commit 8497c845a (#9937) added isTritonAndPluginsVersionsMatch, which dereferences info->tritonVersion unconditionally at plugin load. uTLXPlugin's PluginInfo initializer omitted the field, leaving it nullptr — std::string construction throws and libtriton import dies with "basic_string::_M_construct null not valid" before any Python-side workaround can run. Fix by passing TRITON_VERSION (matches what support/Export.cpp already does).
`make test` runs lit/FileCheck tests, which exercise MLIR passes but never load libutlx.so into Python. As a result, regressions in the plugin's static init path — fields read by libtriton's plugin loader (e.g. PluginInfo metadata) — slip through CI. The recent tritonVersion-nullptr regression rode through five Triton-pin bumps over a month before being noticed. Add a one-shot `python3 -c "import triton"` with TRITON_PLUGIN_PATHS pointed at the freshly-built libutlx.so. Cheap to run, requires no GPU, and would have caught the regression at the first pin bump that included the new version-check code in libtriton.
| TRITON_PLUGIN_PATHS: ${{ github.workspace }}/build/lib/libutlx.so | ||
| run: | | ||
| test -f "$TRITON_PLUGIN_PATHS" || { echo "libutlx.so missing at $TRITON_PLUGIN_PATHS"; exit 1; } | ||
| python3 -c "import triton; print('triton', triton.__version__, 'loaded with utlx plugin from', '$TRITON_PLUGIN_PATHS')" |
There was a problem hiding this comment.
This is a decent test: "does the plugin load?" But this would be more valuable as an integration test (e.g., using pytest) that loads every plugin registered in this repository to make sure each plugin can be loaded. Then whatever command is necessary to run the test could be added to make test.
There was a problem hiding this comment.
...and I guess from the CI failure that we don't really have a virtual environment set up with Triton installed, so that might be needed as well.
|
Also, it looks like whatever bot you are using mis-formatted the PR description? |
Per code review on the prior commit: rather than a single inline shell command in CI hard-coded to libutlx.so, discover every plugin registered in this repo (every triton-ext.conf) and verify each loads. Wired into `make test` so the suite runs locally as well as in CI. testing/test_plugin_imports.py spawns a fresh interpreter per plugin and runs `import triton` with TRITON_PLUGIN_PATHS pointed at the .so. Each plugin runs in its own subprocess so failures isolate per plugin. This catches PluginInfo-level regressions on Triton builds that load plugins eagerly at import time -- the source-built pin used in CI does. Older release wheels load lazily and may pass even for a broken plugin; CI is the canonical environment for this signal. The Makefile splits `test` into `check-lit-tests` and `check-pytest-tests`; both run as part of `make test`. The standalone CI step is removed.
Per review on the prior tritonVersion fix: the doc comment on the TRITON_VERSION field is overlong for what's effectively a one-liner historical note; the commit message and git blame already capture the context.
Pre-commit's yapf hook prefers wrapping arguments with the closing paren on the same line as the last argument; reformat to match.
`make test` invokes `python3 -m pytest`, which needs the venv on PATH so that python3 resolves to the venv interpreter (where pytest is installed via requirements.txt). The earlier `Setup virtual environment` step propagates PATH via $GITHUB_ENV, but in practice python3 was resolving to the GitHub-hosted toolcache binary by the time `make test` ran -- so pytest came up as missing. Re-activating the venv right before invoking make is the robust spelling. (The previous inline `python3 -c "import triton"` smoke test happened to pass because Triton's `make dev-install` had landed in toolcache, so `import triton` worked even without the venv. Pytest is venv-only, so the same path resolution issue surfaces now.)
|
The latest build error is caused by us not really having a Triton wheel installed anywhere. And it's unclear how we should do this. We probably don't want to just pull a version from PyPI since we're compiling against a specific version; maybe what we should be doing is additionally generating a wheel in the Triton CI action and installing it prior to running the tests. I'm not too sure how feasible this is. If you don't want to take it on, I can work on this sometime next week. |
Bundle python/triton/ in the triton artifact (suffix bumped to -v2 to
invalidate cached v1 tarballs that lack the python tree). Add a dedicated
"Run plugin tests" CI step that points PYTHONPATH at the artifact's python
dir so pytest can `import triton` directly from the prebuilt tree -- no
separate clone or pip install.
The plugin test suite (testing/test_plugins.py, renamed from
test_plugin_imports.py) auto-discovers plugins via triton-ext.conf and
runs three categories per plugin:
- test_plugin_loads[<name>] -- static-init smoke (catches
PluginInfo regressions)
- test_plugin_compiles_kernel[<name>] -- end-to-end JIT compile of a
basic kernel through the
plugin's pipeline
- plugin-specific tests, e.g. test_utlx_registers_tlx_dsl, gated with
@pytest.mark.skipif on the plugin .so existence
Adding a plugin: drop a triton-ext.conf; both parametrized tests pick it
up automatically. Exempting a plugin from a parametrized test: tag it via
pytest.param(..., marks=pytest.mark.skip(...)) -- example dialect is
exempted from the compile test (scaffolding-only Dialect::initialize()
doesn't register StringAttr).
Reverts the check-pytest-tests Makefile target added earlier on this
branch -- the new CI step calls pytest directly. Run locally with:
source ~/.venv-ci/bin/activate
PYTHONPATH=~/oss/triton/python python -m pytest testing/test_plugins.py
|
@abrown I managed to use the CI-action-built Triton for the tests. Please approve the CI action to see if it works |
|
this took longer than I expect. let's land the fix first and have another PR for the test. |
CI setup took longer than I expect. Create this PR to land the fix first. CI tests will continue on a PR (#79) Triton commit 8497c845a (#9937) added isTritonAndPluginsVersionsMatch, which dereferences info->tritonVersion unconditionally at plugin load. uTLXPlugin's PluginInfo initializer omitted the field, leaving it nullptr -- std::string construction throws and libtriton import dies with "basic_string::_M_construct null not valid" before any Python-side workaround can run. Pass TRITON_VERSION, matching support/Export.cpp.
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel. Co-authored-by: wychi
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel. Co-authored-by: wychi
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel. Co-authored-by: wychi
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel. Co-authored-by: wychi
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel. Co-authored-by: wychi
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel.
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel.
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel.
Adds the runtime integration test from triton-lang#79 (testing/test_plugins.py) plus the artifact change that makes it actually work in CI: the Triton artifact now bundles the importable Python package under <triton_install_dir>/python, not just the C++ install tree (cmake --install only copies *.h from python/). Symlinks are dereferenced so the package is complete after extraction. The CI step puts that package on PYTHONPATH and runs pytest, loading each built extension via TRITON_PLUGIN_PATHS to confirm it imports, passes the plugin/Triton version check, and lowers a basic kernel.
Summary
Fixes
ImportError: basic_string::_M_construct null not validraised atfrom triton._C.libtriton import getenv, getenv_boolwheneverlibutlx.sois loaded via
TRITON_PLUGIN_PATHSagainst any Triton commit at or after8497c845a(#9937, "[triton-ext] plugin extension's version check"). ThatPR added
isTritonAndPluginsVersionsMatch, which dereferencesinfo->tritonVersionunconditionally — uTLX'sPluginInfoinitializeromitted that field, leaving it
nullptr, andstd::stringconstructionthrows before any user code runs.
The bug rode through five Triton-pin bumps over four weeks
(
441faacc2026-04-15 →7cff1f272026-05-04) because CI'smake testruns lit/FileCheck on the MLIR passes only — the plugin is never actually
loaded into Python on CI.
Changes
extensions/utlx/uTLXPlugin.cpp— populatePluginInfo::tritonVersionwith
TRITON_VERSION(matches whatsupport/Export.cppalready does forthe export-based plugin path).
.github/workflows/ci.yml— add aSmoke test plugin importstepbetween "Build Triton extensions" and "Run tests":
Test plan
make test, it should pass.