Krikri::ControllerPython
Which Python interpreter a CONTROLLER-side wrapper run (the custom
lookup/filter/test/action plugin mechanisms) gets spawned under, and
the one-place retry when the plain python3 on PATH cannot serve it.
The runners used to spawn the first python3/python on PATH and
stamp {"kind": "unavailable"} when that interpreter could not
import the ansible package - which every caller degrades to its
pre-custom-plugin behavior, i.e. a lookup()/query() of a plugin that
DOES exist silently becomes null. On a controller where Ansible is
not installed for that interpreter (the common Homebrew/pipx
layouts: python3 on PATH is Xcode's or a Homebrew python without
the ansible package, while ansible-playbook runs under its own
venv interpreter) that produced exactly the issue #778 symptom -
real ansible-playbook runs the plugin, krikri returns null.
So: when a wrapper answers "unavailable", retry once under the
interpreter the user's own ansible-playbook/ansible runs under
(its shebang line), then any other python3* on PATH that can
import ansible. Nothing changes when the first interpreter works,
and nothing changes when NONE of them can import ansible (the
degradation stays exactly as before) - a wrapper that answered
anything other than "unavailable" is never retried, so a fake/dying
interpreter keeps the #704/#705 spawn classification.
Probing is cached: per interpreter path (a path's answer never changes) and per PATH/HOME pair (which candidates exist at all), so a run pays for at most one probe per candidate, not one per lookup.
Constants
The capability probe. ansible.plugins is the package every
wrapper imports from (ansible.plugins.lookup.LookupBase and
friends), so an interpreter that cannot import it cannot serve any
of them.
Interpreter basenames tried after the ansible binaries' own
shebangs: the plain names first (what the runners always used),
then the versioned ones, newest first, so a controller whose
python3 lacks ansible but whose python3.13 has it still works.
Instance methods
The first interpreter OTHER than primary that can import ansible,
or nil when the controller has none. Order: the shebang interpreter
of ansible-playbook then ansible (the interpreter real ansible
itself runs under - the pipx/Homebrew case), then every python3*
on PATH.
Whether python can import the ansible package. Cached per path;
a probe that cannot even spawn the interpreter is a plain false.
The plain controller interpreter - unchanged behavior, the first
python3/python on PATH.
Runs wrapper (the -c program) against stdin under python,
retrying once under an ansible-capable interpreter when the first
one answered {"ok": false, "kind": "unavailable"} (it ran, it
just cannot import ansible - the issue #778 case). Spawn
classification is the runners' own: only a PRE-spawn failure
raises Krikri::SpawnError, a wrapper that ran keeps its answer.