Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import test: improve docs, add more tests #73600

Merged
merged 11 commits into from
Mar 2, 2021
68 changes: 66 additions & 2 deletions docs/docsite/rst/dev_guide/testing/sanity/import.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
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.
All Python imports in the directories specified that are not from a limited set of possibilities listed below
must be imported in a try/except ImportError block as follows:
felixfontein marked this conversation as resolved.
Show resolved Hide resolved

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:
felixfontein marked this conversation as resolved.
Show resolved Hide resolved

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.

The following shows which unchecked imports are allowed from which directories:
felixfontein marked this conversation as resolved.
Show resolved Hide resolved

* 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.
Original file line number Diff line number Diff line change
@@ -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
felixfontein marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 "${@}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down