Import test: improve docs, add more tests (#73600)

This commit is contained in:
Felix Fontein 2021-03-02 01:10:46 +01:00 committed by GitHub
parent ba3f84883f
commit 3cc693b92c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 148 additions and 4 deletions

View file

@ -1,5 +1,68 @@
import
======
All Python imports in ``lib/ansible/modules/`` and ``lib/ansible/module_utils/`` which are not from the Python standard library
must be imported in a try/except ImportError block.
Ansible allows unchecked imports of some libraries from specific directories, listed at the bottom of this section. Import all other Python libraries in a try/except ImportError block to support sanity tests such as ``validate-modules`` and to allow Ansible to give better error messages to the user. To import a library in a try/except ImportError block:
1. In modules:
.. code-block:: python
# Instead of 'import another_library', do:
import traceback
try:
import another_library
except ImportError:
HAS_ANOTHER_LIBRARY = False
ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
HAS_ANOTHER_LIBRARY = True
# Later in module code:
module = AnsibleModule(...)
if not HAS_ANOTHER_LIBRARY:
# Needs: from ansible.module_utils.basic import missing_required_lib
module.fail_json(
msg=missing_required_lib('another_library'),
exception=ANOTHER_LIBRARY_IMPORT_ERROR)
2. In plugins:
.. code-block:: python
# Instead of 'import another_library', do:
from ansible.module_utils.six import raise_from
try:
import another_library
except ImportError as imp_exc:
ANOTHER_LIBRARY_IMPORT_ERROR = imp_exc
else:
ANOTHER_LIBRARY_IMPORT_ERROR = None
# Later in plugin code, for example in __init__ of the plugin:
if ANOTHER_LIBRARY_IMPORT_ERROR:
raise_from(
AnsibleError('another_library must be installed to use this plugin'),
ANOTHER_LIBRARY_IMPORT_ERROR)
# If you target only newer Python 3 versions, you can also use the
# 'raise ... from ...' syntax.
Ansible allows the following unchecked imports from these specific directories:
* ansible-core:
* For ``lib/ansible/modules/`` and ``lib/ansible/module_utils/``, unchecked imports are only allowed from the Python standard library;
* For ``lib/ansible/plugins/``, unchecked imports are only allowed from the Python standard library, from dependencies of ansible-core, and from ansible-core itself;
* collections:
* For ``plugins/modules/`` and ``plugins/module_utils/``, unchecked imports are only allowed from the Python standard library;
* For other directories in ``plugins/`` (see `the community collection requirements <https://github.com/ansible-collections/overview/blob/main/collection_requirements.rst#modules-plugins>`_ for a list), unchecked imports are only allowed from the Python standard library, from dependencies of ansible-core, and from ansible-core itself.

View file

@ -0,0 +1,31 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
name: bad
short_description: Bad lookup
description: A bad lookup.
author:
- Ansible Core Team
'''
EXAMPLES = '''
- debug:
msg: "{{ lookup('ns.col.bad') }}"
'''
RETURN = ''' # '''
from ansible.plugins.lookup import LookupBase
from ansible import constants
import lxml
class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
self.set_options(var_options=variables, direct=kwargs)
return terms

View file

@ -0,0 +1,29 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
name: world
short_description: World lookup
description: A world lookup.
author:
- Ansible Core Team
'''
EXAMPLES = '''
- debug:
msg: "{{ lookup('ns.col.world') }}"
'''
RETURN = ''' # '''
from ansible.plugins.lookup import LookupBase
from ansible import constants
class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
self.set_options(var_options=variables, direct=kwargs)
return terms

View file

@ -0,0 +1,8 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# This is not an allowed import, but since this file is in a plugins/ subdirectory that is not checked,
# the import sanity test will not complain.
import lxml

View file

@ -1,5 +1,6 @@
plugins/modules/bad.py import
plugins/modules/bad.py pylint:ansible-bad-module-import
plugins/lookup/bad.py import
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-function
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import-from

View file

@ -7,7 +7,13 @@ cd "${WORK_DIR}/ansible_collections/ns/col"
# rename the sanity ignore file to match the current ansible version and update import ignores with the python version
ansible_version="$(python -c 'import ansible.release; print(".".join(ansible.release.__version__.split(".")[:2]))')"
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt"
if [ "${ANSIBLE_TEST_PYTHON_VERSION}" == "2.6" ]; then
# Non-module/module_utils plugins are not checked on this remote-only Python versions
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" | grep -v 'plugins/[^m].* import' > "tests/sanity/ignore-${ansible_version}.txt"
else
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt"
fi
cat "tests/sanity/ignore-${ansible_version}.txt"
# common args for all tests
common=(--venv --color --truncate 0 "${@}")

View file

@ -7,7 +7,13 @@ cd "${WORK_DIR}/ansible_collections/ns/col"
# rename the sanity ignore file to match the current ansible version and update import ignores with the python version
ansible_version="$(python -c 'import ansible.release; print(".".join(ansible.release.__version__.split(".")[:2]))')"
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt"
if [ "${ANSIBLE_TEST_PYTHON_VERSION}" == "2.6" ]; then
# Non-module/module_utils plugins are not checked on this remote-only Python versions
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" | grep -v 'plugins/[^m].* import' > "tests/sanity/ignore-${ansible_version}.txt"
else
sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt"
fi
cat "tests/sanity/ignore-${ansible_version}.txt"
# common args for all tests
# each test will be run in a separate venv to verify that requirements have been properly specified