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) --------------------