ansible-test - Support pylint assertion rewriting (#80020)

Add support for `pylint` assertion rewriting when running unit tests on Python 3.5 and later.
pull/80055/head
Matt Clay 1 year ago committed by GitHub
parent 2f8f7fba4c
commit fe2732b91e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
bugfixes:
- ansible-test - Add support for ``pylint`` assertion rewriting when running unit tests on Python 3.5 and later.
Resolves issue https://github.com/ansible/ansible/issues/68032
known_issues:
- ansible-test - Unit tests for collections do not support ``pylint`` assertion rewriting on Python 2.7.

@ -0,0 +1,4 @@
shippable/generic/group1 # runs in the default test container
context/controller
needs/target/collection
needs/target/ansible-test

@ -0,0 +1,6 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
def test_assertion():
assert dict(yes=True) == dict(no=False)

@ -0,0 +1,22 @@
#!/usr/bin/env bash
source ../collection/setup.sh
set -x
options=$("${TEST_DIR}"/../ansible-test/venv-pythons.py --only-versions)
IFS=', ' read -r -a pythons <<< "${options}"
for python in "${pythons[@]}"; do
if ansible-test units --color --truncate 0 --python "${python}" --requirements "${@}" 2>&1 | tee pylint.log; then
echo "Test did not fail as expected."
exit 1
fi
if [ "${python}" = "2.7" ]; then
grep "^E *AssertionError$" pylint.log
else
grep "^E *AssertionError: assert {'yes': True} == {'no': False}$" pylint.log
fi
done

@ -1,6 +1,7 @@
#!/usr/bin/env python
"""Return target Python options for use with ansible-test."""
import argparse
import os
import shutil
import subprocess
@ -10,6 +11,11 @@ from ansible import release
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--only-versions', action='store_true')
options = parser.parse_args()
ansible_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(release.__file__))))
source_root = os.path.join(ansible_root, 'test', 'lib')
@ -33,6 +39,10 @@ def main():
print(f'{executable} - {"fail" if process.returncode else "pass"}', file=sys.stderr)
if not process.returncode:
if options.only_versions:
args.append(python_version)
continue
args.extend(['--target-python', f'venv/{python_version}'])
print(' '.join(args))

@ -32,6 +32,50 @@ def collection_pypkgpath(self):
raise Exception('File "%s" not found in collection path "%s".' % (self.strpath, ANSIBLE_COLLECTIONS_PATH))
def enable_assertion_rewriting_hook(): # type: () -> None
"""
Enable pylint's AssertionRewritingHook on Python 3.x.
This is necessary because the Ansible collection loader intercepts imports before the pylint provided loader ever sees them.
"""
import sys
if sys.version_info[0] == 2:
return # Python 2.x is not supported
hook_name = '_pytest.assertion.rewrite.AssertionRewritingHook'
hooks = [hook for hook in sys.meta_path if hook.__class__.__module__ + '.' + hook.__class__.__qualname__ == hook_name]
if len(hooks) != 1:
raise Exception('Found {} instance(s) of "{}" in sys.meta_path.'.format(len(hooks), hook_name))
assertion_rewriting_hook = hooks[0]
# This is based on `_AnsibleCollectionPkgLoaderBase.exec_module` from `ansible/utils/collection_loader/_collection_finder.py`.
def exec_module(self, module):
# short-circuit redirect; avoid reinitializing existing modules
if self._redirect_module: # pylint: disable=protected-access
return
# execute the module's code in its namespace
code_obj = self.get_code(self._fullname) # pylint: disable=protected-access
if code_obj is not None: # things like NS packages that can't have code on disk will return None
# This logic is loosely based on `AssertionRewritingHook._should_rewrite` from pytest.
# See: https://github.com/pytest-dev/pytest/blob/779a87aada33af444f14841a04344016a087669e/src/_pytest/assertion/rewrite.py#L209
should_rewrite = self._package_to_load == 'conftest' or self._package_to_load.startswith('test_') # pylint: disable=protected-access
if should_rewrite:
# noinspection PyUnresolvedReferences
assertion_rewriting_hook.exec_module(module)
else:
exec(code_obj, module.__dict__) # pylint: disable=exec-used
# noinspection PyProtectedMember
from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionPkgLoaderBase
_AnsibleCollectionPkgLoaderBase.exec_module = exec_module
def pytest_configure():
"""Configure this pytest plugin."""
try:
@ -40,6 +84,8 @@ def pytest_configure():
except AttributeError:
pytest_configure.executed = True
enable_assertion_rewriting_hook()
# noinspection PyProtectedMember
from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionFinder

Loading…
Cancel
Save