summaryrefslogtreecommitdiffstats
path: root/test/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:16:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:16:49 +0000
commit48e387c5c12026a567eb7b293a3a590241c0cecb (patch)
tree80f2573be2d7d534b8ac4d2a852fe43f7ac35324 /test/lib
parentReleasing progress-linux version 2.16.6-1~progress7.99u1. (diff)
downloadansible-core-48e387c5c12026a567eb7b293a3a590241c0cecb.tar.xz
ansible-core-48e387c5c12026a567eb7b293a3a590241c0cecb.zip
Merging upstream version 2.17.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/lib')
-rw-r--r--test/lib/ansible_test/__init__.py3
-rw-r--r--test/lib/ansible_test/_data/completion/docker.txt16
-rw-r--r--test/lib/ansible_test/_data/completion/remote.txt13
-rw-r--r--test/lib/ansible_test/_data/requirements/ansible-test.txt2
-rw-r--r--test/lib/ansible_test/_data/requirements/constraints.txt8
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt6
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.changelog.txt6
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt4
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.mypy.txt24
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.pep8.txt2
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.pylint.txt14
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt2
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt6
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.yamllint.txt4
-rw-r--r--test/lib/ansible_test/_internal/bootstrap.py4
-rw-r--r--test/lib/ansible_test/_internal/classification/__init__.py5
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/cs.py2
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/nios.py2
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/__init__.py28
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/import.py18
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/mypy.py17
-rw-r--r--test/lib/ansible_test/_internal/commands/units/__init__.py2
-rw-r--r--test/lib/ansible_test/_internal/config.py1
-rw-r--r--test/lib/ansible_test/_internal/content_config.py5
-rw-r--r--test/lib/ansible_test/_internal/coverage_util.py1
-rw-r--r--test/lib/ansible_test/_internal/docker_util.py2
-rw-r--r--test/lib/ansible_test/_internal/host_profiles.py9
-rw-r--r--test/lib/ansible_test/_internal/python_requirements.py120
-rw-r--r--test/lib/ansible_test/_internal/ssh.py1
-rw-r--r--test/lib/ansible_test/_internal/util.py2
-rw-r--r--test/lib/ansible_test/_internal/util_common.py2
-rw-r--r--test/lib/ansible_test/_internal/venv.py118
-rw-r--r--test/lib/ansible_test/_util/__init__.py3
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.py46
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.py44
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.json10
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.json7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.py21
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini6
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini6
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/config/collection.cfg1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/config/default.cfg1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py3
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/shellcheck/exclude.txt1
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py90
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py7
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py12
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py2
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/yamllint/yamllinter.py20
-rw-r--r--test/lib/ansible_test/_util/controller/tools/sslcheck.py22
-rw-r--r--test/lib/ansible_test/_util/target/__init__.py3
-rwxr-xr-xtest/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py3
-rw-r--r--test/lib/ansible_test/_util/target/common/__init__.py2
-rw-r--r--test/lib/ansible_test/_util/target/common/constants.py5
-rw-r--r--test/lib/ansible_test/_util/target/injector/python.py23
-rw-r--r--test/lib/ansible_test/_util/target/pytest/plugins/ansible_forked.py11
-rw-r--r--test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py6
-rw-r--r--test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py3
-rw-r--r--test/lib/ansible_test/_util/target/sanity/compile/compile.py15
-rw-r--r--test/lib/ansible_test/_util/target/sanity/import/importer.py13
-rw-r--r--test/lib/ansible_test/_util/target/setup/bootstrap.sh154
-rw-r--r--test/lib/ansible_test/_util/target/setup/probe_cgroups.py3
-rw-r--r--test/lib/ansible_test/_util/target/setup/quiet_pip.py17
-rw-r--r--test/lib/ansible_test/_util/target/setup/requirements.py11
-rw-r--r--test/lib/ansible_test/_util/target/tools/virtualenvcheck.py3
-rw-r--r--test/lib/ansible_test/_util/target/tools/yamlcheck.py3
80 files changed, 220 insertions, 956 deletions
diff --git a/test/lib/ansible_test/__init__.py b/test/lib/ansible_test/__init__.py
index 527d413..223f9b4 100644
--- a/test/lib/ansible_test/__init__.py
+++ b/test/lib/ansible_test/__init__.py
@@ -1,2 +1 @@
-# Empty __init__.py to allow importing of `ansible_test._util.target.common` under Python 2.x.
-# This allows the ansible-test entry point to report supported Python versions before exiting.
+# Empty __init__.py to allow relative imports to work under mypy.
diff --git a/test/lib/ansible_test/_data/completion/docker.txt b/test/lib/ansible_test/_data/completion/docker.txt
index a863ecb..b8603ec 100644
--- a/test/lib/ansible_test/_data/completion/docker.txt
+++ b/test/lib/ansible_test/_data/completion/docker.txt
@@ -1,9 +1,7 @@
-base image=quay.io/ansible/base-test-container:5.10.0 python=3.12,2.7,3.6,3.7,3.8,3.9,3.10,3.11
-default image=quay.io/ansible/default-test-container:8.12.0 python=3.12,2.7,3.6,3.7,3.8,3.9,3.10,3.11 context=collection
-default image=quay.io/ansible/ansible-core-test-container:8.12.0 python=3.12,2.7,3.6,3.7,3.8,3.9,3.10,3.11 context=ansible-core
-alpine3 image=quay.io/ansible/alpine3-test-container:6.3.0 python=3.11 cgroup=none audit=none
-centos7 image=quay.io/ansible/centos7-test-container:6.3.0 python=2.7 cgroup=v1-only
-fedora38 image=quay.io/ansible/fedora38-test-container:6.3.0 python=3.11
-opensuse15 image=quay.io/ansible/opensuse15-test-container:6.3.0 python=3.6
-ubuntu2004 image=quay.io/ansible/ubuntu2004-test-container:6.3.0 python=3.8
-ubuntu2204 image=quay.io/ansible/ubuntu2204-test-container:6.3.0 python=3.10
+base image=quay.io/ansible/base-test-container:6.2.0 python=3.12,3.7,3.8,3.9,3.10,3.11
+default image=quay.io/ansible/default-test-container:9.6.0 python=3.12,3.7,3.8,3.9,3.10,3.11 context=collection
+default image=quay.io/ansible/ansible-core-test-container:9.6.0 python=3.12,3.7,3.8,3.9,3.10,3.11 context=ansible-core
+alpine319 image=quay.io/ansible/alpine319-test-container:7.1.0 python=3.11 cgroup=none audit=none
+fedora39 image=quay.io/ansible/fedora39-test-container:7.1.0 python=3.12
+ubuntu2004 image=quay.io/ansible/ubuntu2004-test-container:7.1.0 python=3.8
+ubuntu2204 image=quay.io/ansible/ubuntu2204-test-container:7.1.0 python=3.10
diff --git a/test/lib/ansible_test/_data/completion/remote.txt b/test/lib/ansible_test/_data/completion/remote.txt
index 06d4b5e..cad7fa4 100644
--- a/test/lib/ansible_test/_data/completion/remote.txt
+++ b/test/lib/ansible_test/_data/completion/remote.txt
@@ -1,14 +1,13 @@
-alpine/3.18 python=3.11 become=doas_sudo provider=aws arch=x86_64
+alpine/3.19 python=3.11 become=doas_sudo provider=aws arch=x86_64
alpine become=doas_sudo provider=aws arch=x86_64
-fedora/38 python=3.11 become=sudo provider=aws arch=x86_64
+fedora/39 python=3.12 become=sudo provider=aws arch=x86_64
fedora become=sudo provider=aws arch=x86_64
-freebsd/13.2 python=3.9,3.11 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
+freebsd/13.3 python=3.9,3.11 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
+freebsd/14.0 python=3.9,3.11 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
freebsd python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
-macos/13.2 python=3.11 python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
+macos/14.3 python=3.11 python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
macos python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
-rhel/7.9 python=2.7 become=sudo provider=aws arch=x86_64
-rhel/8.8 python=3.6,3.11 become=sudo provider=aws arch=x86_64
-rhel/9.2 python=3.9,3.11 become=sudo provider=aws arch=x86_64
+rhel/9.3 python=3.9,3.11 become=sudo provider=aws arch=x86_64
rhel become=sudo provider=aws arch=x86_64
ubuntu/22.04 python=3.10 become=sudo provider=aws arch=x86_64
ubuntu become=sudo provider=aws arch=x86_64
diff --git a/test/lib/ansible_test/_data/requirements/ansible-test.txt b/test/lib/ansible_test/_data/requirements/ansible-test.txt
index 17662f0..e0bb945 100644
--- a/test/lib/ansible_test/_data/requirements/ansible-test.txt
+++ b/test/lib/ansible_test/_data/requirements/ansible-test.txt
@@ -1,5 +1,3 @@
# The test-constraints sanity test verifies this file, but changes must be made manually to keep it in up-to-date.
-virtualenv == 16.7.12 ; python_version < '3'
coverage == 7.3.2 ; python_version >= '3.8' and python_version <= '3.12'
coverage == 6.5.0 ; python_version >= '3.7' and python_version <= '3.7'
-coverage == 4.5.4 ; python_version >= '2.6' and python_version <= '3.6'
diff --git a/test/lib/ansible_test/_data/requirements/constraints.txt b/test/lib/ansible_test/_data/requirements/constraints.txt
index dd837e3..17f3638 100644
--- a/test/lib/ansible_test/_data/requirements/constraints.txt
+++ b/test/lib/ansible_test/_data/requirements/constraints.txt
@@ -1,15 +1,11 @@
# do not add a cryptography or pyopenssl constraint to this file, they require special handling, see get_cryptography_requirements in python_requirements.py
# do not add a coverage constraint to this file, it is handled internally by ansible-test
-packaging < 21.0 ; python_version < '3.6' # packaging 21.0 requires Python 3.6 or newer
+pypsrp < 1.0.0 # in case the next major version is too big of a change
pywinrm >= 0.3.0 ; python_version < '3.11' # message encryption support
pywinrm >= 0.4.3 ; python_version >= '3.11' # support for Python 3.11
-pytest < 5.0.0, >= 4.5.0 ; python_version == '2.7' # pytest 5.0.0 and later will no longer support python 2.7
-pytest >= 4.5.0 ; python_version > '2.7' # pytest 4.5.0 added support for --strict-markers
+pytest >= 4.5.0 # pytest 4.5.0 added support for --strict-markers
ntlm-auth >= 1.3.0 # message encryption support using cryptography
requests-ntlm >= 1.1.0 # message encryption support
requests-credssp >= 0.1.0 # message encryption support
-pyparsing < 3.0.0 ; python_version < '3.5' # pyparsing 3 and later require python 3.5 or later
mock >= 2.0.0 # needed for features backported from Python 3.6 unittest.mock (assert_called, assert_called_once...)
pytest-mock >= 1.4.0 # needed for mock_use_standalone_module pytest option
-setuptools < 45 ; python_version == '2.7' # setuptools 45 and later require python 3.5 or later
-wheel < 0.38.0 ; python_version < '3.7' # wheel 0.38.0 and later require python 3.7 or later
diff --git a/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt
index 6680145..60ef86f 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt
@@ -1,5 +1,5 @@
# edit "sanity.ansible-doc.in" and generate with: hacking/update-sanity-requirements.py --test ansible-doc
-Jinja2==3.1.2
-MarkupSafe==2.1.3
-packaging==23.2
+Jinja2==3.1.3
+MarkupSafe==2.1.5
+packaging==24.0
PyYAML==6.0.1
diff --git a/test/lib/ansible_test/_data/requirements/sanity.changelog.txt b/test/lib/ansible_test/_data/requirements/sanity.changelog.txt
index d763bad..cc5b635 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.changelog.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.changelog.txt
@@ -1,9 +1,9 @@
# edit "sanity.changelog.in" and generate with: hacking/update-sanity-requirements.py --test changelog
-antsibull-changelog==0.23.0
+antsibull-changelog==0.26.0
docutils==0.18.1
-packaging==23.2
+packaging==24.0
PyYAML==6.0.1
rstcheck==5.0.0
semantic-version==2.10.0
types-docutils==0.18.3
-typing_extensions==4.8.0
+typing_extensions==4.10.0
diff --git a/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt
index 56366b7..9116ed9 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt
@@ -1,4 +1,4 @@
# edit "sanity.import.plugin.in" and generate with: hacking/update-sanity-requirements.py --test import.plugin
-Jinja2==3.1.2
-MarkupSafe==2.1.3
+Jinja2==3.1.3
+MarkupSafe==2.1.5
PyYAML==6.0.1
diff --git a/test/lib/ansible_test/_data/requirements/sanity.mypy.txt b/test/lib/ansible_test/_data/requirements/sanity.mypy.txt
index f6a47fb..651aea8 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.mypy.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.mypy.txt
@@ -1,18 +1,18 @@
# edit "sanity.mypy.in" and generate with: hacking/update-sanity-requirements.py --test mypy
cffi==1.16.0
-cryptography==41.0.4
-Jinja2==3.1.2
-MarkupSafe==2.1.3
-mypy==1.5.1
+cryptography==42.0.5
+Jinja2==3.1.3
+MarkupSafe==2.1.5
+mypy==1.9.0
mypy-extensions==1.0.0
-packaging==23.2
+packaging==24.0
pycparser==2.21
tomli==2.0.1
types-backports==0.1.3
-types-paramiko==3.3.0.0
-types-PyYAML==6.0.12.12
-types-requests==2.31.0.7
-types-setuptools==68.2.0.0
-types-toml==0.10.8.7
-typing_extensions==4.8.0
-urllib3==2.0.6
+types-paramiko==3.4.0.20240311
+types-PyYAML==6.0.12.20240311
+types-requests==2.31.0.20240311
+types-setuptools==69.2.0.20240317
+types-toml==0.10.8.20240310
+typing_extensions==4.10.0
+urllib3==2.2.1
diff --git a/test/lib/ansible_test/_data/requirements/sanity.pep8.txt b/test/lib/ansible_test/_data/requirements/sanity.pep8.txt
index 1a36d4d..51d2b64 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.pep8.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.pep8.txt
@@ -1,2 +1,2 @@
# edit "sanity.pep8.in" and generate with: hacking/update-sanity-requirements.py --test pep8
-pycodestyle==2.11.0
+pycodestyle==2.11.1
diff --git a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt
index c3144fe..b6bdec5 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt
@@ -1,11 +1,11 @@
# edit "sanity.pylint.in" and generate with: hacking/update-sanity-requirements.py --test pylint
-astroid==3.0.0
-dill==0.3.7
-isort==5.12.0
+astroid==3.1.0
+dill==0.3.8
+isort==5.13.2
mccabe==0.7.0
-platformdirs==3.11.0
-pylint==3.0.1
+platformdirs==4.2.0
+pylint==3.1.0
PyYAML==6.0.1
tomli==2.0.1
-tomlkit==0.12.1
-typing_extensions==4.8.0
+tomlkit==0.12.4
+typing_extensions==4.10.0
diff --git a/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt
index 4af9b95..8e6e2ce 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt
@@ -1,3 +1,3 @@
# edit "sanity.runtime-metadata.in" and generate with: hacking/update-sanity-requirements.py --test runtime-metadata
PyYAML==6.0.1
-voluptuous==0.13.1
+voluptuous==0.14.2
diff --git a/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt
index 4e24d64..fba0da1 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt
@@ -1,6 +1,6 @@
# edit "sanity.validate-modules.in" and generate with: hacking/update-sanity-requirements.py --test validate-modules
antsibull-docs-parser==1.0.0
-Jinja2==3.1.2
-MarkupSafe==2.1.3
+Jinja2==3.1.3
+MarkupSafe==2.1.5
PyYAML==6.0.1
-voluptuous==0.13.1
+voluptuous==0.14.2
diff --git a/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt b/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt
index bafd30b..2c91d9e 100644
--- a/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt
+++ b/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt
@@ -1,4 +1,4 @@
# edit "sanity.yamllint.in" and generate with: hacking/update-sanity-requirements.py --test yamllint
-pathspec==0.11.2
+pathspec==0.12.1
PyYAML==6.0.1
-yamllint==1.32.0
+yamllint==1.35.1
diff --git a/test/lib/ansible_test/_internal/bootstrap.py b/test/lib/ansible_test/_internal/bootstrap.py
index b0cfb60..a9dd637 100644
--- a/test/lib/ansible_test/_internal/bootstrap.py
+++ b/test/lib/ansible_test/_internal/bootstrap.py
@@ -28,7 +28,7 @@ class Bootstrap:
"""Base class for bootstrapping systems."""
controller: bool
- python_versions: list[str]
+ python_interpreters: dict[str, str]
ssh_key: SshKey
@property
@@ -41,7 +41,7 @@ class Bootstrap:
return dict(
bootstrap_type=self.bootstrap_type,
controller='yes' if self.controller else '',
- python_versions=self.python_versions,
+ python_interpreters=[f'{key}:{value}' for key, value in self.python_interpreters.items()],
ssh_key_type=self.ssh_key.KEY_TYPE,
ssh_private_key=self.ssh_key.key_contents,
ssh_public_key=self.ssh_key.pub_contents,
diff --git a/test/lib/ansible_test/_internal/classification/__init__.py b/test/lib/ansible_test/_internal/classification/__init__.py
index deda27e..b512284 100644
--- a/test/lib/ansible_test/_internal/classification/__init__.py
+++ b/test/lib/ansible_test/_internal/classification/__init__.py
@@ -674,11 +674,6 @@ class PathMapper:
# Early classification that needs to occur before common classification belongs here.
- if path.startswith('test/units/compat/'):
- return {
- 'units': 'test/units/',
- }
-
if dirname == '.azure-pipelines/commands':
test_map = {
'cloud.sh': 'integration:cloud/',
diff --git a/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py b/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py
index 8060804..ebb2734 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py
@@ -38,7 +38,7 @@ class CsCloudProvider(CloudProvider):
def __init__(self, args: IntegrationConfig) -> None:
super().__init__(args)
- self.image = os.environ.get('ANSIBLE_CLOUDSTACK_CONTAINER', 'quay.io/ansible/cloudstack-test-container:1.6.1')
+ self.image = os.environ.get('ANSIBLE_CLOUDSTACK_CONTAINER', 'quay.io/ansible/cloudstack-test-container:1.7.0')
self.host = ''
self.port = 0
diff --git a/test/lib/ansible_test/_internal/commands/integration/cloud/nios.py b/test/lib/ansible_test/_internal/commands/integration/cloud/nios.py
index 62dd155..876968f 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/nios.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/nios.py
@@ -28,7 +28,7 @@ class NiosProvider(CloudProvider):
#
# It's source source itself resides at:
# https://github.com/ansible/nios-test-container
- DOCKER_IMAGE = 'quay.io/ansible/nios-test-container:2.0.0'
+ DOCKER_IMAGE = 'quay.io/ansible/nios-test-container:3.0.0'
def __init__(self, args: IntegrationConfig) -> None:
super().__init__(args)
diff --git a/test/lib/ansible_test/_internal/commands/sanity/__init__.py b/test/lib/ansible_test/_internal/commands/sanity/__init__.py
index 9b675e4..143fe33 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/__init__.py
@@ -765,11 +765,6 @@ class SanityTest(metaclass=abc.ABCMeta):
return False
@property
- def py2_compat(self) -> bool:
- """True if the test only applies to code that runs on Python 2.x."""
- return False
-
- @property
def supported_python_versions(self) -> t.Optional[tuple[str, ...]]:
"""A tuple of supported Python versions or None if the test does not depend on specific Python versions."""
return CONTROLLER_PYTHON_VERSIONS
@@ -786,23 +781,11 @@ class SanityTest(metaclass=abc.ABCMeta):
def filter_targets_by_version(self, args: SanityConfig, targets: list[TestTarget], python_version: str) -> list[TestTarget]:
"""Return the given list of test targets, filtered to include only those relevant for the test, taking into account the Python version."""
+ del args # args is not used here, but derived classes may make use of it
del python_version # python_version is not used here, but derived classes may make use of it
targets = self.filter_targets(targets)
- if self.py2_compat:
- # This sanity test is a Python 2.x compatibility test.
- content_config = get_content_config(args)
-
- if content_config.py2_support:
- # This collection supports Python 2.x.
- # Filter targets to include only those that require support for remote-only Python versions.
- targets = self.filter_remote_targets(targets)
- else:
- # This collection does not support Python 2.x.
- # There are no targets to test.
- targets = []
-
return targets
@staticmethod
@@ -878,7 +861,6 @@ class SanityCodeSmellTest(SanitySingleVersion):
self.__no_targets: bool = self.config.get('no_targets')
self.__include_directories: bool = self.config.get('include_directories')
self.__include_symlinks: bool = self.config.get('include_symlinks')
- self.__py2_compat: bool = self.config.get('py2_compat', False)
self.__error_code: str | None = self.config.get('error_code', None)
else:
self.output = None
@@ -894,7 +876,6 @@ class SanityCodeSmellTest(SanitySingleVersion):
self.__no_targets = True
self.__include_directories = False
self.__include_symlinks = False
- self.__py2_compat = False
self.__error_code = None
if self.no_targets:
@@ -940,11 +921,6 @@ class SanityCodeSmellTest(SanitySingleVersion):
return self.__include_symlinks
@property
- def py2_compat(self) -> bool:
- """True if the test only applies to code that runs on Python 2.x."""
- return self.__py2_compat
-
- @property
def supported_python_versions(self) -> t.Optional[tuple[str, ...]]:
"""A tuple of supported Python versions or None if the test does not depend on specific Python versions."""
versions = super().supported_python_versions
@@ -1141,10 +1117,8 @@ def create_sanity_virtualenv(
commands = collect_requirements( # create_sanity_virtualenv()
python=python,
controller=True,
- virtualenv=False,
command=None,
ansible=False,
- cryptography=False,
coverage=coverage,
minimize=minimize,
sanity=name,
diff --git a/test/lib/ansible_test/_internal/commands/sanity/import.py b/test/lib/ansible_test/_internal/commands/sanity/import.py
index 36f5241..55b90f7 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/import.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/import.py
@@ -47,11 +47,6 @@ from ...ansible_util import (
ansible_environment,
)
-from ...python_requirements import (
- PipUnavailableError,
- install_requirements,
-)
-
from ...config import (
SanityConfig,
)
@@ -68,10 +63,6 @@ from ...host_configs import (
PythonConfig,
)
-from ...venv import (
- get_virtualenv_version,
-)
-
def _get_module_test(module_restrictions: bool) -> c.Callable[[str], bool]:
"""Create a predicate which tests whether a path can be used by modules or not."""
@@ -109,15 +100,6 @@ class ImportTest(SanityMultipleVersion):
paths = [target.path for target in targets.include]
- if python.version.startswith('2.') and (get_virtualenv_version(args, python.path) or (0,)) < (13,):
- # hack to make sure that virtualenv is available under Python 2.x
- # on Python 3.x we can use the built-in venv
- # version 13+ is required to use the `--no-wheel` option
- try:
- install_requirements(args, python, virtualenv=True, controller=False) # sanity (import)
- except PipUnavailableError as ex:
- display.warning(str(ex))
-
temp_root = os.path.join(ResultType.TMP.path, 'sanity', 'import')
messages = []
diff --git a/test/lib/ansible_test/_internal/commands/sanity/mypy.py b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
index c93474e..0465951 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/mypy.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
@@ -66,7 +66,6 @@ class MypyTest(SanityMultipleVersion):
vendored_paths = (
'lib/ansible/module_utils/six/__init__.py',
'lib/ansible/module_utils/distro/_distro.py',
- 'lib/ansible/module_utils/compat/_selectors2.py',
)
def filter_targets(self, targets: list[TestTarget]) -> list[TestTarget]:
@@ -77,15 +76,6 @@ class MypyTest(SanityMultipleVersion):
or target.path.startswith('test/lib/ansible_test/_util/target/sanity/import/'))]
@property
- def supported_python_versions(self) -> t.Optional[tuple[str, ...]]:
- """A tuple of supported Python versions or None if the test does not depend on specific Python versions."""
- # mypy 0.981 dropped support for Python 2
- # see: https://mypy-lang.blogspot.com/2022/09/mypy-0981-released.html
- # cryptography dropped support for Python 3.5 in version 3.3
- # see: https://cryptography.io/en/latest/changelog/#v3-3
- return tuple(version for version in SUPPORTED_PYTHON_VERSIONS if str_to_version(version) >= (3, 6))
-
- @property
def error_code(self) -> t.Optional[str]:
"""Error code for ansible-test matching the format used by the underlying test program, or None if the program does not use error codes."""
return 'ansible-test'
@@ -95,6 +85,13 @@ class MypyTest(SanityMultipleVersion):
"""True if the test requires PyPI, otherwise False."""
return True
+ @property
+ def supported_python_versions(self) -> t.Optional[tuple[str, ...]]:
+ """A tuple of supported Python versions or None if the test does not depend on specific Python versions."""
+ # Because the version of typeshed mypy use in 1.9 doesn't support 3.7, neither does mypy 1.9.
+ # see: https://mypy-lang.blogspot.com/2024/03/mypy-19-released.html
+ return tuple(version for version in SUPPORTED_PYTHON_VERSIONS if str_to_version(version) >= (3, 8))
+
def test(self, args: SanityConfig, targets: SanityTargets, python: PythonConfig) -> TestResult:
settings = self.load_processor(args, python.version)
diff --git a/test/lib/ansible_test/_internal/commands/units/__init__.py b/test/lib/ansible_test/_internal/commands/units/__init__.py
index 71ce5c4..9427262 100644
--- a/test/lib/ansible_test/_internal/commands/units/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/units/__init__.py
@@ -245,7 +245,7 @@ def command_units(args: UnitsConfig) -> None:
#
# NOTE: This only affects use of pytest-mock.
# Collection unit tests may directly import mock, which will be provided by ansible-test when it installs requirements using pip.
- # Although mock is available for ansible-core unit tests, they should import units.compat.mock instead.
+ # Although mock is available for ansible-core unit tests, they should import unittest.mock instead.
if str_to_version(python.version) < (3, 8):
config_name = 'legacy.ini'
else:
diff --git a/test/lib/ansible_test/_internal/config.py b/test/lib/ansible_test/_internal/config.py
index dbc137b..18d815c 100644
--- a/test/lib/ansible_test/_internal/config.py
+++ b/test/lib/ansible_test/_internal/config.py
@@ -65,7 +65,6 @@ class ContentConfig:
modules: ModulesConfig
python_versions: tuple[str, ...]
- py2_support: bool
class EnvironmentConfig(CommonConfig):
diff --git a/test/lib/ansible_test/_internal/content_config.py b/test/lib/ansible_test/_internal/content_config.py
index 7ac1876..c9c37df 100644
--- a/test/lib/ansible_test/_internal/content_config.py
+++ b/test/lib/ansible_test/_internal/content_config.py
@@ -29,7 +29,6 @@ from .io import (
from .util import (
ApplicationError,
display,
- str_to_version,
)
from .data import (
@@ -75,13 +74,9 @@ def parse_content_config(data: t.Any) -> ContentConfig:
python_versions = tuple(version for version in SUPPORTED_PYTHON_VERSIONS
if version in CONTROLLER_PYTHON_VERSIONS or version in modules.python_versions)
- # True if Python 2.x is supported.
- py2_support = any(version for version in python_versions if str_to_version(version)[0] == 2)
-
return ContentConfig(
modules=modules,
python_versions=python_versions,
- py2_support=py2_support,
)
diff --git a/test/lib/ansible_test/_internal/coverage_util.py b/test/lib/ansible_test/_internal/coverage_util.py
index 3017623..28e66b5 100644
--- a/test/lib/ansible_test/_internal/coverage_util.py
+++ b/test/lib/ansible_test/_internal/coverage_util.py
@@ -71,7 +71,6 @@ COVERAGE_VERSIONS = (
# IMPORTANT: Keep this in sync with the ansible-test.txt requirements file.
CoverageVersion('7.3.2', 7, (3, 8), (3, 12)),
CoverageVersion('6.5.0', 7, (3, 7), (3, 7)),
- CoverageVersion('4.5.4', 0, (2, 6), (3, 6)),
)
"""
This tuple specifies the coverage version to use for Python version ranges.
diff --git a/test/lib/ansible_test/_internal/docker_util.py b/test/lib/ansible_test/_internal/docker_util.py
index 52b9691..97c022c 100644
--- a/test/lib/ansible_test/_internal/docker_util.py
+++ b/test/lib/ansible_test/_internal/docker_util.py
@@ -496,7 +496,7 @@ def get_docker_hostname() -> str:
"""Return the hostname of the Docker service."""
docker_host = os.environ.get('DOCKER_HOST')
- if docker_host and docker_host.startswith('tcp://'):
+ if docker_host and docker_host.startswith(('tcp://', 'ssh://')):
try:
hostname = urllib.parse.urlparse(docker_host)[1].split(':')[0]
display.info('Detected Docker host: %s' % hostname, verbosity=1)
diff --git a/test/lib/ansible_test/_internal/host_profiles.py b/test/lib/ansible_test/_internal/host_profiles.py
index 0981245..39fe7d2 100644
--- a/test/lib/ansible_test/_internal/host_profiles.py
+++ b/test/lib/ansible_test/_internal/host_profiles.py
@@ -958,7 +958,7 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
"""Perform out-of-band setup before delegation."""
bootstrapper = BootstrapDocker(
controller=self.controller,
- python_versions=[self.python.version],
+ python_interpreters={self.python.version: self.python.path},
ssh_key=SshKey(self.args),
)
@@ -1214,8 +1214,9 @@ class PosixRemoteProfile(ControllerHostProfile[PosixRemoteConfig], RemoteProfile
def configure(self) -> None:
"""Perform in-band configuration. Executed before delegation for the controller and after delegation for targets."""
# a target uses a single python version, but a controller may include additional versions for targets running on the controller
- python_versions = [self.python.version] + [target.python.version for target in self.targets if isinstance(target, ControllerConfig)]
- python_versions = sorted_versions(list(set(python_versions)))
+ python_interpreters = {self.python.version: self.python.path}
+ python_interpreters.update({target.python.version: target.python.path for target in self.targets if isinstance(target, ControllerConfig)})
+ python_interpreters = {version: python_interpreters[version] for version in sorted_versions(list(python_interpreters.keys()))}
core_ci = self.wait_for_instance()
pwd = self.wait_until_ready()
@@ -1226,7 +1227,7 @@ class PosixRemoteProfile(ControllerHostProfile[PosixRemoteConfig], RemoteProfile
controller=self.controller,
platform=self.config.platform,
platform_version=self.config.version,
- python_versions=python_versions,
+ python_interpreters=python_interpreters,
ssh_key=core_ci.ssh_key,
)
diff --git a/test/lib/ansible_test/_internal/python_requirements.py b/test/lib/ansible_test/_internal/python_requirements.py
index 81006e4..404f2cd 100644
--- a/test/lib/ansible_test/_internal/python_requirements.py
+++ b/test/lib/ansible_test/_internal/python_requirements.py
@@ -5,7 +5,6 @@ import base64
import dataclasses
import json
import os
-import re
import typing as t
from .encoding import (
@@ -20,14 +19,9 @@ from .io import (
from .util import (
ANSIBLE_TEST_DATA_ROOT,
ANSIBLE_TEST_TARGET_ROOT,
- ANSIBLE_TEST_TOOLS_ROOT,
ApplicationError,
SubprocessError,
display,
- find_executable,
- raw_command,
- str_to_version,
- version_to_str,
)
from .util_common import (
@@ -63,9 +57,6 @@ from .coverage_util import (
QUIET_PIP_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'quiet_pip.py')
REQUIREMENTS_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'requirements.py')
-# IMPORTANT: Keep this in sync with the ansible-test.txt requirements file.
-VIRTUALENV_VERSION = '16.7.12'
-
# Pip Abstraction
@@ -132,7 +123,6 @@ def install_requirements(
ansible: bool = False,
command: bool = False,
coverage: bool = False,
- virtualenv: bool = False,
controller: bool = True,
connection: t.Optional[Connection] = None,
) -> None:
@@ -145,8 +135,6 @@ def install_requirements(
if command and isinstance(args, (UnitsConfig, IntegrationConfig)) and args.coverage:
coverage = True
- cryptography = False
-
if ansible:
try:
ansible_cache = install_requirements.ansible_cache # type: ignore[attr-defined]
@@ -160,19 +148,12 @@ def install_requirements(
else:
ansible_cache[python.path] = True
- # Install the latest cryptography version that the current requirements can support if it is not already available.
- # This avoids downgrading cryptography when OS packages provide a newer version than we are able to install using pip.
- # If not installed here, later install commands may try to install a version of cryptography which cannot be installed.
- cryptography = not is_cryptography_available(python.path)
-
commands = collect_requirements(
python=python,
controller=controller,
ansible=ansible,
- cryptography=cryptography,
command=args.command if command else None,
coverage=coverage,
- virtualenv=virtualenv,
minimize=False,
sanity=None,
)
@@ -205,9 +186,7 @@ def collect_requirements(
python: PythonConfig,
controller: bool,
ansible: bool,
- cryptography: bool,
coverage: bool,
- virtualenv: bool,
minimize: bool,
command: t.Optional[str],
sanity: t.Optional[str],
@@ -215,17 +194,9 @@ def collect_requirements(
"""Collect requirements for the given Python using the specified arguments."""
commands: list[PipCommand] = []
- if virtualenv:
- # sanity tests on Python 2.x install virtualenv when it is too old or is not already installed and the `--requirements` option is given
- # the last version of virtualenv with no dependencies is used to minimize the changes made outside a virtual environment
- commands.extend(collect_package_install(packages=[f'virtualenv=={VIRTUALENV_VERSION}'], constraints=False))
-
if coverage:
commands.extend(collect_package_install(packages=[f'coverage=={get_coverage_version(python.version).coverage_version}'], constraints=False))
- if cryptography:
- commands.extend(collect_package_install(packages=get_cryptography_requirements(python)))
-
if ansible or command:
commands.extend(collect_general_install(command, ansible))
@@ -446,17 +417,7 @@ def get_venv_packages(python: PythonConfig) -> dict[str, str]:
wheel='0.37.1',
)
- override_packages = {
- '2.7': dict(
- pip='20.3.4', # 21.0 requires Python 3.6+
- setuptools='44.1.1', # 45.0.0 requires Python 3.5+
- wheel=None,
- ),
- '3.6': dict(
- pip='21.3.1', # 22.0 requires Python 3.7+
- setuptools='59.6.0', # 59.7.0 requires Python 3.7+
- wheel=None,
- ),
+ override_packages: dict[str, dict[str, str]] = {
}
packages = {name: version or default_packages[name] for name, version in override_packages.get(python.version, default_packages).items()}
@@ -510,82 +471,3 @@ def prepare_pip_script(commands: list[PipCommand]) -> str:
def usable_pip_file(path: t.Optional[str]) -> bool:
"""Return True if the specified pip file is usable, otherwise False."""
return bool(path) and os.path.exists(path) and bool(os.path.getsize(path))
-
-
-# Cryptography
-
-
-def is_cryptography_available(python: str) -> bool:
- """Return True if cryptography is available for the given python."""
- try:
- raw_command([python, '-c', 'import cryptography'], capture=True)
- except SubprocessError:
- return False
-
- return True
-
-
-def get_cryptography_requirements(python: PythonConfig) -> list[str]:
- """
- Return the correct cryptography and pyopenssl requirements for the given python version.
- The version of cryptography installed depends on the python version and openssl version.
- """
- openssl_version = get_openssl_version(python)
-
- if openssl_version and openssl_version < (1, 1, 0):
- # cryptography 3.2 requires openssl 1.1.x or later
- # see https://cryptography.io/en/latest/changelog.html#v3-2
- cryptography = 'cryptography < 3.2'
- # pyopenssl 20.0.0 requires cryptography 3.2 or later
- pyopenssl = 'pyopenssl < 20.0.0'
- else:
- # cryptography 3.4+ builds require a working rust toolchain
- # systems bootstrapped using ansible-core-ci can access additional wheels through the spare-tire package index
- cryptography = 'cryptography'
- # any future installation of pyopenssl is free to use any compatible version of cryptography
- pyopenssl = ''
-
- requirements = [
- cryptography,
- pyopenssl,
- ]
-
- requirements = [requirement for requirement in requirements if requirement]
-
- return requirements
-
-
-def get_openssl_version(python: PythonConfig) -> t.Optional[tuple[int, ...]]:
- """Return the openssl version."""
- if not python.version.startswith('2.'):
- # OpenSSL version checking only works on Python 3.x.
- # This should be the most accurate, since it is the Python we will be using.
- version = json.loads(raw_command([python.path, os.path.join(ANSIBLE_TEST_TOOLS_ROOT, 'sslcheck.py')], capture=True)[0])['version']
-
- if version:
- display.info(f'Detected OpenSSL version {version_to_str(version)} under Python {python.version}.', verbosity=1)
-
- return tuple(version)
-
- # Fall back to detecting the OpenSSL version from the CLI.
- # This should provide an adequate solution on Python 2.x.
- openssl_path = find_executable('openssl', required=False)
-
- if openssl_path:
- try:
- result = raw_command([openssl_path, 'version'], capture=True)[0]
- except SubprocessError:
- result = ''
-
- match = re.search(r'^OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+)', result)
-
- if match:
- version = str_to_version(match.group('version'))
-
- display.info(f'Detected OpenSSL version {version_to_str(version)} using the openssl CLI.', verbosity=1)
-
- return version
-
- display.info('Unable to detect OpenSSL version.', verbosity=1)
-
- return None
diff --git a/test/lib/ansible_test/_internal/ssh.py b/test/lib/ansible_test/_internal/ssh.py
index b2a2678..8125768 100644
--- a/test/lib/ansible_test/_internal/ssh.py
+++ b/test/lib/ansible_test/_internal/ssh.py
@@ -245,6 +245,7 @@ def create_ssh_port_forwards(
"""
options: dict[str, t.Union[str, int]] = dict(
LogLevel='INFO', # info level required to get messages on stderr indicating the ports assigned to each forward
+ ControlPath='none', # if the user has ControlPath set up for every host, it will prevent creation of forwards
)
cli_args = []
diff --git a/test/lib/ansible_test/_internal/util.py b/test/lib/ansible_test/_internal/util.py
index 394c263..903cbcc 100644
--- a/test/lib/ansible_test/_internal/util.py
+++ b/test/lib/ansible_test/_internal/util.py
@@ -998,7 +998,7 @@ def retry(func: t.Callable[..., TValue], ex_type: t.Type[BaseException] = Subpro
def parse_to_list_of_dict(pattern: str, value: str) -> list[dict[str, str]]:
"""Parse lines from the given value using the specified pattern and return the extracted list of key/value pair dictionaries."""
matched = []
- unmatched = []
+ unmatched: list[str] = []
for line in value.splitlines():
match = re.search(pattern, line)
diff --git a/test/lib/ansible_test/_internal/util_common.py b/test/lib/ansible_test/_internal/util_common.py
index 77a6165..a690497 100644
--- a/test/lib/ansible_test/_internal/util_common.py
+++ b/test/lib/ansible_test/_internal/util_common.py
@@ -409,7 +409,7 @@ def create_interpreter_wrapper(interpreter: str, injected_interpreter: str) -> N
code = textwrap.dedent('''
#!%s
- from __future__ import absolute_import
+ from __future__ import annotations
from os import execv
from sys import argv
diff --git a/test/lib/ansible_test/_internal/venv.py b/test/lib/ansible_test/_internal/venv.py
index a83fc8b..cdd73b0 100644
--- a/test/lib/ansible_test/_internal/venv.py
+++ b/test/lib/ansible_test/_internal/venv.py
@@ -15,7 +15,6 @@ from .config import (
from .util import (
find_python,
SubprocessError,
- get_available_python_versions,
ANSIBLE_TEST_TARGET_TOOLS_ROOT,
display,
remove_tree,
@@ -85,49 +84,21 @@ def create_virtual_environment(
system_site_packages: bool = False,
pip: bool = False,
) -> bool:
- """Create a virtual environment using venv or virtualenv for the requested Python version."""
+ """Create a virtual environment using venv for the requested Python version."""
if not os.path.exists(python.path):
# the requested python version could not be found
return False
- if str_to_version(python.version) >= (3, 0):
- # use the built-in 'venv' module on Python 3.x
- # creating a virtual environment using 'venv' when running in a virtual environment created by 'virtualenv' results
- # in a copy of the original virtual environment instead of creation of a new one
- # avoid this issue by only using "real" python interpreters to invoke 'venv'
- for real_python in iterate_real_pythons(python.version):
- if run_venv(args, real_python, system_site_packages, pip, path):
- display.info('Created Python %s virtual environment using "venv": %s' % (python.version, path), verbosity=1)
- return True
-
- # something went wrong, most likely the package maintainer for the Python installation removed ensurepip
- # which will prevent creation of a virtual environment without installation of other OS packages
-
- # use the installed 'virtualenv' module on the Python requested version
- if run_virtualenv(args, python.path, python.path, system_site_packages, pip, path):
- display.info('Created Python %s virtual environment using "virtualenv": %s' % (python.version, path), verbosity=1)
- return True
-
- available_pythons = get_available_python_versions()
-
- for available_python_version, available_python_interpreter in sorted(available_pythons.items()):
- if available_python_interpreter == python.path:
- # already attempted to use this interpreter
- continue
-
- virtualenv_version = get_virtualenv_version(args, available_python_interpreter)
-
- if not virtualenv_version:
- # virtualenv not available for this Python or we were unable to detect the version
- continue
-
- # try using 'virtualenv' from another Python to setup the desired version
- if run_virtualenv(args, available_python_interpreter, python.path, system_site_packages, pip, path):
- display.info('Created Python %s virtual environment using "virtualenv" on Python %s: %s' % (python.version, available_python_version, path),
- verbosity=1)
+ # creating a virtual environment using 'venv' when running in a virtual environment created by 'virtualenv' results
+ # in a copy of the original virtual environment instead of creation of a new one
+ # avoid this issue by only using "real" python interpreters to invoke 'venv'
+ for real_python in iterate_real_pythons(python.version):
+ if run_venv(args, real_python, system_site_packages, pip, path):
+ display.info('Created Python %s virtual environment using "venv": %s' % (python.version, path), verbosity=1)
return True
- # no suitable 'virtualenv' available
+ # something went wrong, most likely the package maintainer for the Python installation removed ensurepip
+ # which will prevent creation of a virtual environment without installation of other OS packages
return False
@@ -188,7 +159,7 @@ def run_venv(
pip: bool,
path: str,
) -> bool:
- """Create a virtual environment using the 'venv' module. Not available on Python 2.x."""
+ """Create a virtual environment using the 'venv' module."""
cmd = [run_python, '-m', 'venv']
if system_site_packages:
@@ -210,72 +181,3 @@ def run_venv(
return False
return True
-
-
-def run_virtualenv(
- args: EnvironmentConfig,
- run_python: str,
- env_python: str,
- system_site_packages: bool,
- pip: bool,
- path: str,
-) -> bool:
- """Create a virtual environment using the 'virtualenv' module."""
- # always specify which interpreter to use to guarantee the desired interpreter is provided
- # otherwise virtualenv may select a different interpreter than the one running virtualenv
- cmd = [run_python, '-m', 'virtualenv', '--python', env_python]
-
- if system_site_packages:
- cmd.append('--system-site-packages')
-
- if not pip:
- cmd.append('--no-pip')
- # these options provide consistency with venv, which does not install them without pip
- cmd.append('--no-setuptools')
- cmd.append('--no-wheel')
-
- cmd.append(path)
-
- try:
- run_command(args, cmd, capture=True)
- except SubprocessError as ex:
- remove_tree(path)
-
- if args.verbosity > 1:
- display.error(ex.message)
-
- return False
-
- return True
-
-
-def get_virtualenv_version(args: EnvironmentConfig, python: str) -> t.Optional[tuple[int, ...]]:
- """Get the virtualenv version for the given python interpreter, if available, otherwise return None."""
- try:
- cache = get_virtualenv_version.cache # type: ignore[attr-defined]
- except AttributeError:
- cache = get_virtualenv_version.cache = {} # type: ignore[attr-defined]
-
- if python not in cache:
- try:
- stdout = run_command(args, [python, '-m', 'virtualenv', '--version'], capture=True)[0]
- except SubprocessError as ex:
- stdout = ''
-
- if args.verbosity > 1:
- display.error(ex.message)
-
- version = None
-
- if stdout:
- # noinspection PyBroadException
- try:
- version = str_to_version(stdout.strip())
- except Exception: # pylint: disable=broad-except
- pass
-
- cache[python] = version
-
- version = cache[python]
-
- return version
diff --git a/test/lib/ansible_test/_util/__init__.py b/test/lib/ansible_test/_util/__init__.py
index 527d413..8f58623 100644
--- a/test/lib/ansible_test/_util/__init__.py
+++ b/test/lib/ansible_test/_util/__init__.py
@@ -1,2 +1 @@
-# Empty __init__.py to allow importing of `ansible_test._util.target.common` under Python 2.x.
-# This allows the ansible-test entry point to report supported Python versions before exiting.
+# Empty __init__.py to keep pylint happy.
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.json
deleted file mode 100644
index 4ebce32..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "py2_compat": true,
- "output": "path-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.py
deleted file mode 100644
index 7b39c37..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""Enforce proper usage of __future__ imports."""
-from __future__ import annotations
-
-import ast
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'rb') as path_fd:
- lines = path_fd.read().splitlines()
-
- missing = True
- if not lines:
- # Files are allowed to be empty of everything including boilerplate
- missing = False
-
- for text in lines:
- if text in (b'from __future__ import (absolute_import, division, print_function)',
- b'from __future__ import absolute_import, division, print_function'):
- missing = False
- break
-
- if missing:
- with open(path, encoding='utf-8') as file:
- contents = file.read()
-
- # noinspection PyBroadException
- try:
- node = ast.parse(contents)
-
- # files consisting of only assignments have no need for future import boilerplate
- # the only exception would be division during assignment, but we'll overlook that for simplicity
- # the most likely case is that of a documentation only python file
- if all(isinstance(statement, ast.Assign) for statement in node.body):
- missing = False
- except Exception: # pylint: disable=broad-except
- pass # the compile sanity test will report this error
-
- if missing:
- print('%s: missing: from __future__ import (absolute_import, division, print_function)' % path)
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.json
deleted file mode 100644
index 4ebce32..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "py2_compat": true,
- "output": "path-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.py
deleted file mode 100644
index 8bdcfc9..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""Require __metaclass__ boilerplate for code that supports Python 2.x."""
-from __future__ import annotations
-
-import ast
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'rb') as path_fd:
- lines = path_fd.read().splitlines()
-
- missing = True
- if not lines:
- # Files are allowed to be empty of everything including boilerplate
- missing = False
-
- for text in lines:
- if text == b'__metaclass__ = type':
- missing = False
- break
-
- if missing:
- with open(path, encoding='utf-8') as file:
- contents = file.read()
-
- # noinspection PyBroadException
- try:
- node = ast.parse(contents)
-
- # files consisting of only assignments have no need for metaclass boilerplate
- # the most likely case is that of a documentation only python file
- if all(isinstance(statement, ast.Assign) for statement in node.body):
- missing = False
- except Exception: # pylint: disable=broad-except
- pass # the compile sanity test will report this error
-
- if missing:
- print('%s: missing: __metaclass__ = type' % path)
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.json
deleted file mode 100644
index 88858ae..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "ignore_self": true,
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.py
deleted file mode 100644
index 74e38d7..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-basestring.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow use of basestring isinstance checks."""
-from __future__ import annotations
-
-import re
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as path_fd:
- for line, text in enumerate(path_fd.readlines()):
- match = re.search(r'(isinstance.*basestring)', text)
-
- if match:
- print('%s:%d:%d: do not use `isinstance(s, basestring)`' % (
- path, line + 1, match.start(1) + 1))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.json
deleted file mode 100644
index 88858ae..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "ignore_self": true,
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.py
deleted file mode 100644
index b4e4002..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow use of the dict.iteritems function."""
-from __future__ import annotations
-
-import re
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as path_fd:
- for line, text in enumerate(path_fd.readlines()):
- match = re.search(r'(?<! six)\.(iteritems)', text)
-
- if match:
- print('%s:%d:%d: use `dict.items` or `ansible.module_utils.six.iteritems` instead of `dict.iteritems`' % (
- path, line + 1, match.start(1) + 1))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.json
deleted file mode 100644
index 88858ae..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "ignore_self": true,
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.py
deleted file mode 100644
index 00c8703..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow use of the dict.iterkeys function."""
-from __future__ import annotations
-
-import re
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as path_fd:
- for line, text in enumerate(path_fd.readlines()):
- match = re.search(r'\.(iterkeys)', text)
-
- if match:
- print('%s:%d:%d: use `dict.keys` or `for key in dict:` instead of `dict.iterkeys`' % (
- path, line + 1, match.start(1) + 1))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.json
deleted file mode 100644
index 88858ae..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "ignore_self": true,
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.py
deleted file mode 100644
index 2e8036a..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow use of the dict.itervalues function."""
-from __future__ import annotations
-
-import re
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as path_fd:
- for line, text in enumerate(path_fd.readlines()):
- match = re.search(r'(?<! six)\.(itervalues)', text)
-
- if match:
- print('%s:%d:%d: use `dict.values` or `ansible.module_utils.six.itervalues` instead of `dict.itervalues`' % (
- path, line + 1, match.start(1) + 1))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.json
deleted file mode 100644
index ccee80a..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "prefixes": [
- "lib/ansible/",
- "plugins/"
- ],
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.py
deleted file mode 100644
index eb5987d..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-main-display.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow importing display from __main__."""
-from __future__ import annotations
-
-import sys
-
-MAIN_DISPLAY_IMPORT = 'from __main__ import display'
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as file:
- for i, line in enumerate(file.readlines()):
- if MAIN_DISPLAY_IMPORT in line:
- lineno = i + 1
- colno = line.index(MAIN_DISPLAY_IMPORT) + 1
- print('%s:%d:%d: Display is a singleton, just import and instantiate' % (path, lineno, colno))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.json b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.json
deleted file mode 100644
index 88858ae..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extensions": [
- ".py"
- ],
- "ignore_self": true,
- "output": "path-line-column-message"
-}
diff --git a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.py b/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.py
deleted file mode 100644
index 75c34f2..0000000
--- a/test/lib/ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Disallow use of the unicode_literals future."""
-from __future__ import annotations
-
-import re
-import sys
-
-
-def main():
- """Main entry point."""
- for path in sys.argv[1:] or sys.stdin.read().splitlines():
- with open(path, 'r', encoding='utf-8') as path_fd:
- for line, text in enumerate(path_fd.readlines()):
- match = re.search(r'(unicode_literals)', text)
-
- if match:
- print('%s:%d:%d: do not use `unicode_literals`' % (
- path, line + 1, match.start(1) + 1))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
index 41d824b..0251f67 100644
--- a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
+++ b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
@@ -46,9 +46,6 @@ ignore_missing_imports = True
[mypy-lxml.*]
ignore_missing_imports = True
-[mypy-yum.*]
-ignore_missing_imports = True
-
[mypy-rpmUtils.*]
ignore_missing_imports = True
@@ -85,9 +82,6 @@ ignore_missing_imports = True
[mypy-distro.*]
ignore_missing_imports = True
-[mypy-selectors2.*]
-ignore_missing_imports = True
-
[mypy-resolvelib.*]
ignore_missing_imports = True
diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
index d6a608f..b4e7b05 100644
--- a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
+++ b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
@@ -13,9 +13,6 @@ ignore_missing_imports = True
[mypy-md5.*]
ignore_missing_imports = True
-[mypy-yum.*]
-ignore_missing_imports = True
-
[mypy-rpmUtils.*]
ignore_missing_imports = True
@@ -52,9 +49,6 @@ ignore_missing_imports = True
[mypy-distro.*]
ignore_missing_imports = True
-[mypy-selectors2.*]
-ignore_missing_imports = True
-
[mypy-selinux.*]
ignore_missing_imports = True
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg b/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg
index f8a0a8a..51d0bb0 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg
@@ -35,7 +35,6 @@ bad-names=
tutu,
good-names=
- __metaclass__,
C,
ex,
i,
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg b/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg
index 5bec36f..801adbe 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg
@@ -34,7 +34,6 @@ bad-names=
tutu,
good-names=
- __metaclass__,
C,
ex,
i,
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg b/test/lib/ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg
index c30eb37..adc0b95 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg
@@ -33,7 +33,6 @@ bad-names=
tutu,
good-names=
- __metaclass__,
C,
ex,
i,
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/config/collection.cfg b/test/lib/ansible_test/_util/controller/sanity/pylint/config/collection.cfg
index 762d488..bb6c3e4 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/config/collection.cfg
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/config/collection.cfg
@@ -102,7 +102,6 @@ disable=
undefined-loop-variable,
unexpected-keyword-arg,
ungrouped-imports,
- unidiomatic-typecheck,
unnecessary-pass,
unnecessary-dunder-call,
unsubscriptable-object,
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/config/default.cfg b/test/lib/ansible_test/_util/controller/sanity/pylint/config/default.cfg
index 825e5df..6264948 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/config/default.cfg
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/config/default.cfg
@@ -95,7 +95,6 @@ disable=
undefined-loop-variable,
unexpected-keyword-arg,
ungrouped-imports,
- unidiomatic-typecheck,
unnecessary-pass,
unsubscriptable-object,
unsupported-assignment-operation,
diff --git a/test/lib/ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py b/test/lib/ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py
index f6c8337..be4dba5 100644
--- a/test/lib/ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py
+++ b/test/lib/ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py
@@ -32,7 +32,6 @@ except ImportError:
from pylint.checkers import BaseChecker, BaseTokenChecker
from ansible.module_utils.compat.version import LooseVersion
-from ansible.module_utils.six import string_types
from ansible.release import __version__ as ansible_version_raw
from ansible.utils.version import SemanticVersion
@@ -137,7 +136,7 @@ def _get_func_name(node):
def parse_isodate(value):
"""Parse an ISO 8601 date string."""
msg = 'Expected ISO 8601 date string (YYYY-MM-DD)'
- if not isinstance(value, string_types):
+ if not isinstance(value, str):
raise ValueError(msg)
# From Python 3.7 in, there is datetime.date.fromisoformat(). For older versions,
# we have to do things manually.
diff --git a/test/lib/ansible_test/_util/controller/sanity/shellcheck/exclude.txt b/test/lib/ansible_test/_util/controller/sanity/shellcheck/exclude.txt
index 29588dd..6563f8f 100644
--- a/test/lib/ansible_test/_util/controller/sanity/shellcheck/exclude.txt
+++ b/test/lib/ansible_test/_util/controller/sanity/shellcheck/exclude.txt
@@ -1,3 +1,2 @@
SC1090
SC1091
-SC2164
diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
index 2b92a56..5e3a07e 100644
--- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
+++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
@@ -70,13 +70,12 @@ from ansible.module_utils.common.collections import is_iterable
from ansible.module_utils.common.parameters import DEFAULT_TYPE_VALIDATORS
from ansible.module_utils.compat.version import StrictVersion, LooseVersion
from ansible.module_utils.basic import to_bytes
-from ansible.module_utils.six import PY3, with_metaclass, string_types
from ansible.plugins.loader import fragment_loader
from ansible.plugins.list import IGNORE as REJECTLIST
from ansible.utils.plugin_docs import add_collection_to_versions_and_dates, add_fragments, get_docstring
from ansible.utils.version import SemanticVersion
-from .module_args import AnsibleModuleImportError, AnsibleModuleNotInitialized, get_argument_spec
+from .module_args import AnsibleModuleImportError, AnsibleModuleNotInitialized, get_py_argument_spec, get_ps_argument_spec
from .schema import (
ansible_module_kwargs_schema,
@@ -87,18 +86,14 @@ from .schema import (
from .utils import CaptureStd, NoArgsAnsibleModule, compare_unordered_lists, parse_yaml, parse_isodate
-if PY3:
- # Because there is no ast.TryExcept in Python 3 ast module
- TRY_EXCEPT = ast.Try
- # REPLACER_WINDOWS from ansible.executor.module_common is byte
- # string but we need unicode for Python 3
- REPLACER_WINDOWS = REPLACER_WINDOWS.decode('utf-8')
-else:
- TRY_EXCEPT = ast.TryExcept
+# Because there is no ast.TryExcept in Python 3 ast module
+TRY_EXCEPT = ast.Try
+# REPLACER_WINDOWS from ansible.executor.module_common is byte
+# string but we need unicode for Python 3
+REPLACER_WINDOWS = REPLACER_WINDOWS.decode('utf-8')
REJECTLIST_DIRS = frozenset(('.git', 'test', '.github', '.idea'))
INDENT_REGEX = re.compile(r'([\t]*)')
-TYPE_REGEX = re.compile(r'.*(if|or)(\s+[^"\']*|\s+)(?<!_)(?<!str\()type\([^)].*')
SYS_EXIT_REGEX = re.compile(r'[^#]*sys.exit\s*\(.*')
NO_LOG_REGEX = re.compile(r'(?:pass(?!ive)|secret|token|key)', re.I)
@@ -269,7 +264,7 @@ class Reporter:
return 3 if sum(ret) else 0
-class Validator(with_metaclass(abc.ABCMeta, object)):
+class Validator(metaclass=abc.ABCMeta):
"""Validator instances are intended to be run on a single object. if you
are scanning multiple objects for problems, you'll want to have a separate
Validator for each one."""
@@ -335,8 +330,6 @@ class ModuleValidator(Validator):
self.git_cache = git_cache
self.base_module = self.git_cache.get_original_path(self.path)
- self._python_module_override = False
-
with open(path) as f:
self.text = f.read()
self.length = len(self.text.splitlines())
@@ -383,7 +376,7 @@ class ModuleValidator(Validator):
pass
def _python_module(self):
- if self.path.endswith('.py') or self._python_module_override:
+ if self.path.endswith('.py'):
return True
return False
@@ -421,7 +414,7 @@ class ModuleValidator(Validator):
return self.git_cache.is_new(self.path)
def _check_interpreter(self, powershell=False):
- if powershell:
+ if self._powershell_module():
if not self.text.startswith('#!powershell\n'):
self.reporter.error(
path=self.object_path,
@@ -430,34 +423,20 @@ class ModuleValidator(Validator):
)
return
- missing_python_interpreter = False
-
- if not self.text.startswith('#!/usr/bin/python'):
- if NEW_STYLE_PYTHON_MODULE_RE.search(to_bytes(self.text)):
- missing_python_interpreter = self.text.startswith('#!') # shebang optional, but if present must match
- else:
- missing_python_interpreter = True # shebang required
+ if self._python_module():
+ missing_python_interpreter = False
- if missing_python_interpreter:
- self.reporter.error(
- path=self.object_path,
- code='missing-python-interpreter',
- msg='Interpreter line is not "#!/usr/bin/python"',
- )
+ if not self.text.startswith('#!/usr/bin/python'):
+ if NEW_STYLE_PYTHON_MODULE_RE.search(to_bytes(self.text)):
+ missing_python_interpreter = self.text.startswith('#!') # shebang optional, but if present must match
+ else:
+ missing_python_interpreter = True # shebang required
- def _check_type_instead_of_isinstance(self, powershell=False):
- if powershell:
- return
- for line_no, line in enumerate(self.text.splitlines()):
- typekeyword = TYPE_REGEX.match(line)
- if typekeyword:
- # TODO: add column
+ if missing_python_interpreter:
self.reporter.error(
path=self.object_path,
- code='unidiomatic-typecheck',
- msg=('Type comparison using type() found. '
- 'Use isinstance() instead'),
- line=line_no + 1
+ code='missing-python-interpreter',
+ msg='Interpreter line is not "#!/usr/bin/python"',
)
def _check_for_sys_exit(self):
@@ -1120,14 +1099,6 @@ class ModuleValidator(Validator):
' documentation for removed'
)
else:
- # We are testing a collection
- if self.object_name.startswith('_'):
- self.reporter.error(
- path=self.object_path,
- code='collections-no-underscore-on-deprecation',
- msg='Deprecated content in collections MUST NOT start with "_", update meta/runtime.yml instead',
- )
-
if not (doc_deprecated == routing_says_deprecated):
# DOCUMENTATION.deprecated and meta/runtime.yml disagree
self.reporter.error(
@@ -1193,7 +1164,7 @@ class ModuleValidator(Validator):
for entry in object:
self._validate_semantic_markup(entry)
return
- if not isinstance(object, string_types):
+ if not isinstance(object, str):
return
if self.collection:
@@ -1312,7 +1283,12 @@ class ModuleValidator(Validator):
def _validate_ansible_module_call(self, docs):
try:
- spec, kwargs = get_argument_spec(self.path, self.collection)
+ if self._python_module():
+ spec, kwargs = get_py_argument_spec(self.path, self.collection)
+ elif self._powershell_module():
+ spec, kwargs = get_ps_argument_spec(self.path, self.collection)
+ else:
+ raise NotImplementedError()
except AnsibleModuleNotInitialized:
self.reporter.error(
path=self.object_path,
@@ -1374,7 +1350,7 @@ class ModuleValidator(Validator):
continue
bad_term = False
for term in check:
- if not isinstance(term, string_types):
+ if not isinstance(term, str):
msg = name
if context:
msg += " found in %s" % " -> ".join(context)
@@ -1442,7 +1418,7 @@ class ModuleValidator(Validator):
continue
bad_term = False
for term in requirements:
- if not isinstance(term, string_types):
+ if not isinstance(term, str):
msg = "required_if"
if context:
msg += " found in %s" % " -> ".join(context)
@@ -1525,13 +1501,13 @@ class ModuleValidator(Validator):
# This is already reported by schema checking
return
for key, value in terms.items():
- if isinstance(value, string_types):
+ if isinstance(value, str):
value = [value]
if not isinstance(value, (list, tuple)):
# This is already reported by schema checking
continue
for term in value:
- if not isinstance(term, string_types):
+ if not isinstance(term, str):
# This is already reported by schema checking
continue
if len(set(value)) != len(value) or key in value:
@@ -2268,7 +2244,6 @@ class ModuleValidator(Validator):
'extension for python modules or a .ps1 '
'for powershell modules')
)
- self._python_module_override = True
if self._python_module() and self.ast is None:
self.reporter.error(
@@ -2380,10 +2355,7 @@ class ModuleValidator(Validator):
self._check_gpl3_header()
if not self._just_docs() and not self._sidecar_doc() and not end_of_deprecation_should_be_removed_only:
if self.plugin_type == 'module':
- self._check_interpreter(powershell=self._powershell_module())
- self._check_type_instead_of_isinstance(
- powershell=self._powershell_module()
- )
+ self._check_interpreter()
class PythonPackageValidator(Validator):
diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py
index 1b71217..bff9306 100644
--- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py
+++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py
@@ -167,10 +167,3 @@ def get_py_argument_spec(filename, collection):
return argument_spec, fake.kwargs
except (TypeError, IndexError):
return {}, {}
-
-
-def get_argument_spec(filename, collection):
- if filename.endswith('.py'):
- return get_py_argument_spec(filename, collection)
- else:
- return get_ps_argument_spec(filename, collection)
diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
index a6068c6..ba4e188 100644
--- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
+++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
@@ -297,6 +297,7 @@ def argument_spec_schema(for_collection):
[is_callable, list_string_types],
),
'choices': Any([object], (object,)),
+ 'context': dict,
'required': bool,
'no_log': bool,
'aliases': Any(list_string_types, tuple(list_string_types)),
@@ -487,10 +488,17 @@ def check_option_choices(v):
type_checker, type_name = get_type_checker({'type': v.get('elements')})
else:
type_checker, type_name = get_type_checker(v)
+
if type_checker is None:
return v
- for value in v_choices:
+ if isinstance(v_choices, dict):
+ # choices are still a list (the keys) but dict form serves to document each choice.
+ iterate = v_choices.keys()
+ else:
+ iterate = v_choices
+
+ for value in iterate:
try:
type_checker(value)
except Exception as exc:
@@ -542,7 +550,7 @@ def list_dict_option_schema(for_collection, plugin_type):
basic_option_schema = {
Required('description'): doc_string_or_strings,
'required': bool,
- 'choices': list,
+ 'choices': Any(list, {object: doc_string_or_strings}),
'aliases': Any(list_string_types),
'version_added': version(for_collection),
'version_added_collection': collection_name,
diff --git a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py
index 15cb703..84bab28 100644
--- a/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py
+++ b/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py
@@ -192,7 +192,7 @@ def compare_unordered_lists(a, b):
- unordered lists
- unhashable elements
"""
- return len(a) == len(b) and all(x in b for x in a)
+ return len(a) == len(b) and all(x in b for x in a) and all(x in a for x in b)
class NoArgsAnsibleModule(AnsibleModule):
diff --git a/test/lib/ansible_test/_util/controller/sanity/yamllint/yamllinter.py b/test/lib/ansible_test/_util/controller/sanity/yamllint/yamllinter.py
index ed1afcf..0c39fc7 100644
--- a/test/lib/ansible_test/_util/controller/sanity/yamllint/yamllinter.py
+++ b/test/lib/ansible_test/_util/controller/sanity/yamllint/yamllinter.py
@@ -126,19 +126,31 @@ class YamlChecker:
yaml_data = yaml_data[1:]
lineno += 1
- self.check_parsable(path, yaml_data, lineno)
+ multiple_docs_allowed = [
+ "EXAMPLES",
+ ]
+ self.check_parsable(path, yaml_data, lineno, (key in multiple_docs_allowed), key)
messages = list(linter.run(yaml_data, conf, path))
self.messages += [self.result_to_message(r, path, lineno - 1, key) for r in messages]
- def check_parsable(self, path, contents, lineno=1): # type: (str, str, int) -> None
+ def check_parsable(self, path, contents, lineno=1, allow_multiple=False, prefix=""): # type: (str, str, int, bool) -> None
"""Check the given contents to verify they can be parsed as YAML."""
+ prefix = f"{prefix}: " if prefix else ""
try:
- yaml.load(contents, Loader=TestLoader)
+ documents = len(list(yaml.load_all(contents, Loader=TestLoader)))
+ if documents > 1 and not allow_multiple:
+ self.messages += [{'code': 'multiple-yaml-documents',
+ 'message': f'{prefix}expected a single document in the stream',
+ 'path': path,
+ 'line': lineno,
+ 'column': 1,
+ 'level': 'error',
+ }]
except MarkedYAMLError as ex:
self.messages += [{'code': 'unparsable-with-libyaml',
- 'message': '%s - %s' % (ex.args[0], ex.args[2]),
+ 'message': f'{prefix}{ex.args[0]} - {ex.args[2]}',
'path': path,
'line': ex.problem_mark.line + lineno,
'column': ex.problem_mark.column + 1,
diff --git a/test/lib/ansible_test/_util/controller/tools/sslcheck.py b/test/lib/ansible_test/_util/controller/tools/sslcheck.py
deleted file mode 100644
index c25fed6..0000000
--- a/test/lib/ansible_test/_util/controller/tools/sslcheck.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""Show openssl version."""
-from __future__ import annotations
-
-import json
-
-# noinspection PyBroadException
-try:
- from ssl import OPENSSL_VERSION_INFO
- VERSION = list(OPENSSL_VERSION_INFO[:3])
-except Exception: # pylint: disable=broad-except
- VERSION = None
-
-
-def main():
- """Main program entry point."""
- print(json.dumps(dict(
- version=VERSION,
- )))
-
-
-if __name__ == '__main__':
- main()
diff --git a/test/lib/ansible_test/_util/target/__init__.py b/test/lib/ansible_test/_util/target/__init__.py
index 527d413..8f58623 100644
--- a/test/lib/ansible_test/_util/target/__init__.py
+++ b/test/lib/ansible_test/_util/target/__init__.py
@@ -1,2 +1 @@
-# Empty __init__.py to allow importing of `ansible_test._util.target.common` under Python 2.x.
-# This allows the ansible-test entry point to report supported Python versions before exiting.
+# Empty __init__.py to keep pylint happy.
diff --git a/test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py b/test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py
index 930654f..9cb5d04 100755
--- a/test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py
+++ b/test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py
@@ -4,8 +4,7 @@
# NOTE: This file resides in the _util/target directory to ensure compatibility with all supported Python versions.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import sys
diff --git a/test/lib/ansible_test/_util/target/common/__init__.py b/test/lib/ansible_test/_util/target/common/__init__.py
deleted file mode 100644
index 527d413..0000000
--- a/test/lib/ansible_test/_util/target/common/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# Empty __init__.py to allow importing of `ansible_test._util.target.common` under Python 2.x.
-# This allows the ansible-test entry point to report supported Python versions before exiting.
diff --git a/test/lib/ansible_test/_util/target/common/constants.py b/test/lib/ansible_test/_util/target/common/constants.py
index 36a5a2c..fdad9f2 100644
--- a/test/lib/ansible_test/_util/target/common/constants.py
+++ b/test/lib/ansible_test/_util/target/common/constants.py
@@ -2,12 +2,9 @@
# NOTE: This file resides in the _util/target directory to ensure compatibility with all supported Python versions.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
REMOTE_ONLY_PYTHON_VERSIONS = (
- '2.7',
- '3.6',
'3.7',
'3.8',
'3.9',
diff --git a/test/lib/ansible_test/_util/target/injector/python.py b/test/lib/ansible_test/_util/target/injector/python.py
index c1e88a9..82f59b1 100644
--- a/test/lib/ansible_test/_util/target/injector/python.py
+++ b/test/lib/ansible_test/_util/target/injector/python.py
@@ -1,8 +1,8 @@
# auto-shebang
"""Provides an entry point for python scripts and python modules on the controller with the current python interpreter and optional code coverage collection."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
+import importlib.util
import os
import sys
@@ -19,22 +19,7 @@ def main():
if coverage_output:
args += ['-m', 'coverage.__main__', 'run', '--rcfile', coverage_config]
else:
- if sys.version_info >= (3, 4):
- # noinspection PyUnresolvedReferences
- import importlib.util
-
- # noinspection PyUnresolvedReferences
- found = bool(importlib.util.find_spec('coverage'))
- else:
- # noinspection PyDeprecation
- import imp
-
- try:
- # noinspection PyDeprecation
- imp.find_module('coverage')
- found = True
- except ImportError:
- found = False
+ found = bool(importlib.util.find_spec('coverage'))
if not found:
sys.exit('ERROR: Could not find `coverage` module. '
@@ -62,7 +47,7 @@ def find_program(name, executable): # type: (str, bool) -> str
Raises an exception if the program is not found.
"""
path = os.environ.get('PATH', os.path.defpath)
- seen = set([os.path.abspath(__file__)])
+ seen = {os.path.abspath(__file__)}
mode = os.F_OK | os.X_OK if executable else os.F_OK
for base in path.split(os.path.pathsep):
diff --git a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_forked.py b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_forked.py
index d00d9e9..9e98359 100644
--- a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_forked.py
+++ b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_forked.py
@@ -4,21 +4,14 @@
# https://github.com/pytest-dev/pytest-forked
# https://github.com/pytest-dev/py
# TIP: Disable pytest-xdist when debugging internal errors in this plugin.
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
+from __future__ import annotations
import os
import pickle
import tempfile
import warnings
-from pytest import Item, hookimpl
-
-try:
- from pytest import TestReport
-except ImportError:
- from _pytest.runner import TestReport # Backwards compatibility with pytest < 7. Remove once Python 2.7 is not supported.
+from pytest import Item, hookimpl, TestReport
from _pytest.runner import runtestprotocol
diff --git a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py
index 2f77c03..3aa2e12 100644
--- a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py
+++ b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py
@@ -1,6 +1,5 @@
"""Enable unit testing of Ansible collections. PYTEST_DONT_REWRITE"""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
@@ -39,9 +38,6 @@ def enable_assertion_rewriting_hook(): # type: () -> None
"""
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]
diff --git a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py
index b05298a..577a498 100644
--- a/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py
+++ b/test/lib/ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py
@@ -1,6 +1,5 @@
"""Monkey patch os._exit when running under coverage so we don't lose coverage data in forks, such as with `pytest --boxed`. PYTEST_DONT_REWRITE"""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
def pytest_configure():
diff --git a/test/lib/ansible_test/_util/target/sanity/compile/compile.py b/test/lib/ansible_test/_util/target/sanity/compile/compile.py
index bd2446f..3dfec39 100644
--- a/test/lib/ansible_test/_util/target/sanity/compile/compile.py
+++ b/test/lib/ansible_test/_util/target/sanity/compile/compile.py
@@ -1,12 +1,10 @@
"""Python syntax checker with lint friendly output."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import sys
ENCODING = 'utf-8'
ERRORS = 'replace'
-Text = type(u'')
def main():
@@ -29,21 +27,14 @@ def compile_source(path):
else:
return
- # In some situations offset can be None. This can happen for syntax errors on Python 2.6
- # (__future__ import following after a regular import).
- offset = offset or 0
-
result = "%s:%d:%d: %s: %s" % (path, lineno, offset, extype.__name__, safe_message(message))
- if sys.version_info <= (3,):
- result = result.encode(ENCODING, ERRORS)
-
print(result)
def safe_message(value):
- """Given an input value as text or bytes, return the first non-empty line as text, ensuring it can be round-tripped as UTF-8."""
- if isinstance(value, Text):
+ """Given an input value as str or bytes, return the first non-empty line as str, ensuring it can be round-tripped as UTF-8."""
+ if isinstance(value, str):
value = value.encode(ENCODING, ERRORS)
value = value.decode(ENCODING, ERRORS)
diff --git a/test/lib/ansible_test/_util/target/sanity/import/importer.py b/test/lib/ansible_test/_util/target/sanity/import/importer.py
index 38a7364..28f3f85 100644
--- a/test/lib/ansible_test/_util/target/sanity/import/importer.py
+++ b/test/lib/ansible_test/_util/target/sanity/import/importer.py
@@ -1,6 +1,5 @@
"""Import the given python module(s) and report error(s) encountered."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
def main():
@@ -542,16 +541,6 @@ def main():
"ignore",
"AnsibleCollectionFinder has already been configured")
- if sys.version_info[0] == 2:
- warnings.filterwarnings(
- "ignore",
- "Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography,"
- " and will be removed in a future release.")
- warnings.filterwarnings(
- "ignore",
- "Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography,"
- " and will be removed in the next release.")
-
# ansible.utils.unsafe_proxy attempts patching sys.intern generating a warning if it was already patched
warnings.filterwarnings(
"ignore",
diff --git a/test/lib/ansible_test/_util/target/setup/bootstrap.sh b/test/lib/ansible_test/_util/target/setup/bootstrap.sh
index 65673da..16e6d38 100644
--- a/test/lib/ansible_test/_util/target/setup/bootstrap.sh
+++ b/test/lib/ansible_test/_util/target/setup/bootstrap.sh
@@ -2,6 +2,16 @@
set -eu
+remove_externally_managed_marker()
+{
+ "${python_interpreter}" -c '
+import pathlib
+import sysconfig
+path = pathlib.Path(sysconfig.get_path("stdlib")) / "EXTERNALLY-MANAGED"
+path.unlink(missing_ok=True)
+'
+}
+
install_ssh_keys()
{
if [ ! -f "${ssh_private_key_path}" ]; then
@@ -49,9 +59,6 @@ customize_bashrc()
install_pip() {
if ! "${python_interpreter}" -m pip.__main__ --version --disable-pip-version-check 2>/dev/null; then
case "${python_version}" in
- "2.7")
- pip_bootstrap_url="https://ci-files.testing.ansible.com/ansible-test/get-pip-20.3.4.py"
- ;;
*)
pip_bootstrap_url="https://ci-files.testing.ansible.com/ansible-test/get-pip-23.1.2.py"
;;
@@ -68,18 +75,6 @@ install_pip() {
fi
}
-pip_install() {
- pip_packages="$1"
-
- while true; do
- # shellcheck disable=SC2086
- "${python_interpreter}" -m pip install --disable-pip-version-check ${pip_packages} \
- && break
- echo "Failed to install packages. Sleeping before trying again..."
- sleep 10
- done
-}
-
bootstrap_remote_alpine()
{
py_pkg_prefix="py3"
@@ -168,19 +163,39 @@ bootstrap_remote_freebsd()
jinja2_pkg="py${python_package_version}-jinja2"
cryptography_pkg="py${python_package_version}-cryptography"
pyyaml_pkg="py${python_package_version}-yaml"
+ packaging_pkg="py${python_package_version}-packaging"
# Declare platform/python version combinations which do not have supporting OS packages available.
# For these combinations ansible-test will use pip to install the requirements instead.
case "${platform_version}/${python_version}" in
+ 13.3/3.9)
+ # defaults above 'just work'TM
+ ;;
+ 13.3/3.11)
+ jinja2_pkg="" # not available
+ cryptography_pkg="" # not available
+ pyyaml_pkg="" # not available
+ ;;
+ 14.0/3.9)
+ # defaults above 'just work'TM
+ ;;
+ 14.0/3.11)
+ cryptography_pkg="" # not available
+ jinja2_pkg="" # not available
+ pyyaml_pkg="" # not available
+ ;;
*)
+ # just assume nothing is available
jinja2_pkg="" # not available
cryptography_pkg="" # not available
pyyaml_pkg="" # not available
+ packaging_pkg="" # not available
;;
esac
packages="
${packages}
+ ${packaging_pkg}
libyaml
${pyyaml_pkg}
${jinja2_pkg}
@@ -242,79 +257,6 @@ bootstrap_remote_macos()
echo 'PATH="/usr/local/bin:$PATH"' > /etc/zshenv
}
-bootstrap_remote_rhel_7()
-{
- packages="
- gcc
- python-devel
- python-virtualenv
- "
-
- while true; do
- # shellcheck disable=SC2086
- yum install -q -y ${packages} \
- && break
- echo "Failed to install packages. Sleeping before trying again..."
- sleep 10
- done
-
- install_pip
-
- bootstrap_remote_rhel_pinned_pip_packages
-}
-
-bootstrap_remote_rhel_8()
-{
- if [ "${python_version}" = "3.6" ]; then
- py_pkg_prefix="python3"
- else
- py_pkg_prefix="python${python_version}"
- fi
-
- packages="
- gcc
- ${py_pkg_prefix}-devel
- "
-
- # pip isn't included in the Python devel package under Python 3.11
- if [ "${python_version}" != "3.6" ]; then
- packages="
- ${packages}
- ${py_pkg_prefix}-pip
- "
- fi
-
- # Jinja2 is not installed with an OS package since the provided version is too old.
- # Instead, ansible-test will install it using pip.
- if [ "${controller}" ]; then
- packages="
- ${packages}
- ${py_pkg_prefix}-cryptography
- "
- fi
-
- # Python 3.11 isn't a module like the earlier versions
- if [ "${python_version}" = "3.6" ]; then
- while true; do
- # shellcheck disable=SC2086
- yum module install -q -y "python${python_package_version}" \
- && break
- echo "Failed to install packages. Sleeping before trying again..."
- sleep 10
- done
- fi
-
- while true; do
- # shellcheck disable=SC2086
- yum install -q -y ${packages} \
- && break
- echo "Failed to install packages. Sleeping before trying again..."
- sleep 10
- done
-
- bootstrap_remote_rhel_pinned_pip_packages
-}
-
bootstrap_remote_rhel_9()
{
if [ "${python_version}" = "3.9" ]; then
@@ -360,23 +302,10 @@ bootstrap_remote_rhel_9()
bootstrap_remote_rhel()
{
case "${platform_version}" in
- 7.*) bootstrap_remote_rhel_7 ;;
- 8.*) bootstrap_remote_rhel_8 ;;
9.*) bootstrap_remote_rhel_9 ;;
esac
}
-bootstrap_remote_rhel_pinned_pip_packages()
-{
- # pin packaging and pyparsing to match the downstream vendored versions
- pip_packages="
- packaging==20.4
- pyparsing==2.4.7
- "
-
- pip_install "${pip_packages}"
-}
-
bootstrap_remote_ubuntu()
{
py_pkg_prefix="python3"
@@ -430,14 +359,27 @@ bootstrap_docker()
{
# Required for newer mysql-server packages to install/upgrade on Ubuntu 16.04.
rm -f /usr/sbin/policy-rc.d
+
+ for key_value in ${python_interpreters}; do
+ IFS=':' read -r python_version python_interpreter << EOF
+${key_value}
+EOF
+
+ echo "Bootstrapping Python ${python_version} at: ${python_interpreter}"
+
+ remove_externally_managed_marker
+ done
}
bootstrap_remote()
{
- for python_version in ${python_versions}; do
- echo "Bootstrapping Python ${python_version}"
+ for key_value in ${python_interpreters}; do
+ IFS=':' read -r python_version python_interpreter << EOF
+${key_value}
+EOF
+
+ echo "Bootstrapping Python ${python_version} at: ${python_interpreter}"
- python_interpreter="python${python_version}"
python_package_version="$(echo "${python_version}" | tr -d '.')"
case "${platform}" in
@@ -448,6 +390,8 @@ bootstrap_remote()
"rhel") bootstrap_remote_rhel ;;
"ubuntu") bootstrap_remote_ubuntu ;;
esac
+
+ remove_externally_managed_marker
done
}
@@ -474,7 +418,7 @@ bootstrap_type=#{bootstrap_type}
controller=#{controller}
platform=#{platform}
platform_version=#{platform_version}
-python_versions=#{python_versions}
+python_interpreters=#{python_interpreters}
ssh_key_type=#{ssh_key_type}
ssh_private_key=#{ssh_private_key}
ssh_public_key=#{ssh_public_key}
diff --git a/test/lib/ansible_test/_util/target/setup/probe_cgroups.py b/test/lib/ansible_test/_util/target/setup/probe_cgroups.py
index 2ac7ecb..a09c024 100644
--- a/test/lib/ansible_test/_util/target/setup/probe_cgroups.py
+++ b/test/lib/ansible_test/_util/target/setup/probe_cgroups.py
@@ -1,6 +1,5 @@
"""A tool for probing cgroups to determine write access."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import json
import os
diff --git a/test/lib/ansible_test/_util/target/setup/quiet_pip.py b/test/lib/ansible_test/_util/target/setup/quiet_pip.py
index 171ff8f..c2e9ba2 100644
--- a/test/lib/ansible_test/_util/target/setup/quiet_pip.py
+++ b/test/lib/ansible_test/_util/target/setup/quiet_pip.py
@@ -1,20 +1,17 @@
"""Custom entry-point for pip that filters out unwanted logging and warnings."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import logging
import os
import re
import runpy
import sys
-import warnings
BUILTIN_FILTERER_FILTER = logging.Filterer.filter
LOGGING_MESSAGE_FILTER = re.compile("^("
".*Running pip install with root privileges is generally not a good idea.*|" # custom Fedora patch [1]
".*Running pip as the 'root' user can result in broken permissions .*|" # pip 21.1
- "DEPRECATION: Python 2.7 will reach the end of its life .*|" # pip 19.2.3
"Ignoring .*: markers .* don't match your environment|"
"Looking in indexes: .*|" # pypi-test-container
"Requirement already satisfied.*"
@@ -22,13 +19,6 @@ LOGGING_MESSAGE_FILTER = re.compile("^("
# [1] https://src.fedoraproject.org/rpms/python-pip/blob/f34/f/emit-a-warning-when-running-with-root-privileges.patch
-WARNING_MESSAGE_FILTERS = (
- # DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained.
- # pip 21.0 will drop support for Python 2.7 in January 2021.
- # More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
- 'DEPRECATION: Python 2.7 reached the end of its life ',
-)
-
def custom_filterer_filter(self, record):
"""Globally omit logging of unwanted messages."""
@@ -44,11 +34,6 @@ def main():
# It also avoids problems with loss of color output and mixing up the order of stdout/stderr messages.
logging.Filterer.filter = custom_filterer_filter
- for message_filter in WARNING_MESSAGE_FILTERS:
- # Setting filterwarnings in code is necessary because of the following:
- # Python 2.7 cannot use the -W option to match warning text after a colon. This makes it impossible to match specific warning messages.
- warnings.filterwarnings('ignore', message_filter)
-
get_pip = os.environ.get('GET_PIP')
try:
diff --git a/test/lib/ansible_test/_util/target/setup/requirements.py b/test/lib/ansible_test/_util/target/setup/requirements.py
index b145fde..a320dfc 100644
--- a/test/lib/ansible_test/_util/target/setup/requirements.py
+++ b/test/lib/ansible_test/_util/target/setup/requirements.py
@@ -1,6 +1,5 @@
"""A tool for installing test requirements on the controller and target host."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
# pylint: disable=wrong-import-position
@@ -250,6 +249,14 @@ def common_pip_environment(): # type: () -> t.Dict[str, str]
"""Return common environment variables used to run pip."""
env = os.environ.copy()
+ # When ansible-test installs requirements outside a virtual environment, it does so under one of two conditions:
+ # 1) The environment is an ephemeral one provisioned by ansible-test.
+ # 2) The user has provided the `--requirements` option to force installation of requirements.
+ # It seems reasonable to bypass PEP 668 checks in both of these cases.
+ # Doing so with an environment variable allows it to work under any version of pip which supports it, without breaking older versions.
+ # NOTE: pip version 23.0 enforces PEP 668 but does not support the override, in which case upgrading pip is required.
+ env.update(PIP_BREAK_SYSTEM_PACKAGES='1')
+
return env
diff --git a/test/lib/ansible_test/_util/target/tools/virtualenvcheck.py b/test/lib/ansible_test/_util/target/tools/virtualenvcheck.py
index a38ad07..49b308b 100644
--- a/test/lib/ansible_test/_util/target/tools/virtualenvcheck.py
+++ b/test/lib/ansible_test/_util/target/tools/virtualenvcheck.py
@@ -1,6 +1,5 @@
"""Detect the real python interpreter when running in a virtual environment created by the 'virtualenv' module."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import json
diff --git a/test/lib/ansible_test/_util/target/tools/yamlcheck.py b/test/lib/ansible_test/_util/target/tools/yamlcheck.py
index dfd08e5..07dccca 100644
--- a/test/lib/ansible_test/_util/target/tools/yamlcheck.py
+++ b/test/lib/ansible_test/_util/target/tools/yamlcheck.py
@@ -1,6 +1,5 @@
"""Show availability of PyYAML and libyaml support."""
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import json