From 78a8d090825f0aeb520c4f9f3268833915885bd5 Mon Sep 17 00:00:00 2001 From: Adrian Likins Date: Wed, 21 Feb 2018 12:19:03 -0500 Subject: [PATCH] Changes to bundled distro to be Python-2.6 compatible * Port bundled distro to use optparse instead of argparse (py2.6) * Use an absolute import to satisfy the current import testing harness * Port from subprocess.check_output to subprocess.Popen.communicate() (py2.6) * Add license location The changes have been proposed upstream here: https://github.com/nir0s/distro/pull/232 Upstream is contemplating a branch where everyone wanting python-2.6 support can collaborate without it becoming part of the regularly supported releases. --- lib/ansible/module_utils/distro/_distro.py | 68 ++++++++++++++++++---- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/lib/ansible/module_utils/distro/_distro.py b/lib/ansible/module_utils/distro/_distro.py index 1e01f4d72f9..4db837e2d99 100644 --- a/lib/ansible/module_utils/distro/_distro.py +++ b/lib/ansible/module_utils/distro/_distro.py @@ -12,11 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# A copy of the Apache License can also be found in the same directory -# as this file (./LICENSE) +# A local copy of the license can be found in licenses/Apache-License.txt # # Modifications to this code have been made by Ansible Project -# """ The ``distro`` package (``distro`` stands for Linux Distribution) provides @@ -40,7 +38,7 @@ import sys import json import shlex import logging -import argparse +import optparse import subprocess @@ -98,6 +96,56 @@ _DISTRO_RELEASE_IGNORE_BASENAMES = ( ) +# +# Python 2.6 does not have subprocess.check_output so replicate it here +# +def _my_check_output(*popenargs, **kwargs): + r"""Run command with arguments and return its output as a byte string. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(["ls", "-l", "/dev/null"]) + 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(["/bin/sh", "-c", + ... "ls -l non_existent_file ; exit 0"], + ... stderr=STDOUT) + 'ls: non_existent_file: No such file or directory\n' + + This is a backport of Python-2.7's check output to Python-2.6 + """ + if 'stdout' in kwargs: + raise ValueError( + 'stdout argument not allowed, it will be overridden.' + ) + process = subprocess.Popen( + stdout=subprocess.PIPE, *popenargs, **kwargs + ) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + # Deviation from Python-2.7: Python-2.6's CalledProcessError does not + # have an argument for the stdout so simply omit it. + raise subprocess.CalledProcessError(retcode, cmd) + return output + + +try: + _check_output = subprocess.check_output +except AttributeError: + _check_output = _my_check_output + + def linux_distribution(full_distribution_name=True): """ Return information about the current OS distribution as a tuple @@ -552,7 +600,7 @@ class cached_property(object): self._f = f def __get__(self, obj, owner): - assert obj is not None, 'call {} on an instance'.format(self._fname) + assert obj is not None, 'call {0} on an instance'.format(self._fname) ret = obj.__dict__[self._fname] = self._f(obj) return ret @@ -1001,7 +1049,7 @@ class LinuxDistribution(object): with open(os.devnull, 'w') as devnull: try: cmd = ('lsb_release', '-a') - stdout = subprocess.check_output(cmd, stderr=devnull) + stdout = _check_output(cmd, stderr=devnull) except OSError: # Command not found return {} content = stdout.decode(sys.getfilesystemencoding()).splitlines() @@ -1036,7 +1084,7 @@ class LinuxDistribution(object): with open(os.devnull, 'w') as devnull: try: cmd = ('uname', '-rs') - stdout = subprocess.check_output(cmd, stderr=devnull) + stdout = _check_output(cmd, stderr=devnull) except OSError: return {} content = stdout.decode(sys.getfilesystemencoding()).splitlines() @@ -1181,13 +1229,13 @@ def main(): logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) - parser = argparse.ArgumentParser(description="OS distro info tool") - parser.add_argument( + parser = optparse.OptionParser(description="OS distro info tool") + parser.add_option( '--json', '-j', help="Output in machine readable format", action="store_true") - args = parser.parse_args() + args, opts = parser.parse_args() if args.json: logger.info(json.dumps(info(), indent=4, sort_keys=True))