Krikri::PythonTestRunner
Constants
The controller-side wrapper: imports each plugin file, merges
every TestModule class's tests() dict, then either reports the
merged names ("list") or invokes the requested test with the
JSON-encoded operand and arguments ("call") and prints the
JSON-encoded result. Always responds with a single-line JSON
object, so a plugin that prints its own noise cannot corrupt the
protocol - only the LAST line of stdout is parsed.
Class methods
Where "Skipping plugin" warnings go - STDERR in production, replaceable so a spec can capture the text without touching the process's STDERR.
Instance methods
Invokes test name from sources with the Jinja operand value and its resolved arguments, returning the structured result (the caller decides truthiness - Ansible tests may return any Python value, Jinja coerces it). Raises TestError when the test is not found or the invocation fails (callers degrade to the plain unknown-test error).
Whether name is exposed by any of sources' TestModule classes.
Results are cached per source file (keyed by path, invalidated by
mtime) - the when: clause's compile-time test-name pre-pass can
ask this repeatedly for the same role.
Finds every .py file under the role's own test_plugins/, the
test_plugins/ of every role LOADED so far this run (same
loaded-role registry as PythonFilterRunner#find_sources), the
playbook-adjacent test_plugins/, plus Ansible's configured test
dirs (PythonFilterRunner#configured_roots, issue #793; searched last
there, which is why they lead this last-import-wins list). Ansible
loads ALL files in a plugin directory, not name-matched ones.
Empty when no root exists - the overwhelmingly common case.
The set of test names exposed across sources. A source file that
fails to introspect (syntax error, no python3, a tests() that
raises) contributes no names - never raises. A file whose import
raises additionally gets Ansible's "Skipping plugin" warning
(Ansible prints one stderr line per unloadable file and keeps
loading the rest); it is emitted only on a cache miss, i.e. once
per file per process, matching Ansible's once-per-load behaviour.
Only a wrapper that actually ANSWERED ({"ok": true}) is cached: a wrapper-level failure (python3 unspawnable, an ok:false crash) used to be cached as this file's empty name set, permanently turning every later lookup of its tests into "No test named" for the rest of the run - the same poisoning the filter-side cache suffered (issue #675). An UNLOADABLE file (import error) still caches as empty: that answer is final, and it keeps the "Skipping plugin" warning once-per-process like Ansible's.