diff --git a/changelogs/fragments/py3-ansible_interpreter.yaml b/changelogs/fragments/py3-ansible_interpreter.yaml new file mode 100644 index 00000000000..7f92f623790 --- /dev/null +++ b/changelogs/fragments/py3-ansible_interpreter.yaml @@ -0,0 +1,4 @@ +bugfixes: +- Fix for ansible_*_interpreter on Python3 when using non-newstyle modules. + Those include old-style ansible modules and Ansible modules written in + non-python scripting languages (https://github.com/ansible/ansible/pull/36541) diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index fe0704aca16..893feabd5b5 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -35,7 +35,7 @@ from io import BytesIO from ansible.release import __version__, __author__ from ansible import constants as C from ansible.errors import AnsibleError -from ansible.module_utils._text import to_bytes, to_text +from ansible.module_utils._text import to_bytes, to_text, to_native from ansible.plugins.loader import module_utils_loader, ps_module_utils_loader from ansible.plugins.shell.powershell import async_watchdog, async_wrapper, become_wrapper, leaf_exec, exec_wrapper # Must import strategy and use write_locks from there @@ -60,6 +60,7 @@ REPLACER_SELINUX = b"<>" # We could end up writing out parameters with unicode characters so we need to # specify an encoding for the python source file ENCODING_STRING = u'# -*- coding: utf-8 -*-' +b_ENCODING_STRING = b'# -*- coding: utf-8 -*-' # module_common is relative to module_utils, so fix the path _MODULE_UTILS_PATH = os.path.join(os.path.dirname(__file__), '..', 'module_utils') @@ -911,25 +912,29 @@ def modify_module(module_name, module_path, module_args, task_vars=None, templar if module_style == 'binary': return (b_module_data, module_style, to_text(shebang, nonstring='passthru')) elif shebang is None: - lines = b_module_data.split(b"\n", 1) - if lines[0].startswith(b"#!"): - shebang = lines[0].strip() - args = shlex.split(str(shebang[2:])) + b_lines = b_module_data.split(b"\n", 1) + if b_lines[0].startswith(b"#!"): + b_shebang = b_lines[0].strip() + # shlex.split on python-2.6 needs bytes. On python-3.x it needs text + args = shlex.split(to_native(b_shebang[2:], errors='surrogate_or_strict')) + + # _get_shebang() takes text strings + args = [to_text(a, errors='surrogate_or_strict') for a in args] interpreter = args[0] - interpreter = to_bytes(interpreter) + b_new_shebang = to_bytes(_get_shebang(interpreter, task_vars, templar, args[1:])[0], + errors='surrogate_or_strict', nonstring='passthru') - new_shebang = to_bytes(_get_shebang(interpreter, task_vars, templar, args[1:])[0], errors='surrogate_or_strict', nonstring='passthru') - if new_shebang: - lines[0] = shebang = new_shebang + if b_new_shebang: + b_lines[0] = b_shebang = b_new_shebang - if os.path.basename(interpreter).startswith(b'python'): - lines.insert(1, to_bytes(ENCODING_STRING)) + if os.path.basename(interpreter).startswith(u'python'): + b_lines.insert(1, b_ENCODING_STRING) + + shebang = to_text(b_shebang, nonstring='passthru', errors='surrogate_or_strict') else: # No shebang, assume a binary module? pass - b_module_data = b"\n".join(lines) - else: - shebang = to_bytes(shebang, errors='surrogate_or_strict') + b_module_data = b"\n".join(b_lines) - return (b_module_data, module_style, to_text(shebang, nonstring='passthru')) + return (b_module_data, module_style, shebang)