From 699a8ebfb5a4910de7cc9032b1b2504b8b78b693 Mon Sep 17 00:00:00 2001 From: Alex Willmer Date: Fri, 28 Nov 2025 13:35:35 +0000 Subject: [PATCH] ansible_mitogen: Use INTERPRETER_PYTHON_FALLBACK as python candidates This shouldn't change the interpreter ultimately chosen by Ansible. It should only improve the hit rate of performing interpreter discovery, particular in cases where only pythonX.Y is present on the target. Interpreter discovery may take longer or shorter, depending on the Ansible version and the interpreters present on the target. --- ansible_mitogen/mixins.py | 20 +++++----------- ansible_mitogen/transport_config.py | 36 ++++++++++++++++++++--------- docs/changelog.rst | 3 +++ 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/ansible_mitogen/mixins.py b/ansible_mitogen/mixins.py index 8ddbb437..7065c69a 100644 --- a/ansible_mitogen/mixins.py +++ b/ansible_mitogen/mixins.py @@ -470,18 +470,7 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): # chicken-and-egg issue, mitogen needs a python to run low_level_execute_command # which is required by Ansible's discover_interpreter function if self._mitogen_discovering_interpreter: - possible_pythons = [ - '/usr/bin/python', - 'python3', - 'python3.7', - 'python3.6', - 'python3.5', - 'python2.7', - 'python2.6', - '/usr/libexec/platform-python', - '/usr/bin/python3', - 'python' - ] + possible_pythons = self._mitogen_interpreter_candidates else: # not used, just adding a filler value possible_pythons = ['python'] @@ -492,12 +481,15 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase): rc, stdout, stderr = self._connection.exec_command( cmd, in_data, sudoable, mitogen_chdir=chdir, ) - # TODO: what exception is thrown? - except: + except BaseException as exc: # we've reached the last python attempted and failed if possible_python == possible_pythons[-1]: raise else: + LOG.debug( + '%r._low_level_execute_command: candidate=%r ignored: %s, %r', + self, possible_python, type(exc), exc, + ) continue stdout_text = to_text(stdout, errors=encoding_errors) diff --git a/ansible_mitogen/transport_config.py b/ansible_mitogen/transport_config.py index 3d5fac3c..203ce790 100644 --- a/ansible_mitogen/transport_config.py +++ b/ansible_mitogen/transport_config.py @@ -73,13 +73,21 @@ import ansible.utils.unsafe_proxy from ansible.module_utils.six import with_metaclass from ansible.module_utils.parsing.convert_bool import boolean +import ansible_mitogen.utils import mitogen.core LOG = logging.getLogger(__name__) +if ansible_mitogen.utils.ansible_version[:2] >= (2, 19): + _FALLBACK_INTERPRETER = ansible.executor.interpreter_discovery._FALLBACK_INTERPRETER +elif ansible_mitogen.utils.ansible_version[:2] >= (2, 17): + _FALLBACK_INTERPRETER = u'/usr/bin/python3' +else: + _FALLBACK_INTERPRETER = u'/usr/bin/python' -def run_interpreter_discovery_if_necessary(s, task_vars, action, rediscover_python): + +def run_interpreter_discovery_if_necessary(s, candidates, task_vars, action, rediscover_python): """ Triggers ansible python interpreter discovery if requested. Caches this value the same way Ansible does it. @@ -107,8 +115,11 @@ def run_interpreter_discovery_if_necessary(s, task_vars, action, rediscover_pyth # blow away the discovered_interpreter_config cache and rediscover del task_vars['ansible_facts'][discovered_interpreter_config] - if discovered_interpreter_config not in task_vars['ansible_facts']: + try: + s = task_vars[u'ansible_facts'][discovered_interpreter_config] + except KeyError: action._mitogen_discovering_interpreter = True + action._mitogen_interpreter_candidates = candidates # fake pipelining so discover_interpreter can be happy action._connection.has_pipelining = True s = ansible.executor.interpreter_discovery.discover_interpreter( @@ -121,18 +132,17 @@ def run_interpreter_discovery_if_necessary(s, task_vars, action, rediscover_pyth # cache discovered interpreter task_vars['ansible_facts'][discovered_interpreter_config] = s action._connection.has_pipelining = False - else: - s = task_vars['ansible_facts'][discovered_interpreter_config] # propagate discovered interpreter as fact action._discovered_interpreter_key = discovered_interpreter_config action._discovered_interpreter = s action._mitogen_discovering_interpreter = False + action._mitogen_interpreter_candidates = None return s -def parse_python_path(s, task_vars, action, rediscover_python): +def parse_python_path(s, candidates, task_vars, action, rediscover_python): """ Given the string set for ansible_python_interpeter, parse it using shell syntax and return an appropriate argument vector. If the value detected is @@ -143,10 +153,9 @@ def parse_python_path(s, task_vars, action, rediscover_python): # if python_path doesn't exist, default to `auto` and attempt to discover it s = 'auto' - s = run_interpreter_discovery_if_necessary(s, task_vars, action, rediscover_python) - # if unable to determine python_path, fallback to '/usr/bin/python' + s = run_interpreter_discovery_if_necessary(s, candidates, task_vars, action, rediscover_python) if not s: - s = '/usr/bin/python' + s = _FALLBACK_INTERPRETER return ansible.utils.shlex.shlex_split(s) @@ -510,6 +519,9 @@ class PlayContextSpec(Spec): interpreter_python = C.config.get_config_value( 'INTERPRETER_PYTHON', variables=variables, ) + interpreter_python_fallback = C.config.get_config_value( + 'INTERPRETER_PYTHON_FALLBACK', variables=variables, + ) if '{{' in interpreter_python or '{%' in interpreter_python: templar = self._connection.templar @@ -517,6 +529,7 @@ class PlayContextSpec(Spec): return parse_python_path( interpreter_python, + candidates=interpreter_python_fallback, task_vars=self._task_vars, action=self._action, rediscover_python=rediscover_python) @@ -732,11 +745,12 @@ class MitogenViaSpec(Spec): def python_path(self, rediscover_python=False): s = self._host_vars.get('ansible_python_interpreter') - # #511, #536: executor/module_common.py::_get_shebang() hard-wires - # "/usr/bin/python" as the default interpreter path if no other - # interpreter is specified. + interpreter_python_fallback = self._host_vars.get( + 'ansible_interpreter_python_fallback', [], + ) return parse_python_path( s, + candidates=interpreter_python_fallback, task_vars=self._task_vars, action=self._action, rediscover_python=rediscover_python) diff --git a/docs/changelog.rst b/docs/changelog.rst index 3fd0ab54..f046252f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,6 +21,9 @@ To avail of fixes in an unreleased version, please download a ZIP file In progress (unreleased) ------------------------ +* :gh:issue:`1132` :mod:`ansible_mitogen` During intrepreter discovery use + Ansible ``INTERPRETER_PYTHON_FALLBACK`` config as list of candidates + v0.3.34 (2025-11-27) --------------------