summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLee Garrett <lgarrett@rocketjump.eu>2023-03-01 21:04:53 +0100
committerLee Garrett <lgarrett@rocketjump.eu>2023-03-01 21:04:53 +0100
commit3cda7ad4dd15b514ff660905294b5b6330ecfb6f (patch)
tree527d16e74bfd1840c85efd675fdecad056c54107 /test
parent520506f035967306d0548a8f69a0fea3181dca35 (diff)
downloaddebian-ansible-core-3cda7ad4dd15b514ff660905294b5b6330ecfb6f.zip
New upstream version 2.14.3
Diffstat (limited to 'test')
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/meta/main.yml1
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml1
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/test_manifest_metadata.yml55
-rw-r--r--test/integration/targets/handlers/79776-handlers.yml2
-rw-r--r--test/integration/targets/handlers/79776.yml10
-rwxr-xr-xtest/integration/targets/handlers/runme.sh11
-rw-r--r--test/integration/targets/handlers/test_block_as_handler-import.yml7
-rw-r--r--test/integration/targets/handlers/test_block_as_handler-include.yml8
-rw-r--r--test/integration/targets/handlers/test_block_as_handler-include_import-handlers.yml8
-rw-r--r--test/integration/targets/handlers/test_block_as_handler.yml13
-rw-r--r--test/integration/targets/register/aliases2
-rw-r--r--test/integration/targets/register/can_register.yml21
-rw-r--r--test/integration/targets/register/invalid.yml11
-rw-r--r--test/integration/targets/register/invalid_skipped.yml12
-rwxr-xr-xtest/integration/targets/register/runme.sh12
-rw-r--r--test/integration/targets/tasks/playbook.yml19
-rwxr-xr-xtest/integration/targets/tasks/runme.sh3
-rw-r--r--test/integration/targets/tasks/tasks/main.yml4
-rw-r--r--test/lib/ansible_test/_internal/ansible_util.py10
-rw-r--r--test/lib/ansible_test/_internal/become.py6
-rw-r--r--test/lib/ansible_test/_internal/cgroup.py2
-rw-r--r--test/lib/ansible_test/_internal/ci/__init__.py2
-rw-r--r--test/lib/ansible_test/_internal/ci/azp.py2
-rw-r--r--test/lib/ansible_test/_internal/classification/__init__.py54
-rw-r--r--test/lib/ansible_test/_internal/classification/python.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/argparsing/__init__.py48
-rw-r--r--test/lib/ansible_test/_internal/cli/argparsing/argcompletion.py4
-rw-r--r--test/lib/ansible_test/_internal/cli/argparsing/parsers.py2
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/__init__.py4
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/__init__.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/__init__.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/combine.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/erase.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/html.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/report.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/coverage/xml.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/env.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/integration/__init__.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/integration/network.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/integration/posix.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/integration/windows.py8
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/sanity.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/shell.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/commands/units.py6
-rw-r--r--test/lib/ansible_test/_internal/cli/compat.py32
-rw-r--r--test/lib/ansible_test/_internal/cli/environments.py64
-rw-r--r--test/lib/ansible_test/_internal/cli/parsers/base_argument_parsers.py2
-rw-r--r--test/lib/ansible_test/_internal/cli/parsers/key_value_parsers.py4
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/__init__.py36
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/analyze/targets/__init__.py11
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/analyze/targets/combine.py8
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/analyze/targets/filter.py19
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/analyze/targets/generate.py20
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/analyze/targets/missing.py22
-rw-r--r--test/lib/ansible_test/_internal/commands/coverage/combine.py8
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/__init__.py106
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py8
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/acme.py2
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/cs.py4
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/coverage.py4
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/filters.py12
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/__init__.py28
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/import.py4
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/mypy.py10
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/pylint.py16
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/validate_modules.py2
-rw-r--r--test/lib/ansible_test/_internal/completion.py20
-rw-r--r--test/lib/ansible_test/_internal/containers.py86
-rw-r--r--test/lib/ansible_test/_internal/core_ci.py33
-rw-r--r--test/lib/ansible_test/_internal/coverage_util.py22
-rw-r--r--test/lib/ansible_test/_internal/data.py6
-rw-r--r--test/lib/ansible_test/_internal/delegation.py22
-rw-r--r--test/lib/ansible_test/_internal/dev/container_probe.py2
-rw-r--r--test/lib/ansible_test/_internal/docker_util.py74
-rw-r--r--test/lib/ansible_test/_internal/executor.py6
-rw-r--r--test/lib/ansible_test/_internal/host_configs.py6
-rw-r--r--test/lib/ansible_test/_internal/host_profiles.py17
-rw-r--r--test/lib/ansible_test/_internal/io.py9
-rw-r--r--test/lib/ansible_test/_internal/metadata.py4
-rw-r--r--test/lib/ansible_test/_internal/provider/layout/__init__.py2
-rw-r--r--test/lib/ansible_test/_internal/provisioning.py8
-rw-r--r--test/lib/ansible_test/_internal/pypi_proxy.py2
-rw-r--r--test/lib/ansible_test/_internal/python_requirements.py50
-rw-r--r--test/lib/ansible_test/_internal/ssh.py30
-rw-r--r--test/lib/ansible_test/_internal/target.py92
-rw-r--r--test/lib/ansible_test/_internal/test.py34
-rw-r--r--test/lib/ansible_test/_internal/thread.py9
-rw-r--r--test/lib/ansible_test/_internal/timeout.py2
-rw-r--r--test/lib/ansible_test/_internal/util.py90
-rw-r--r--test/lib/ansible_test/_internal/util_common.py52
-rw-r--r--test/lib/ansible_test/_internal/venv.py4
-rw-r--r--test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py10
-rw-r--r--test/lib/ansible_test/_util/target/sanity/import/importer.py3
-rw-r--r--test/units/playbook/test_helpers.py14
100 files changed, 879 insertions, 736 deletions
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/meta/main.yml b/test/integration/targets/ansible-galaxy-collection-scm/meta/main.yml
index e3dd5fb1..655a62f0 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/meta/main.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/meta/main.yml
@@ -1,3 +1,4 @@
---
dependencies:
- setup_remote_tmp_dir
+- setup_gnutar
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
index 546c4083..dab599b1 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
@@ -30,6 +30,7 @@
- include_tasks: ./download.yml
- include_tasks: ./setup_collection_bad_version.yml
- include_tasks: ./test_invalid_version.yml
+ - include_tasks: ./test_manifest_metadata.yml
always:
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/test_manifest_metadata.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/test_manifest_metadata.yml
new file mode 100644
index 00000000..a01551ca
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/test_manifest_metadata.yml
@@ -0,0 +1,55 @@
+- name: Test installing a collection from a git repo containing a MANIFEST.json
+ block:
+ - name: Create a temp directory for building the collection
+ file:
+ path: '{{ galaxy_dir }}/scratch'
+ state: directory
+
+ - name: Initialize a collection
+ command: 'ansible-galaxy collection init namespace_3.collection_1'
+ args:
+ chdir: '{{ galaxy_dir }}/scratch'
+
+ - name: Build the collection
+ command: 'ansible-galaxy collection build namespace_3/collection_1'
+ args:
+ chdir: '{{ galaxy_dir }}/scratch'
+
+ - name: Initialize git repository
+ command: 'git init {{ scm_path }}/namespace_3'
+
+ - name: Create the destination for the collection
+ file:
+ path: '{{ scm_path }}/namespace_3/collection_1'
+ state: directory
+
+ - name: Unarchive the collection in the git repo
+ unarchive:
+ dest: '{{ scm_path }}/namespace_3/collection_1'
+ src: '{{ galaxy_dir }}/scratch/namespace_3-collection_1-1.0.0.tar.gz'
+ remote_src: yes
+
+ - name: Commit the changes
+ shell: git add ./; git commit -m 'add collection'
+ args:
+ chdir: '{{ scm_path }}/namespace_3'
+
+ - name: Install the collection in the git repository
+ command: 'ansible-galaxy collection install git+file://{{ scm_path }}/namespace_3/.git'
+ register: result
+
+ - name: Assert the collection was installed successfully
+ assert:
+ that:
+ - '"namespace_3.collection_1:1.0.0 was installed successfully" in result.stdout_lines'
+
+ always:
+ - name: Clean up directories from test
+ file:
+ path: '{{ galaxy_dir }}/scratch'
+ state: absent
+ loop:
+ - '{{ galaxy_dir }}/scratch'
+ - '{{ scm_path }}/namespace_3'
+
+ - include_tasks: ./empty_installed_collections.yml
diff --git a/test/integration/targets/handlers/79776-handlers.yml b/test/integration/targets/handlers/79776-handlers.yml
new file mode 100644
index 00000000..639c9cad
--- /dev/null
+++ b/test/integration/targets/handlers/79776-handlers.yml
@@ -0,0 +1,2 @@
+- debug:
+ msg: "Handler for {{ inventory_hostname }}"
diff --git a/test/integration/targets/handlers/79776.yml b/test/integration/targets/handlers/79776.yml
new file mode 100644
index 00000000..08d22272
--- /dev/null
+++ b/test/integration/targets/handlers/79776.yml
@@ -0,0 +1,10 @@
+- hosts: A,B
+ gather_facts: false
+ force_handlers: true
+ tasks:
+ - command: echo
+ notify: handler1
+ when: inventory_hostname == "A"
+ handlers:
+ - name: handler1
+ include_tasks: 79776-handlers.yml
diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh
index e2d52180..76fc99d8 100755
--- a/test/integration/targets/handlers/runme.sh
+++ b/test/integration/targets/handlers/runme.sh
@@ -170,3 +170,14 @@ ansible-playbook test_flush_handlers_rescue_always.yml -i inventory.handlers "$@
ansible-playbook test_fqcn_meta_flush_handlers.yml -i inventory.handlers "$@" 2>&1 | tee out.txt
grep out.txt -e "handler ran"
grep out.txt -e "after flush"
+
+ansible-playbook 79776.yml -i inventory.handlers "$@"
+
+ansible-playbook test_block_as_handler.yml "$@" 2>&1 | tee out.txt
+grep out.txt -e "ERROR! Using a block as a handler is not supported."
+
+ansible-playbook test_block_as_handler-include.yml "$@" 2>&1 | tee out.txt
+grep out.txt -e "ERROR! Using a block as a handler is not supported."
+
+ansible-playbook test_block_as_handler-import.yml "$@" 2>&1 | tee out.txt
+grep out.txt -e "ERROR! Using a block as a handler is not supported."
diff --git a/test/integration/targets/handlers/test_block_as_handler-import.yml b/test/integration/targets/handlers/test_block_as_handler-import.yml
new file mode 100644
index 00000000..ad6bb0d5
--- /dev/null
+++ b/test/integration/targets/handlers/test_block_as_handler-import.yml
@@ -0,0 +1,7 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - debug:
+ handlers:
+ - name: handler
+ import_tasks: test_block_as_handler-include_import-handlers.yml
diff --git a/test/integration/targets/handlers/test_block_as_handler-include.yml b/test/integration/targets/handlers/test_block_as_handler-include.yml
new file mode 100644
index 00000000..5b03b0a8
--- /dev/null
+++ b/test/integration/targets/handlers/test_block_as_handler-include.yml
@@ -0,0 +1,8 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - command: echo
+ notify: handler
+ handlers:
+ - name: handler
+ include_tasks: test_block_as_handler-include_import-handlers.yml
diff --git a/test/integration/targets/handlers/test_block_as_handler-include_import-handlers.yml b/test/integration/targets/handlers/test_block_as_handler-include_import-handlers.yml
new file mode 100644
index 00000000..61c058ba
--- /dev/null
+++ b/test/integration/targets/handlers/test_block_as_handler-include_import-handlers.yml
@@ -0,0 +1,8 @@
+- name: handler
+ block:
+ - name: due to how handlers are implemented, this is correct as it is equivalent to an implicit block
+ debug:
+ - name: this is a parser error, blocks as handlers are not supported
+ block:
+ - name: handler in a nested block
+ debug:
diff --git a/test/integration/targets/handlers/test_block_as_handler.yml b/test/integration/targets/handlers/test_block_as_handler.yml
new file mode 100644
index 00000000..bd4f5b99
--- /dev/null
+++ b/test/integration/targets/handlers/test_block_as_handler.yml
@@ -0,0 +1,13 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - debug:
+ handlers:
+ - name: handler
+ block:
+ - name: due to how handlers are implemented, this is correct as it is equivalent to an implicit block
+ debug:
+ - name: this is a parser error, blocks as handlers are not supported
+ block:
+ - name: handler in a nested block
+ debug:
diff --git a/test/integration/targets/register/aliases b/test/integration/targets/register/aliases
new file mode 100644
index 00000000..b76d5f67
--- /dev/null
+++ b/test/integration/targets/register/aliases
@@ -0,0 +1,2 @@
+shippable/posix/group3
+context/controller # this "module" is actually an action that runs on the controller
diff --git a/test/integration/targets/register/can_register.yml b/test/integration/targets/register/can_register.yml
new file mode 100644
index 00000000..da610101
--- /dev/null
+++ b/test/integration/targets/register/can_register.yml
@@ -0,0 +1,21 @@
+- hosts: testhost
+ gather_facts: false
+ tasks:
+ - name: test registering
+ debug: msg='does nothing really but register this'
+ register: debug_msg
+
+ - name: validate registering
+ assert:
+ that:
+ - debug_msg is defined
+
+ - name: test registering skipped
+ debug: msg='does nothing really but register this'
+ when: false
+ register: debug_skipped
+
+ - name: validate registering
+ assert:
+ that:
+ - debug_skipped is defined
diff --git a/test/integration/targets/register/invalid.yml b/test/integration/targets/register/invalid.yml
new file mode 100644
index 00000000..bdca9d66
--- /dev/null
+++ b/test/integration/targets/register/invalid.yml
@@ -0,0 +1,11 @@
+- hosts: testhost
+ gather_facts: false
+ tasks:
+ - name: test registering
+ debug: msg='does nothing really but register this'
+ register: 200
+
+ - name: never gets here
+ assert:
+ that:
+ - 200 is not defined
diff --git a/test/integration/targets/register/invalid_skipped.yml b/test/integration/targets/register/invalid_skipped.yml
new file mode 100644
index 00000000..0ad31f51
--- /dev/null
+++ b/test/integration/targets/register/invalid_skipped.yml
@@ -0,0 +1,12 @@
+- hosts: testhost
+ gather_facts: false
+ tasks:
+ - name: test registering bad var when skipped
+ debug: msg='does nothing really but register this'
+ when: false
+ register: 200
+
+ - name: never gets here
+ assert:
+ that:
+ - 200 is not defined
diff --git a/test/integration/targets/register/runme.sh b/test/integration/targets/register/runme.sh
new file mode 100755
index 00000000..8adc5047
--- /dev/null
+++ b/test/integration/targets/register/runme.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -eux
+
+# does it work?
+ansible-playbook can_register.yml -i ../../inventory -v "$@"
+
+# ensure we do error when it its apprpos
+set +e
+result="$(ansible-playbook invalid.yml -i ../../inventory -v "$@" 2>&1)"
+set -e
+grep -q "Invalid variable name in " <<< "${result}"
diff --git a/test/integration/targets/tasks/playbook.yml b/test/integration/targets/tasks/playbook.yml
new file mode 100644
index 00000000..80d9f8b1
--- /dev/null
+++ b/test/integration/targets/tasks/playbook.yml
@@ -0,0 +1,19 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ # make sure tasks with an undefined variable in the name are gracefully handled
+ - name: "Task name with undefined variable: {{ not_defined }}"
+ debug:
+ msg: Hello
+
+ - name: ensure malformed raw_params on arbitrary actions are not ignored
+ debug:
+ garbage {{"with a template"}}
+ ignore_errors: true
+ register: bad_templated_raw_param
+
+ - assert:
+ that:
+ - bad_templated_raw_param is failed
+ - |
+ "invalid or malformed argument: 'garbage with a template'" in bad_templated_raw_param.msg
diff --git a/test/integration/targets/tasks/runme.sh b/test/integration/targets/tasks/runme.sh
new file mode 100755
index 00000000..594447bd
--- /dev/null
+++ b/test/integration/targets/tasks/runme.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+ansible-playbook playbook.yml "$@"
diff --git a/test/integration/targets/tasks/tasks/main.yml b/test/integration/targets/tasks/tasks/main.yml
deleted file mode 100644
index f6ac1114..00000000
--- a/test/integration/targets/tasks/tasks/main.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# make sure tasks with an undefined variable in the name are gracefully handled
-- name: "Task name with undefined variable: {{ not_defined }}"
- debug:
- msg: Hello
diff --git a/test/lib/ansible_test/_internal/ansible_util.py b/test/lib/ansible_test/_internal/ansible_util.py
index 5798d352..9efcda26 100644
--- a/test/lib/ansible_test/_internal/ansible_util.py
+++ b/test/lib/ansible_test/_internal/ansible_util.py
@@ -284,11 +284,11 @@ def get_collection_detail(python: PythonConfig) -> CollectionDetail:
def run_playbook(
- args: EnvironmentConfig,
- inventory_path: str,
- playbook: str,
- capture: bool,
- variables: t.Optional[dict[str, t.Any]] = None,
+ args: EnvironmentConfig,
+ inventory_path: str,
+ playbook: str,
+ capture: bool,
+ variables: t.Optional[dict[str, t.Any]] = None,
) -> None:
"""Run the specified playbook using the given inventory file and playbook variables."""
playbook_path = os.path.join(ANSIBLE_TEST_DATA_ROOT, 'playbooks', playbook)
diff --git a/test/lib/ansible_test/_internal/become.py b/test/lib/ansible_test/_internal/become.py
index cabf97e4..e653959a 100644
--- a/test/lib/ansible_test/_internal/become.py
+++ b/test/lib/ansible_test/_internal/become.py
@@ -12,7 +12,7 @@ from .util import (
class Become(metaclass=abc.ABCMeta):
"""Base class for become implementations."""
@classmethod
- def name(cls):
+ def name(cls) -> str:
"""The name of this plugin."""
return cls.__name__.lower()
@@ -48,7 +48,7 @@ class Doas(Become):
class DoasSudo(Doas):
"""Become using 'doas' in ansible-test and then after bootstrapping use 'sudo' for other ansible commands."""
@classmethod
- def name(cls):
+ def name(cls) -> str:
"""The name of this plugin."""
return 'doas_sudo'
@@ -78,7 +78,7 @@ class Su(Become):
class SuSudo(Su):
"""Become using 'su' in ansible-test and then after bootstrapping use 'sudo' for other ansible commands."""
@classmethod
- def name(cls):
+ def name(cls) -> str:
"""The name of this plugin."""
return 'su_sudo'
diff --git a/test/lib/ansible_test/_internal/cgroup.py b/test/lib/ansible_test/_internal/cgroup.py
index b55d878d..977e359d 100644
--- a/test/lib/ansible_test/_internal/cgroup.py
+++ b/test/lib/ansible_test/_internal/cgroup.py
@@ -29,7 +29,7 @@ class CGroupEntry:
path: pathlib.PurePosixPath
@property
- def root_path(self):
+ def root_path(self) -> pathlib.PurePosixPath:
"""The root path for this cgroup subsystem."""
return pathlib.PurePosixPath(CGroupPath.ROOT, self.subsystem)
diff --git a/test/lib/ansible_test/_internal/ci/__init__.py b/test/lib/ansible_test/_internal/ci/__init__.py
index 677fafce..97e41dae 100644
--- a/test/lib/ansible_test/_internal/ci/__init__.py
+++ b/test/lib/ansible_test/_internal/ci/__init__.py
@@ -152,6 +152,8 @@ class CryptographyAuthHelper(AuthHelper, metaclass=abc.ABCMeta):
private_key_pem = self.initialize_private_key()
private_key = load_pem_private_key(to_bytes(private_key_pem), None, default_backend())
+ assert isinstance(private_key, ec.EllipticCurvePrivateKey)
+
signature_raw_bytes = private_key.sign(payload_bytes, ec.ECDSA(hashes.SHA256()))
return signature_raw_bytes
diff --git a/test/lib/ansible_test/_internal/ci/azp.py b/test/lib/ansible_test/_internal/ci/azp.py
index 557fbacb..9170dfec 100644
--- a/test/lib/ansible_test/_internal/ci/azp.py
+++ b/test/lib/ansible_test/_internal/ci/azp.py
@@ -40,7 +40,7 @@ CODE = 'azp'
class AzurePipelines(CIProvider):
"""CI provider implementation for Azure Pipelines."""
- def __init__(self):
+ def __init__(self) -> None:
self.auth = AzurePipelinesAuthHelper()
@staticmethod
diff --git a/test/lib/ansible_test/_internal/classification/__init__.py b/test/lib/ansible_test/_internal/classification/__init__.py
index 863de23c..aacc2ca9 100644
--- a/test/lib/ansible_test/_internal/classification/__init__.py
+++ b/test/lib/ansible_test/_internal/classification/__init__.py
@@ -379,9 +379,9 @@ class PathMapper:
if is_subdir(path, data_context().content.integration_path):
if dirname == data_context().content.integration_path:
for command in (
- 'integration',
- 'windows-integration',
- 'network-integration',
+ 'integration',
+ 'windows-integration',
+ 'network-integration',
):
if name == command and ext == '.cfg':
return {
@@ -641,19 +641,19 @@ class PathMapper:
if '/' not in path:
if path in (
- '.gitignore',
- 'COPYING',
- 'LICENSE',
- 'Makefile',
+ '.gitignore',
+ 'COPYING',
+ 'LICENSE',
+ 'Makefile',
):
return minimal
if ext in (
- '.in',
- '.md',
- '.rst',
- '.toml',
- '.txt',
+ '.in',
+ '.md',
+ '.rst',
+ '.toml',
+ '.txt',
):
return minimal
@@ -757,17 +757,17 @@ class PathMapper:
if path.startswith('test/lib/ansible_test/_data/requirements/'):
if name in (
- 'integration',
- 'network-integration',
- 'windows-integration',
+ 'integration',
+ 'network-integration',
+ 'windows-integration',
):
return {
name: self.integration_all_target,
}
if name in (
- 'sanity',
- 'units',
+ 'sanity',
+ 'units',
):
return {
name: 'all',
@@ -826,11 +826,11 @@ class PathMapper:
if '/' not in path:
if path in (
- '.gitattributes',
- '.gitignore',
- '.mailmap',
- 'COPYING',
- 'Makefile',
+ '.gitattributes',
+ '.gitignore',
+ '.mailmap',
+ 'COPYING',
+ 'Makefile',
):
return minimal
@@ -840,11 +840,11 @@ class PathMapper:
return all_tests(self.args) # broad impact, run all tests
if ext in (
- '.in',
- '.md',
- '.rst',
- '.toml',
- '.txt',
+ '.in',
+ '.md',
+ '.rst',
+ '.toml',
+ '.txt',
):
return minimal
diff --git a/test/lib/ansible_test/_internal/classification/python.py b/test/lib/ansible_test/_internal/classification/python.py
index df888738..77ffeacf 100644
--- a/test/lib/ansible_test/_internal/classification/python.py
+++ b/test/lib/ansible_test/_internal/classification/python.py
@@ -146,10 +146,8 @@ def get_python_module_utils_name(path: str) -> str:
return name
-def enumerate_module_utils():
- """Return a list of available module_utils imports.
- :rtype: set[str]
- """
+def enumerate_module_utils() -> set[str]:
+ """Return a list of available module_utils imports."""
module_utils = []
for path in data_context().content.walk_files(data_context().content.module_utils_path):
diff --git a/test/lib/ansible_test/_internal/cli/argparsing/__init__.py b/test/lib/ansible_test/_internal/cli/argparsing/__init__.py
index 9356442d..540cf552 100644
--- a/test/lib/ansible_test/_internal/cli/argparsing/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/argparsing/__init__.py
@@ -34,17 +34,17 @@ class RegisteredCompletionFinder(OptionCompletionFinder):
These registered completions, if provided, are used to filter the final completion results.
This works around a known bug: https://github.com/kislyuk/argcomplete/issues/221
"""
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.registered_completions: t.Optional[list[str]] = None
def completer(
- self,
- prefix: str,
- action: argparse.Action,
- parsed_args: argparse.Namespace,
- **kwargs,
+ self,
+ prefix: str,
+ action: argparse.Action,
+ parsed_args: argparse.Namespace,
+ **kwargs,
) -> list[str]:
"""
Return a list of completions for the specified prefix and action.
@@ -63,10 +63,10 @@ class RegisteredCompletionFinder(OptionCompletionFinder):
@abc.abstractmethod
def get_completions(
- self,
- prefix: str,
- action: argparse.Action,
- parsed_args: argparse.Namespace,
+ self,
+ prefix: str,
+ action: argparse.Action,
+ parsed_args: argparse.Namespace,
) -> list[str]:
"""
Return a list of completions for the specified prefix and action.
@@ -89,9 +89,9 @@ class CompositeAction(argparse.Action, metaclass=abc.ABCMeta):
documentation_state: dict[t.Type[CompositeAction], DocumentationState] = {}
def __init__(
- self,
- *args,
- **kwargs,
+ self,
+ *args,
+ **kwargs,
):
self.definition = self.create_parser()
self.documentation_state[type(self)] = documentation_state = DocumentationState()
@@ -108,11 +108,11 @@ class CompositeAction(argparse.Action, metaclass=abc.ABCMeta):
"""Return a namespace parser to parse the argument associated with this action."""
def __call__(
- self,
- parser,
- namespace,
- values,
- option_string=None,
+ self,
+ parser,
+ namespace,
+ values,
+ option_string=None,
):
state = ParserState(mode=ParserMode.PARSE, namespaces=[namespace], remainder=values)
@@ -135,10 +135,10 @@ class CompositeAction(argparse.Action, metaclass=abc.ABCMeta):
class CompositeActionCompletionFinder(RegisteredCompletionFinder):
"""Completion finder with support for composite argument parsing."""
def get_completions(
- self,
- prefix: str,
- action: argparse.Action,
- parsed_args: argparse.Namespace,
+ self,
+ prefix: str,
+ action: argparse.Action,
+ parsed_args: argparse.Namespace,
) -> list[str]:
"""Return a list of completions appropriate for the given prefix and action, taking into account the arguments that have already been parsed."""
assert isinstance(action, CompositeAction)
@@ -232,8 +232,8 @@ def detect_false_file_completion(value: str, mode: ParserMode) -> bool:
def complete(
- completer: Parser,
- state: ParserState,
+ completer: Parser,
+ state: ParserState,
) -> Completion:
"""Perform argument completion using the given completer and return the completion result."""
value = state.remainder
diff --git a/test/lib/ansible_test/_internal/cli/argparsing/argcompletion.py b/test/lib/ansible_test/_internal/cli/argparsing/argcompletion.py
index df19b338..cf5776da 100644
--- a/test/lib/ansible_test/_internal/cli/argparsing/argcompletion.py
+++ b/test/lib/ansible_test/_internal/cli/argparsing/argcompletion.py
@@ -9,7 +9,7 @@ import typing as t
class Substitute:
"""Substitute for missing class which accepts all arguments."""
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
pass
@@ -87,7 +87,7 @@ class OptionCompletionFinder(CompletionFinder):
"""
enabled = bool(argcomplete)
- def __init__(self, *args, validator=None, **kwargs):
+ def __init__(self, *args, validator=None, **kwargs) -> None:
if validator:
raise ValueError()
diff --git a/test/lib/ansible_test/_internal/cli/argparsing/parsers.py b/test/lib/ansible_test/_internal/cli/argparsing/parsers.py
index 429b9c0c..d07e03cb 100644
--- a/test/lib/ansible_test/_internal/cli/argparsing/parsers.py
+++ b/test/lib/ansible_test/_internal/cli/argparsing/parsers.py
@@ -341,7 +341,7 @@ class IntegerParser(DynamicChoicesParser):
class BooleanParser(ChoicesParser):
"""Composite argument parser for boolean (yes/no) values."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__(['yes', 'no'])
def parse(self, state: ParserState) -> bool:
diff --git a/test/lib/ansible_test/_internal/cli/commands/__init__.py b/test/lib/ansible_test/_internal/cli/commands/__init__.py
index 2ecd3a5e..2eb14abc 100644
--- a/test/lib/ansible_test/_internal/cli/commands/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/commands/__init__.py
@@ -44,8 +44,8 @@ from .units import (
def do_commands(
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for all commands."""
common = argparse.ArgumentParser(add_help=False)
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/__init__.py b/test/lib/ansible_test/_internal/cli/commands/coverage/__init__.py
index 96beafab..28e67709 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/__init__.py
@@ -37,9 +37,9 @@ from .xml import (
def do_coverage(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for all `coverage` commands."""
coverage_common = argparse.ArgumentParser(add_help=False, parents=[parent])
@@ -61,7 +61,7 @@ def do_coverage(
def add_coverage_common(
- parser: argparse.ArgumentParser,
+ parser: argparse.ArgumentParser,
):
"""Add common coverage arguments."""
parser.add_argument(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/__init__.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/__init__.py
index 9dbf8c0c..05fbd233 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/__init__.py
@@ -13,9 +13,9 @@ from ....environments import (
def do_analyze(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for all `coverage analyze` commands."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py
index 429111e2..7b6ea3eb 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py
@@ -29,9 +29,9 @@ from .missing import (
def do_targets(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for all `coverage analyze targets` commands."""
targets = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py
index d25240b4..7fa49bf9 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py
@@ -17,9 +17,9 @@ from .....environments import (
def do_combine(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `coverage analyze targets combine` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py
index d34a12bc..f5f020fe 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py
@@ -17,9 +17,9 @@ from .....environments import (
def do_expand(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `coverage analyze targets expand` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py
index 395d42a9..afcb828b 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py
@@ -17,9 +17,9 @@ from .....environments import (
def do_filter(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `coverage analyze targets filter` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py
index 0090d277..0d13933d 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py
@@ -17,9 +17,9 @@ from .....environments import (
def do_generate(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `coverage analyze targets generate` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py
index 7a6b847c..8af236f3 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py
@@ -17,9 +17,9 @@ from .....environments import (
def do_missing(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `coverage analyze targets missing` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/combine.py b/test/lib/ansible_test/_internal/cli/commands/coverage/combine.py
index 03d3ae89..9b6d34a3 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/combine.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/combine.py
@@ -19,10 +19,10 @@ from ...environments import (
def do_combine(
- subparsers,
- parent: argparse.ArgumentParser,
- add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for the `coverage combine` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/erase.py b/test/lib/ansible_test/_internal/cli/commands/coverage/erase.py
index 2491fa0d..ef356f02 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/erase.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/erase.py
@@ -17,9 +17,9 @@ from ...environments import (
def do_erase(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for the `coverage erase` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/html.py b/test/lib/ansible_test/_internal/cli/commands/coverage/html.py
index 5823b692..5f719de7 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/html.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/html.py
@@ -19,10 +19,10 @@ from ...environments import (
def do_html(
- subparsers,
- parent: argparse.ArgumentParser,
- add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for the `coverage html` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/report.py b/test/lib/ansible_test/_internal/cli/commands/coverage/report.py
index 93f868fd..e6a6e805 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/report.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/report.py
@@ -19,10 +19,10 @@ from ...environments import (
def do_report(
- subparsers,
- parent: argparse.ArgumentParser,
- add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for the `coverage report` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/coverage/xml.py b/test/lib/ansible_test/_internal/cli/commands/coverage/xml.py
index 07e66252..e7b03ca8 100644
--- a/test/lib/ansible_test/_internal/cli/commands/coverage/xml.py
+++ b/test/lib/ansible_test/_internal/cli/commands/coverage/xml.py
@@ -19,10 +19,10 @@ from ...environments import (
def do_xml(
- subparsers,
- parent: argparse.ArgumentParser,
- add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_coverage_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
) -> None:
"""Command line parsing for the `coverage xml` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/env.py b/test/lib/ansible_test/_internal/cli/commands/env.py
index 87fa41ad..0cd21145 100644
--- a/test/lib/ansible_test/_internal/cli/commands/env.py
+++ b/test/lib/ansible_test/_internal/cli/commands/env.py
@@ -17,9 +17,9 @@ from ..environments import (
def do_env(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `env` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/integration/__init__.py b/test/lib/ansible_test/_internal/cli/commands/integration/__init__.py
index 916249fa..dfdefb11 100644
--- a/test/lib/ansible_test/_internal/cli/commands/integration/__init__.py
+++ b/test/lib/ansible_test/_internal/cli/commands/integration/__init__.py
@@ -26,9 +26,9 @@ from .windows import (
def do_integration(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for all integration commands."""
parser = argparse.ArgumentParser(
@@ -42,7 +42,7 @@ def do_integration(
def add_integration_common(
- parser: argparse.ArgumentParser,
+ parser: argparse.ArgumentParser,
):
"""Add common integration arguments."""
register_completer(parser.add_argument(
diff --git a/test/lib/ansible_test/_internal/cli/commands/integration/network.py b/test/lib/ansible_test/_internal/cli/commands/integration/network.py
index 4d0eb918..a05985b5 100644
--- a/test/lib/ansible_test/_internal/cli/commands/integration/network.py
+++ b/test/lib/ansible_test/_internal/cli/commands/integration/network.py
@@ -35,10 +35,10 @@ from ...completers import (
def do_network_integration(
- subparsers,
- parent: argparse.ArgumentParser,
- add_integration_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_integration_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `network-integration` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/integration/posix.py b/test/lib/ansible_test/_internal/cli/commands/integration/posix.py
index e6a9527c..78d61658 100644
--- a/test/lib/ansible_test/_internal/cli/commands/integration/posix.py
+++ b/test/lib/ansible_test/_internal/cli/commands/integration/posix.py
@@ -26,10 +26,10 @@ from ...environments import (
def do_posix_integration(
- subparsers,
- parent: argparse.ArgumentParser,
- add_integration_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_integration_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `integration` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/integration/windows.py b/test/lib/ansible_test/_internal/cli/commands/integration/windows.py
index cfbdb44f..ab022e3b 100644
--- a/test/lib/ansible_test/_internal/cli/commands/integration/windows.py
+++ b/test/lib/ansible_test/_internal/cli/commands/integration/windows.py
@@ -26,10 +26,10 @@ from ...environments import (
def do_windows_integration(
- subparsers,
- parent: argparse.ArgumentParser,
- add_integration_common: c.Callable[[argparse.ArgumentParser], None],
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ add_integration_common: c.Callable[[argparse.ArgumentParser], None],
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `windows-integration` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/sanity.py b/test/lib/ansible_test/_internal/cli/commands/sanity.py
index 36f0ec58..8b4a9ae5 100644
--- a/test/lib/ansible_test/_internal/cli/commands/sanity.py
+++ b/test/lib/ansible_test/_internal/cli/commands/sanity.py
@@ -29,9 +29,9 @@ from ..environments import (
def do_sanity(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `sanity` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/shell.py b/test/lib/ansible_test/_internal/cli/commands/shell.py
index 16fb8b44..1baffc6e 100644
--- a/test/lib/ansible_test/_internal/cli/commands/shell.py
+++ b/test/lib/ansible_test/_internal/cli/commands/shell.py
@@ -20,9 +20,9 @@ from ..environments import (
def do_shell(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `shell` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/commands/units.py b/test/lib/ansible_test/_internal/cli/commands/units.py
index ab7f055c..c541a872 100644
--- a/test/lib/ansible_test/_internal/cli/commands/units.py
+++ b/test/lib/ansible_test/_internal/cli/commands/units.py
@@ -24,9 +24,9 @@ from ..environments import (
def do_units(
- subparsers,
- parent: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
+ subparsers,
+ parent: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
):
"""Command line parsing for the `units` command."""
parser: argparse.ArgumentParser = subparsers.add_parser(
diff --git a/test/lib/ansible_test/_internal/cli/compat.py b/test/lib/ansible_test/_internal/cli/compat.py
index c69b54d7..93006d5c 100644
--- a/test/lib/ansible_test/_internal/cli/compat.py
+++ b/test/lib/ansible_test/_internal/cli/compat.py
@@ -84,25 +84,25 @@ def get_option_name(name: str) -> str:
class PythonVersionUnsupportedError(ApplicationError):
"""A Python version was requested for a context which does not support that version."""
- def __init__(self, context, version, versions):
+ def __init__(self, context: str, version: str, versions: c.Iterable[str]) -> None:
super().__init__(f'Python {version} is not supported by environment `{context}`. Supported Python version(s) are: {", ".join(versions)}')
class PythonVersionUnspecifiedError(ApplicationError):
"""A Python version was not specified for a context which is unknown, thus the Python version is unknown."""
- def __init__(self, context):
+ def __init__(self, context: str) -> None:
super().__init__(f'A Python version was not specified for environment `{context}`. Use the `--python` option to specify a Python version.')
class ControllerNotSupportedError(ApplicationError):
"""Option(s) were specified which do not provide support for the controller and would be ignored because they are irrelevant for the target."""
- def __init__(self, context):
+ def __init__(self, context: str) -> None:
super().__init__(f'Environment `{context}` does not provide a Python version supported by the controller.')
class OptionsConflictError(ApplicationError):
"""Option(s) were specified which conflict with other options."""
- def __init__(self, first, second):
+ def __init__(self, first: c.Iterable[str], second: c.Iterable[str]) -> None:
super().__init__(f'Options `{" ".join(first)}` cannot be combined with options `{" ".join(second)}`.')
@@ -170,30 +170,30 @@ class TargetMode(enum.Enum):
NO_TARGETS = enum.auto() # coverage
@property
- def one_host(self):
+ def one_host(self) -> bool:
"""Return True if only one host (the controller) should be used, otherwise return False."""
return self in (TargetMode.SANITY, TargetMode.UNITS, TargetMode.NO_TARGETS)
@property
- def no_fallback(self):
+ def no_fallback(self) -> bool:
"""Return True if no fallback is acceptable for the controller (due to options not applying to the target), otherwise return False."""
return self in (TargetMode.WINDOWS_INTEGRATION, TargetMode.NETWORK_INTEGRATION, TargetMode.NO_TARGETS)
@property
- def multiple_pythons(self):
+ def multiple_pythons(self) -> bool:
"""Return True if multiple Python versions are allowed, otherwise False."""
return self in (TargetMode.SANITY, TargetMode.UNITS)
@property
- def has_python(self):
+ def has_python(self) -> bool:
"""Return True if this mode uses Python, otherwise False."""
return self in (TargetMode.POSIX_INTEGRATION, TargetMode.SANITY, TargetMode.UNITS, TargetMode.SHELL)
def convert_legacy_args(
- argv: list[str],
- args: t.Union[argparse.Namespace, types.SimpleNamespace],
- mode: TargetMode,
+ argv: list[str],
+ args: t.Union[argparse.Namespace, types.SimpleNamespace],
+ mode: TargetMode,
) -> HostSettings:
"""Convert pre-split host arguments in the given namespace to their split counterparts."""
old_options = LegacyHostOptions.create(args)
@@ -262,9 +262,9 @@ def convert_legacy_args(
def controller_targets(
- mode: TargetMode,
- options: LegacyHostOptions,
- controller: ControllerHostConfig,
+ mode: TargetMode,
+ options: LegacyHostOptions,
+ controller: ControllerHostConfig,
) -> list[HostConfig]:
"""Return the configuration for controller targets."""
python = native_python(options)
@@ -288,8 +288,8 @@ def native_python(options: LegacyHostOptions) -> t.Optional[NativePythonConfig]:
def get_legacy_host_config(
- mode: TargetMode,
- options: LegacyHostOptions,
+ mode: TargetMode,
+ options: LegacyHostOptions,
) -> tuple[ControllerHostConfig, list[HostConfig], t.Optional[FallbackDetail]]:
"""
Returns controller and target host configs derived from the provided legacy host options.
diff --git a/test/lib/ansible_test/_internal/cli/environments.py b/test/lib/ansible_test/_internal/cli/environments.py
index 1dde9e63..5063715a 100644
--- a/test/lib/ansible_test/_internal/cli/environments.py
+++ b/test/lib/ansible_test/_internal/cli/environments.py
@@ -81,10 +81,10 @@ class ControllerMode(enum.Enum):
def add_environments(
- parser: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
- controller_mode: ControllerMode,
- target_mode: TargetMode,
+ parser: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
+ controller_mode: ControllerMode,
+ target_mode: TargetMode,
) -> None:
"""Add arguments for the environments used to run ansible-test and commands it invokes."""
no_environment = controller_mode == ControllerMode.NO_DELEGATION and target_mode == TargetMode.NO_TARGETS
@@ -114,8 +114,8 @@ def add_environments(
def add_global_options(
- parser: argparse.ArgumentParser,
- controller_mode: ControllerMode,
+ parser: argparse.ArgumentParser,
+ controller_mode: ControllerMode,
):
"""Add global options for controlling the test environment that work with both the legacy and composite options."""
global_parser = t.cast(argparse.ArgumentParser, parser.add_argument_group(title='global environment arguments'))
@@ -156,10 +156,10 @@ def add_global_options(
def add_composite_environment_options(
- parser: argparse.ArgumentParser,
- completer: CompositeActionCompletionFinder,
- controller_mode: ControllerMode,
- target_mode: TargetMode,
+ parser: argparse.ArgumentParser,
+ completer: CompositeActionCompletionFinder,
+ controller_mode: ControllerMode,
+ target_mode: TargetMode,
) -> list[t.Type[CompositeAction]]:
"""Add composite options for controlling the test environment."""
composite_parser = t.cast(argparse.ArgumentParser, parser.add_argument_group(
@@ -246,9 +246,9 @@ def add_composite_environment_options(
def add_legacy_environment_options(
- parser: argparse.ArgumentParser,
- controller_mode: ControllerMode,
- target_mode: TargetMode,
+ parser: argparse.ArgumentParser,
+ controller_mode: ControllerMode,
+ target_mode: TargetMode,
):
"""Add legacy options for controlling the test environment."""
environment: argparse.ArgumentParser = parser.add_argument_group( # type: ignore[assignment] # real type private
@@ -259,8 +259,8 @@ def add_legacy_environment_options(
def add_environments_python(
- environments_parser: argparse.ArgumentParser,
- target_mode: TargetMode,
+ environments_parser: argparse.ArgumentParser,
+ target_mode: TargetMode,
) -> None:
"""Add environment arguments to control the Python version(s) used."""
python_versions: tuple[str, ...]
@@ -285,9 +285,9 @@ def add_environments_python(
def add_environments_host(
- environments_parser: argparse.ArgumentParser,
- controller_mode: ControllerMode,
- target_mode: TargetMode,
+ environments_parser: argparse.ArgumentParser,
+ controller_mode: ControllerMode,
+ target_mode: TargetMode,
) -> None:
"""Add environment arguments for the given host and argument modes."""
environments_exclusive_group: argparse.ArgumentParser = environments_parser.add_mutually_exclusive_group() # type: ignore[assignment] # real type private
@@ -341,7 +341,7 @@ def add_environment_network(
def add_environment_windows(
- environments_parser: argparse.ArgumentParser,
+ environments_parser: argparse.ArgumentParser,
) -> None:
"""Add environment arguments for running on a windows host."""
register_completer(environments_parser.add_argument(
@@ -359,7 +359,7 @@ def add_environment_windows(
def add_environment_local(
- exclusive_parser: argparse.ArgumentParser,
+ exclusive_parser: argparse.ArgumentParser,
) -> None:
"""Add environment arguments for running on the local (origin) host."""
exclusive_parser.add_argument(
@@ -370,8 +370,8 @@ def add_environment_local(
def add_environment_venv(
- exclusive_parser: argparse.ArgumentParser,
- environments_parser: argparse.ArgumentParser,
+ exclusive_parser: argparse.ArgumentParser,
+ environments_parser: argparse.ArgumentParser,
) -> None:
"""Add environment arguments for running in ansible-test managed virtual environments."""
exclusive_parser.add_argument(
@@ -387,8 +387,8 @@ def add_environment_venv(
def add_global_docker(
- parser: argparse.ArgumentParser,
- controller_mode: ControllerMode,
+ parser: argparse.ArgumentParser,
+ controller_mode: ControllerMode,
) -> None:
"""Add global options for Docker."""
if controller_mode != ControllerMode.DELEGATED:
@@ -450,9 +450,9 @@ def add_global_docker(
def add_environment_docker(
- exclusive_parser: argparse.ArgumentParser,
- environments_parser: argparse.ArgumentParser,
- target_mode: TargetMode,
+ exclusive_parser: argparse.ArgumentParser,
+ environments_parser: argparse.ArgumentParser,
+ target_mode: TargetMode,
) -> None:
"""Add environment arguments for running in docker containers."""
if target_mode in (TargetMode.POSIX_INTEGRATION, TargetMode.SHELL):
@@ -490,8 +490,8 @@ def add_environment_docker(
def add_global_remote(
- parser: argparse.ArgumentParser,
- controller_mode: ControllerMode,
+ parser: argparse.ArgumentParser,
+ controller_mode: ControllerMode,
) -> None:
"""Add global options for remote instances."""
if controller_mode != ControllerMode.DELEGATED:
@@ -529,9 +529,9 @@ def add_global_remote(
def add_environment_remote(
- exclusive_parser: argparse.ArgumentParser,
- environments_parser: argparse.ArgumentParser,
- target_mode: TargetMode,
+ exclusive_parser: argparse.ArgumentParser,
+ environments_parser: argparse.ArgumentParser,
+ target_mode: TargetMode,
) -> None:
"""Add environment arguments for running in ansible-core-ci provisioned remote virtual machines."""
if target_mode == TargetMode.POSIX_INTEGRATION:
diff --git a/test/lib/ansible_test/_internal/cli/parsers/base_argument_parsers.py b/test/lib/ansible_test/_internal/cli/parsers/base_argument_parsers.py
index ed933bd5..aac7a694 100644
--- a/test/lib/ansible_test/_internal/cli/parsers/base_argument_parsers.py
+++ b/test/lib/ansible_test/_internal/cli/parsers/base_argument_parsers.py
@@ -69,5 +69,5 @@ class TargetsNamespaceParser(NamespaceParser, metaclass=abc.ABCMeta):
class ControllerRequiredFirstError(CompletionError):
"""Exception raised when controller and target options are specified out-of-order."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('The `--controller` option must be specified before `--target` option(s).')
diff --git a/test/lib/ansible_test/_internal/cli/parsers/key_value_parsers.py b/test/lib/ansible_test/_internal/cli/parsers/key_value_parsers.py
index a6af7f80..049b71ee 100644
--- a/test/lib/ansible_test/_internal/cli/parsers/key_value_parsers.py
+++ b/test/lib/ansible_test/_internal/cli/parsers/key_value_parsers.py
@@ -99,7 +99,7 @@ class ControllerKeyValueParser(KeyValueParser):
class DockerKeyValueParser(KeyValueParser):
"""Composite argument parser for docker key/value pairs."""
- def __init__(self, image, controller):
+ def __init__(self, image: str, controller: bool) -> None:
self.controller = controller
self.versions = get_docker_pythons(image, controller, False)
self.allow_default = bool(get_docker_pythons(image, controller, True))
@@ -135,7 +135,7 @@ class DockerKeyValueParser(KeyValueParser):
class PosixRemoteKeyValueParser(KeyValueParser):
"""Composite argument parser for POSIX remote key/value pairs."""
- def __init__(self, name, controller):
+ def __init__(self, name: str, controller: bool) -> None:
self.controller = controller
self.versions = get_remote_pythons(name, controller, False)
self.allow_default = bool(get_remote_pythons(name, controller, True))
diff --git a/test/lib/ansible_test/_internal/commands/coverage/__init__.py b/test/lib/ansible_test/_internal/commands/coverage/__init__.py
index cdf2d544..139cf3c6 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/__init__.py
@@ -2,7 +2,6 @@
from __future__ import annotations
import collections.abc as c
-import errno
import json
import os
import re
@@ -37,7 +36,7 @@ from ...python_requirements import (
install_requirements,
)
-from ... target import (
+from ...target import (
walk_module_targets,
)
@@ -135,11 +134,8 @@ def get_coverage_files(language: str, path: t.Optional[str] = None) -> list[str]
try:
coverage_files = [os.path.join(coverage_dir, f) for f in os.listdir(coverage_dir)
if '=coverage.' in f and '=%s' % language in f]
- except IOError as ex:
- if ex.errno == errno.ENOENT:
- return []
-
- raise
+ except FileNotFoundError:
+ return []
return coverage_files
@@ -162,11 +158,11 @@ def get_python_modules() -> dict[str, str]:
def enumerate_python_arcs(
- path: str,
- coverage: coverage_module,
- modules: dict[str, str],
- collection_search_re: t.Optional[t.Pattern],
- collection_sub_re: t.Optional[t.Pattern],
+ path: str,
+ coverage: coverage_module,
+ modules: dict[str, str],
+ collection_search_re: t.Optional[t.Pattern],
+ collection_sub_re: t.Optional[t.Pattern],
) -> c.Generator[tuple[str, set[tuple[int, int]]], None, None]:
"""Enumerate Python code coverage arcs in the given file."""
if os.path.getsize(path) == 0:
@@ -231,7 +227,7 @@ def read_python_coverage_legacy(path: str) -> PythonArcs:
contents = read_text_file(path)
contents = re.sub(r'''^!coverage.py: This is a private format, don't read it directly!''', '', contents)
data = json.loads(contents)
- arcs: PythonArcs = {filename: [tuple(arc) for arc in arcs] for filename, arcs in data['arcs'].items()}
+ arcs: PythonArcs = {filename: [t.cast(tuple[int, int], tuple(arc)) for arc in arc_list] for filename, arc_list in data['arcs'].items()}
except Exception as ex:
raise CoverageError(path, f'Error reading JSON coverage file: {ex}') from ex
@@ -239,9 +235,9 @@ def read_python_coverage_legacy(path: str) -> PythonArcs:
def enumerate_powershell_lines(
- path: str,
- collection_search_re: t.Optional[t.Pattern],
- collection_sub_re: t.Optional[t.Pattern],
+ path: str,
+ collection_search_re: t.Optional[t.Pattern],
+ collection_sub_re: t.Optional[t.Pattern],
) -> c.Generator[tuple[str, dict[int, int]], None, None]:
"""Enumerate PowerShell code coverage lines in the given file."""
if os.path.getsize(path) == 0:
@@ -278,10 +274,10 @@ def enumerate_powershell_lines(
def sanitize_filename(
- filename: str,
- modules: t.Optional[dict[str, str]] = None,
- collection_search_re: t.Optional[t.Pattern] = None,
- collection_sub_re: t.Optional[t.Pattern] = None,
+ filename: str,
+ modules: t.Optional[dict[str, str]] = None,
+ collection_search_re: t.Optional[t.Pattern] = None,
+ collection_sub_re: t.Optional[t.Pattern] = None,
) -> t.Optional[str]:
"""Convert the given code coverage path to a local absolute path and return its, or None if the path is not valid."""
ansible_path = os.path.abspath('lib/ansible/') + '/'
diff --git a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/__init__.py b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/__init__.py
index f16f7b4f..ad6cf86f 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/__init__.py
@@ -20,6 +20,7 @@ from .. import (
)
TargetKey = t.TypeVar('TargetKey', int, tuple[int, int])
+TFlexKey = t.TypeVar('TFlexKey', int, tuple[int, int], str)
NamedPoints = dict[str, dict[TargetKey, set[str]]]
IndexedPoints = dict[str, dict[TargetKey, set[int]]]
Arcs = dict[str, dict[tuple[int, int], set[int]]]
@@ -118,12 +119,12 @@ def get_target_index(name: str, target_indexes: TargetIndexes) -> int:
def expand_indexes(
- source_data: IndexedPoints,
- source_index: list[str],
- format_func: c.Callable[[TargetKey], str],
-) -> NamedPoints:
+ source_data: IndexedPoints,
+ source_index: list[str],
+ format_func: c.Callable[[TargetKey], TFlexKey],
+) -> dict[str, dict[TFlexKey, set[str]]]:
"""Expand indexes from the source into target names for easier processing of the data (arcs or lines)."""
- combined_data: dict[str, dict[t.Any, set[str]]] = {}
+ combined_data: dict[str, dict[TFlexKey, set[str]]] = {}
for covered_path, covered_points in source_data.items():
combined_points = combined_data.setdefault(covered_path, {})
diff --git a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/combine.py b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/combine.py
index e7698974..e3782cee 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/combine.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/combine.py
@@ -58,10 +58,10 @@ def command_coverage_analyze_targets_combine(args: CoverageAnalyzeTargetsCombine
def merge_indexes(
- source_data: IndexedPoints,
- source_index: list[str],
- combined_data: IndexedPoints,
- combined_index: TargetIndexes,
+ source_data: IndexedPoints,
+ source_index: list[str],
+ combined_data: IndexedPoints,
+ combined_index: TargetIndexes,
) -> None:
"""Merge indexes from the source into the combined data set (arcs or lines)."""
for covered_path, covered_points in source_data.items():
diff --git a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/filter.py b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/filter.py
index f1d8551a..29a8ee5b 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/filter.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/filter.py
@@ -24,6 +24,7 @@ from . import (
from . import (
NamedPoints,
+ TargetKey,
TargetIndexes,
)
@@ -50,8 +51,12 @@ def command_coverage_analyze_targets_filter(args: CoverageAnalyzeTargetsFilterCo
covered_targets, covered_path_arcs, covered_path_lines = read_report(args.input_file)
- filtered_path_arcs = expand_indexes(covered_path_arcs, covered_targets, lambda v: v)
- filtered_path_lines = expand_indexes(covered_path_lines, covered_targets, lambda v: v)
+ def pass_target_key(value: TargetKey) -> TargetKey:
+ """Return the given target key unmodified."""
+ return value
+
+ filtered_path_arcs = expand_indexes(covered_path_arcs, covered_targets, pass_target_key)
+ filtered_path_lines = expand_indexes(covered_path_lines, covered_targets, pass_target_key)
include_targets = set(args.include_targets) if args.include_targets else None
exclude_targets = set(args.exclude_targets) if args.exclude_targets else None
@@ -59,7 +64,7 @@ def command_coverage_analyze_targets_filter(args: CoverageAnalyzeTargetsFilterCo
include_path = re.compile(args.include_path) if args.include_path else None
exclude_path = re.compile(args.exclude_path) if args.exclude_path else None
- def path_filter_func(path):
+ def path_filter_func(path: str) -> bool:
"""Return True if the given path should be included, otherwise return False."""
if include_path and not re.search(include_path, path):
return False
@@ -69,7 +74,7 @@ def command_coverage_analyze_targets_filter(args: CoverageAnalyzeTargetsFilterCo
return True
- def target_filter_func(targets):
+ def target_filter_func(targets: set[str]) -> set[str]:
"""Filter the given targets and return the result based on the defined includes and excludes."""
if include_targets:
targets &= include_targets
@@ -92,9 +97,9 @@ def command_coverage_analyze_targets_filter(args: CoverageAnalyzeTargetsFilterCo
def filter_data(
- data: NamedPoints,
- path_filter_func: c.Callable[[str], bool],
- target_filter_func: c.Callable[[set[str]], set[str]],
+ data: NamedPoints,
+ path_filter_func: c.Callable[[str], bool],
+ target_filter_func: c.Callable[[set[str]], set[str]],
) -> NamedPoints:
"""Filter the data set using the specified filter function."""
result: NamedPoints = {}
diff --git a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/generate.py b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/generate.py
index 2c61190a..127b5b7f 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/generate.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/generate.py
@@ -75,10 +75,10 @@ def command_coverage_analyze_targets_generate(args: CoverageAnalyzeTargetsGenera
def analyze_python_coverage(
- args: CoverageAnalyzeTargetsGenerateConfig,
- host_state: HostState,
- path: str,
- target_indexes: TargetIndexes,
+ args: CoverageAnalyzeTargetsGenerateConfig,
+ host_state: HostState,
+ path: str,
+ target_indexes: TargetIndexes,
) -> Arcs:
"""Analyze Python code coverage."""
results: Arcs = {}
@@ -107,9 +107,9 @@ def analyze_python_coverage(
def analyze_powershell_coverage(
- args: CoverageAnalyzeTargetsGenerateConfig,
- path: str,
- target_indexes: TargetIndexes,
+ args: CoverageAnalyzeTargetsGenerateConfig,
+ path: str,
+ target_indexes: TargetIndexes,
) -> Lines:
"""Analyze PowerShell code coverage"""
results: Lines = {}
@@ -136,9 +136,9 @@ def analyze_powershell_coverage(
def prune_invalid_filenames(
- args: CoverageAnalyzeTargetsGenerateConfig,
- results: dict[str, t.Any],
- collection_search_re: t.Optional[t.Pattern] = None,
+ args: CoverageAnalyzeTargetsGenerateConfig,
+ results: dict[str, t.Any],
+ collection_search_re: t.Optional[t.Pattern] = None,
) -> None:
"""Remove invalid filenames from the given result set."""
path_checker = PathChecker(args, collection_search_re)
diff --git a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/missing.py b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/missing.py
index 4d6b469e..c1c77e75 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/missing.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/analyze/targets/missing.py
@@ -66,11 +66,11 @@ def command_coverage_analyze_targets_missing(args: CoverageAnalyzeTargetsMissing
def find_gaps(
- from_data: IndexedPoints,
- from_index: list[str],
- to_data: IndexedPoints,
- target_indexes: TargetIndexes,
- only_exists: bool,
+ from_data: IndexedPoints,
+ from_index: list[str],
+ to_data: IndexedPoints,
+ target_indexes: TargetIndexes,
+ only_exists: bool,
) -> IndexedPoints:
"""Find gaps in coverage between the from and to data sets."""
target_data: IndexedPoints = {}
@@ -91,12 +91,12 @@ def find_gaps(
def find_missing(
- from_data: IndexedPoints,
- from_index: list[str],
- to_data: IndexedPoints,
- to_index: list[str],
- target_indexes: TargetIndexes,
- only_exists: bool,
+ from_data: IndexedPoints,
+ from_index: list[str],
+ to_data: IndexedPoints,
+ to_index: list[str],
+ target_indexes: TargetIndexes,
+ only_exists: bool,
) -> IndexedPoints:
"""Find coverage in from_data not present in to_data (arcs or lines)."""
target_data: IndexedPoints = {}
diff --git a/test/lib/ansible_test/_internal/commands/coverage/combine.py b/test/lib/ansible_test/_internal/commands/coverage/combine.py
index cb086fd5..66210c73 100644
--- a/test/lib/ansible_test/_internal/commands/coverage/combine.py
+++ b/test/lib/ansible_test/_internal/commands/coverage/combine.py
@@ -101,7 +101,7 @@ def combine_coverage_files(args: CoverageCombineConfig, host_state: HostState) -
class ExportedCoverageDataNotFound(ApplicationError):
"""Exception when no combined coverage data is present yet is required."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__(
'Coverage data must be exported before processing with the `--docker` or `--remote` option.\n'
'Export coverage with `ansible-test coverage combine` using the `--export` option.\n'
@@ -283,9 +283,9 @@ def _get_coverage_targets(args: CoverageCombineConfig, walk_func: c.Callable) ->
def _build_stub_groups(
- args: CoverageCombineConfig,
- sources: list[tuple[str, int]],
- default_stub_value: c.Callable[[list[str]], dict[str, TValue]],
+ args: CoverageCombineConfig,
+ sources: list[tuple[str, int]],
+ default_stub_value: c.Callable[[list[str]], dict[str, TValue]],
) -> dict[str, dict[str, TValue]]:
"""
Split the given list of sources with line counts into groups, maintaining a maximum line count for each group.
diff --git a/test/lib/ansible_test/_internal/commands/integration/__init__.py b/test/lib/ansible_test/_internal/commands/integration/__init__.py
index 33bd45f6..8864d2ee 100644
--- a/test/lib/ansible_test/_internal/commands/integration/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/integration/__init__.py
@@ -240,9 +240,9 @@ def delegate_inventory(args: IntegrationConfig, inventory_path_src: str) -> None
@contextlib.contextmanager
def integration_test_environment(
- args: IntegrationConfig,
- target: IntegrationTarget,
- inventory_path_src: str,
+ args: IntegrationConfig,
+ target: IntegrationTarget,
+ inventory_path_src: str,
) -> c.Iterator[IntegrationEnvironment]:
"""Context manager that prepares the integration test environment and cleans it up."""
ansible_config_src = args.get_ansible_config()
@@ -343,9 +343,9 @@ def integration_test_environment(
@contextlib.contextmanager
def integration_test_config_file(
- args: IntegrationConfig,
- env_config: CloudEnvironmentConfig,
- integration_dir: str,
+ args: IntegrationConfig,
+ env_config: CloudEnvironmentConfig,
+ integration_dir: str,
) -> c.Iterator[t.Optional[str]]:
"""Context manager that provides a config file for integration tests, if needed."""
if not env_config:
@@ -372,10 +372,10 @@ def integration_test_config_file(
def create_inventory(
- args: IntegrationConfig,
- host_state: HostState,
- inventory_path: str,
- target: IntegrationTarget,
+ args: IntegrationConfig,
+ host_state: HostState,
+ inventory_path: str,
+ target: IntegrationTarget,
) -> None:
"""Create inventory."""
if isinstance(args, PosixIntegrationConfig):
@@ -398,13 +398,13 @@ def create_inventory(
def command_integration_filtered(
- args: IntegrationConfig,
- host_state: HostState,
- targets: tuple[IntegrationTarget, ...],
- all_targets: tuple[IntegrationTarget, ...],
- inventory_path: str,
- pre_target: t.Optional[c.Callable[[IntegrationTarget], None]] = None,
- post_target: t.Optional[c.Callable[[IntegrationTarget], None]] = None,
+ args: IntegrationConfig,
+ host_state: HostState,
+ targets: tuple[IntegrationTarget, ...],
+ all_targets: tuple[IntegrationTarget, ...],
+ inventory_path: str,
+ pre_target: t.Optional[c.Callable[[IntegrationTarget], None]] = None,
+ post_target: t.Optional[c.Callable[[IntegrationTarget], None]] = None,
):
"""Run integration tests for the specified targets."""
found = False
@@ -577,12 +577,12 @@ def command_integration_filtered(
def command_integration_script(
- args: IntegrationConfig,
- host_state: HostState,
- target: IntegrationTarget,
- test_dir: str,
- inventory_path: str,
- coverage_manager: CoverageManager,
+ args: IntegrationConfig,
+ host_state: HostState,
+ target: IntegrationTarget,
+ test_dir: str,
+ inventory_path: str,
+ coverage_manager: CoverageManager,
):
"""Run an integration test script."""
display.info('Running %s integration test script' % target.name)
@@ -629,13 +629,13 @@ def command_integration_script(
def command_integration_role(
- args: IntegrationConfig,
- host_state: HostState,
- target: IntegrationTarget,
- start_at_task: t.Optional[str],
- test_dir: str,
- inventory_path: str,
- coverage_manager: CoverageManager,
+ args: IntegrationConfig,
+ host_state: HostState,
+ target: IntegrationTarget,
+ start_at_task: t.Optional[str],
+ test_dir: str,
+ inventory_path: str,
+ coverage_manager: CoverageManager,
):
"""Run an integration test role."""
display.info('Running %s integration test role' % target.name)
@@ -748,15 +748,15 @@ def command_integration_role(
def run_setup_targets(
- args: IntegrationConfig,
- host_state: HostState,
- test_dir: str,
- target_names: c.Sequence[str],
- targets_dict: dict[str, IntegrationTarget],
- targets_executed: set[str],
- inventory_path: str,
- coverage_manager: CoverageManager,
- always: bool,
+ args: IntegrationConfig,
+ host_state: HostState,
+ test_dir: str,
+ target_names: c.Sequence[str],
+ targets_dict: dict[str, IntegrationTarget],
+ targets_executed: set[str],
+ inventory_path: str,
+ coverage_manager: CoverageManager,
+ always: bool,
):
"""Run setup targets."""
for target_name in target_names:
@@ -779,13 +779,13 @@ def run_setup_targets(
def integration_environment(
- args: IntegrationConfig,
- target: IntegrationTarget,
- test_dir: str,
- inventory_path: str,
- ansible_config: t.Optional[str],
- env_config: t.Optional[CloudEnvironmentConfig],
- test_env: IntegrationEnvironment,
+ args: IntegrationConfig,
+ target: IntegrationTarget,
+ test_dir: str,
+ inventory_path: str,
+ ansible_config: t.Optional[str],
+ env_config: t.Optional[CloudEnvironmentConfig],
+ test_env: IntegrationEnvironment,
) -> dict[str, str]:
"""Return a dictionary of environment variables to use when running the given integration test target."""
env = ansible_environment(args, ansible_config=ansible_config)
@@ -819,7 +819,7 @@ def integration_environment(
class IntegrationEnvironment:
"""Details about the integration environment."""
- def __init__(self, test_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file):
+ def __init__(self, test_dir: str, integration_dir: str, targets_dir: str, inventory_path: str, ansible_config: str, vars_file: str) -> None:
self.test_dir = test_dir
self.integration_dir = integration_dir
self.targets_dir = targets_dir
@@ -831,17 +831,13 @@ class IntegrationEnvironment:
class IntegrationCache(CommonCache):
"""Integration cache."""
@property
- def integration_targets(self):
- """
- :rtype: list[IntegrationTarget]
- """
+ def integration_targets(self) -> list[IntegrationTarget]:
+ """The list of integration test targets."""
return self.get('integration_targets', lambda: list(walk_integration_targets()))
@property
- def dependency_map(self):
- """
- :rtype: dict[str, set[IntegrationTarget]]
- """
+ def dependency_map(self) -> dict[str, set[IntegrationTarget]]:
+ """The dependency map of integration test targets."""
return self.get('dependency_map', lambda: generate_dependency_map(self.integration_targets))
diff --git a/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py b/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
index 32d90d6f..0c078b98 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
@@ -288,14 +288,14 @@ class CloudProvider(CloudBase):
exclude.append(skip)
if not self.uses_docker and self.uses_config:
- display.warning('Excluding tests marked "%s" which require config (see "%s"): %s'
- % (skip.rstrip('/'), self.config_template_path, ', '.join(skipped)))
+ display.warning('Excluding tests marked "%s" which require a "%s" config file (see "%s"): %s'
+ % (skip.rstrip('/'), self.config_static_path, self.config_template_path, ', '.join(skipped)))
elif self.uses_docker and not self.uses_config:
display.warning('Excluding tests marked "%s" which requires container support: %s'
% (skip.rstrip('/'), ', '.join(skipped)))
elif self.uses_docker and self.uses_config:
- display.warning('Excluding tests marked "%s" which requires container support or config (see "%s"): %s'
- % (skip.rstrip('/'), self.config_template_path, ', '.join(skipped)))
+ display.warning('Excluding tests marked "%s" which requires container support or a "%s" config file (see "%s"): %s'
+ % (skip.rstrip('/'), self.config_static_path, self.config_template_path, ', '.join(skipped)))
def setup(self) -> None:
"""Setup the cloud resource before delegation and register a cleanup callback."""
diff --git a/test/lib/ansible_test/_internal/commands/integration/cloud/acme.py b/test/lib/ansible_test/_internal/commands/integration/cloud/acme.py
index 8a83ed2b..007d383c 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/acme.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/acme.py
@@ -30,7 +30,7 @@ class ACMEProvider(CloudProvider):
if os.environ.get('ANSIBLE_ACME_CONTAINER'):
self.image = os.environ.get('ANSIBLE_ACME_CONTAINER')
else:
- self.image = 'quay.io/ansible/acme-test-container:2.0.0'
+ self.image = 'quay.io/ansible/acme-test-container:2.1.0'
self.uses_docker = True
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 25a02ff5..0037b423 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/cs.py
@@ -131,12 +131,12 @@ class CsCloudProvider(CloudProvider):
def _get_credentials(self, container_name: str) -> dict[str, t.Any]:
"""Wait for the CloudStack simulator to return credentials."""
- def check(value):
+ def check(value) -> bool:
"""Return True if the given configuration is valid JSON, otherwise return False."""
# noinspection PyBroadException
try:
json.loads(value)
- except Exception: # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
return False # sometimes the file exists but is not yet valid JSON
return True
diff --git a/test/lib/ansible_test/_internal/commands/integration/coverage.py b/test/lib/ansible_test/_internal/commands/integration/coverage.py
index e9917692..5a486e93 100644
--- a/test/lib/ansible_test/_internal/commands/integration/coverage.py
+++ b/test/lib/ansible_test/_internal/commands/integration/coverage.py
@@ -158,7 +158,7 @@ class PosixCoverageHandler(CoverageHandler[PosixConfig]):
self.teardown_controller()
self.teardown_target()
- def setup_controller(self):
+ def setup_controller(self) -> None:
"""Perform setup for code coverage on the controller."""
coverage_config_path = os.path.join(self.common_temp_path, COVERAGE_CONFIG_NAME)
coverage_output_path = os.path.join(self.common_temp_path, ResultType.COVERAGE.name)
@@ -171,7 +171,7 @@ class PosixCoverageHandler(CoverageHandler[PosixConfig]):
os.mkdir(coverage_output_path)
verified_chmod(coverage_output_path, MODE_DIRECTORY_WRITE)
- def setup_target(self):
+ def setup_target(self) -> None:
"""Perform setup for code coverage on the target."""
if not self.target_profile:
return
diff --git a/test/lib/ansible_test/_internal/commands/integration/filters.py b/test/lib/ansible_test/_internal/commands/integration/filters.py
index 1f242bd7..be03d7f4 100644
--- a/test/lib/ansible_test/_internal/commands/integration/filters.py
+++ b/test/lib/ansible_test/_internal/commands/integration/filters.py
@@ -67,12 +67,12 @@ class TargetFilter(t.Generic[THostConfig], metaclass=abc.ABCMeta):
return self.configs[0]
def skip(
- self,
- skip: str,
- reason: str,
- targets: list[IntegrationTarget],
- exclude: set[str],
- override: t.Optional[list[str]] = None,
+ self,
+ skip: str,
+ reason: str,
+ targets: list[IntegrationTarget],
+ exclude: set[str],
+ override: t.Optional[list[str]] = None,
) -> None:
"""Apply the specified skip rule to the given targets by updating the provided exclude list."""
if skip.startswith('skip/'):
diff --git a/test/lib/ansible_test/_internal/commands/sanity/__init__.py b/test/lib/ansible_test/_internal/commands/sanity/__init__.py
index 7aea3988..00b30310 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/__init__.py
@@ -631,11 +631,11 @@ class SanitySkipped(TestSkipped):
class SanityFailure(TestFailure):
"""Sanity test failure."""
def __init__(
- self,
- test: str,
- python_version: t.Optional[str] = None,
- messages: t.Optional[c.Sequence[SanityMessage]] = None,
- summary: t.Optional[str] = None,
+ self,
+ test: str,
+ python_version: t.Optional[str] = None,
+ messages: t.Optional[c.Sequence[SanityMessage]] = None,
+ summary: t.Optional[str] = None,
) -> None:
super().__init__(COMMAND, test, python_version, messages, summary)
@@ -827,7 +827,7 @@ class SanitySingleVersion(SanityTest, metaclass=abc.ABCMeta):
class SanityCodeSmellTest(SanitySingleVersion):
"""Sanity test script."""
- def __init__(self, path):
+ def __init__(self, path) -> None:
name = os.path.splitext(os.path.basename(path))[0]
config_path = os.path.splitext(path)[0] + '.json'
@@ -862,10 +862,10 @@ class SanityCodeSmellTest(SanitySingleVersion):
self.extensions = []
self.prefixes = []
self.files = []
- self.text: t.Optional[bool] = None
+ self.text = None
self.ignore_self = False
- self.minimum_python_version: t.Optional[str] = None
- self.maximum_python_version: t.Optional[str] = None
+ self.minimum_python_version = None
+ self.maximum_python_version = None
self.__all_targets = False
self.__no_targets = True
@@ -1097,11 +1097,11 @@ def sanity_get_tests() -> tuple[SanityTest, ...]:
def create_sanity_virtualenv(
- args: SanityConfig,
- python: PythonConfig,
- name: str,
- coverage: bool = False,
- minimize: bool = False,
+ args: SanityConfig,
+ python: PythonConfig,
+ name: str,
+ coverage: bool = False,
+ minimize: bool = False,
) -> t.Optional[VirtualPythonConfig]:
"""Return an existing sanity virtual environment matching the requested parameters or create a new one."""
commands = collect_requirements( # create_sanity_virtualenv()
diff --git a/test/lib/ansible_test/_internal/commands/sanity/import.py b/test/lib/ansible_test/_internal/commands/sanity/import.py
index d1b2641b..8511d7ac 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/import.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/import.py
@@ -122,8 +122,8 @@ class ImportTest(SanityMultipleVersion):
messages = []
for import_type, test in (
- ('module', _get_module_test(True)),
- ('plugin', _get_module_test(False)),
+ ('module', _get_module_test(True)),
+ ('plugin', _get_module_test(False)),
):
if import_type == 'plugin' and python.version in REMOTE_ONLY_PYTHON_VERSIONS:
continue
diff --git a/test/lib/ansible_test/_internal/commands/sanity/mypy.py b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
index c8497139..cb8ed12c 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/mypy.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
@@ -165,11 +165,11 @@ class MypyTest(SanityMultipleVersion):
@staticmethod
def test_context(
- args: SanityConfig,
- virtualenv_python: VirtualPythonConfig,
- python: PythonConfig,
- context: MyPyContext,
- paths: list[str],
+ args: SanityConfig,
+ virtualenv_python: VirtualPythonConfig,
+ python: PythonConfig,
+ context: MyPyContext,
+ paths: list[str],
) -> list[SanityMessage]:
"""Run mypy tests for the specified context."""
context_paths = [path for path in paths if any(is_subdir(path, match_path) for match_path in context.paths)]
diff --git a/test/lib/ansible_test/_internal/commands/sanity/pylint.py b/test/lib/ansible_test/_internal/commands/sanity/pylint.py
index cd5a8350..86f287ab 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/pylint.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/pylint.py
@@ -58,7 +58,7 @@ from ...host_configs import (
class PylintTest(SanitySingleVersion):
"""Sanity test using pylint."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
self.optional_error_codes.update([
'ansible-deprecated-date',
@@ -189,13 +189,13 @@ class PylintTest(SanitySingleVersion):
@staticmethod
def pylint(
- args: SanityConfig,
- context: str,
- paths: list[str],
- plugin_dir: str,
- plugin_names: list[str],
- python: PythonConfig,
- collection_detail: CollectionDetail,
+ args: SanityConfig,
+ context: str,
+ paths: list[str],
+ plugin_dir: str,
+ plugin_names: list[str],
+ python: PythonConfig,
+ collection_detail: CollectionDetail,
) -> list[dict[str, str]]:
"""Run pylint using the config specified by the context on the specified paths."""
rcfile = os.path.join(SANITY_ROOT, 'pylint', 'config', context.split('/')[0] + '.cfg')
diff --git a/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py b/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
index 9ab8970b..e1dacb7c 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
@@ -59,7 +59,7 @@ from ...host_configs import (
class ValidateModulesTest(SanitySingleVersion):
"""Sanity test using validate-modules."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
self.optional_error_codes.update([
diff --git a/test/lib/ansible_test/_internal/completion.py b/test/lib/ansible_test/_internal/completion.py
index ee096772..f443181c 100644
--- a/test/lib/ansible_test/_internal/completion.py
+++ b/test/lib/ansible_test/_internal/completion.py
@@ -54,7 +54,7 @@ class CompletionConfig(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
- def is_default(self):
+ def is_default(self) -> bool:
"""True if the completion entry is only used for defaults, otherwise False."""
@@ -107,17 +107,17 @@ class RemoteCompletionConfig(CompletionConfig):
arch: t.Optional[str] = None
@property
- def platform(self):
+ def platform(self) -> str:
"""The name of the platform."""
return self.name.partition('/')[0]
@property
- def version(self):
+ def version(self) -> str:
"""The version of the platform."""
return self.name.partition('/')[2]
@property
- def is_default(self):
+ def is_default(self) -> bool:
"""True if the completion entry is only used for defaults, otherwise False."""
return not self.version
@@ -166,7 +166,7 @@ class DockerCompletionConfig(PythonCompletionConfig):
placeholder: bool = False
@property
- def is_default(self):
+ def is_default(self) -> bool:
"""True if the completion entry is only used for defaults, otherwise False."""
return False
@@ -270,13 +270,15 @@ def parse_completion_entry(value: str) -> tuple[str, dict[str, str]]:
def filter_completion(
- completion: dict[str, TCompletionConfig],
- controller_only: bool = False,
- include_defaults: bool = False,
+ completion: dict[str, TCompletionConfig],
+ controller_only: bool = False,
+ include_defaults: bool = False,
) -> dict[str, TCompletionConfig]:
"""Return the given completion dictionary, filtering out configs which do not support the controller if controller_only is specified."""
if controller_only:
- completion = {name: config for name, config in completion.items() if isinstance(config, PosixCompletionConfig) and config.controller_supported}
+ # The cast is needed because mypy gets confused here and forgets that completion values are TCompletionConfig.
+ completion = {name: t.cast(TCompletionConfig, config) for name, config in completion.items() if
+ isinstance(config, PosixCompletionConfig) and config.controller_supported}
if not include_defaults:
completion = {name: config for name, config in completion.items() if not config.is_default}
diff --git a/test/lib/ansible_test/_internal/containers.py b/test/lib/ansible_test/_internal/containers.py
index 95b1718b..a581ecf2 100644
--- a/test/lib/ansible_test/_internal/containers.py
+++ b/test/lib/ansible_test/_internal/containers.py
@@ -108,19 +108,19 @@ class CleanupMode(enum.Enum):
def run_support_container(
- args: EnvironmentConfig,
- context: str,
- image: str,
- name: str,
- ports: list[int],
- aliases: t.Optional[list[str]] = None,
- start: bool = True,
- allow_existing: bool = False,
- cleanup: t.Optional[CleanupMode] = None,
- cmd: t.Optional[list[str]] = None,
- env: t.Optional[dict[str, str]] = None,
- options: t.Optional[list[str]] = None,
- publish_ports: bool = True,
+ args: EnvironmentConfig,
+ context: str,
+ image: str,
+ name: str,
+ ports: list[int],
+ aliases: t.Optional[list[str]] = None,
+ start: bool = True,
+ allow_existing: bool = False,
+ cleanup: t.Optional[CleanupMode] = None,
+ cmd: t.Optional[list[str]] = None,
+ env: t.Optional[dict[str, str]] = None,
+ options: t.Optional[list[str]] = None,
+ publish_ports: bool = True,
) -> t.Optional[ContainerDescriptor]:
"""
Start a container used to support tests, but not run them.
@@ -236,12 +236,12 @@ def run_support_container(
def run_container(
- args: EnvironmentConfig,
- image: str,
- name: str,
- options: t.Optional[list[str]],
- cmd: t.Optional[list[str]] = None,
- create_only: bool = False,
+ args: EnvironmentConfig,
+ image: str,
+ name: str,
+ options: t.Optional[list[str]],
+ cmd: t.Optional[list[str]] = None,
+ create_only: bool = False,
) -> str:
"""Run a container using the given docker image."""
options = list(options or [])
@@ -263,7 +263,7 @@ def run_container(
stdout = docker_run(args, image, options, cmd)[0]
except SubprocessError as ex:
display.error(ex.message)
- display.warning('Failed to run docker image "{image}". Waiting a few seconds before trying again.')
+ display.warning(f'Failed to run docker image "{image}". Waiting a few seconds before trying again.')
docker_rm(args, name) # podman doesn't remove containers after create if run fails
time.sleep(3)
else:
@@ -594,8 +594,8 @@ class SupportContainerContext:
@contextlib.contextmanager
def support_container_context(
- args: EnvironmentConfig,
- ssh: t.Optional[SshConnectionDetail],
+ args: EnvironmentConfig,
+ ssh: t.Optional[SshConnectionDetail],
) -> c.Iterator[t.Optional[ContainerDatabase]]:
"""Create a context manager for integration tests that use support containers."""
if not isinstance(args, (IntegrationConfig, UnitsConfig, SanityConfig, ShellConfig)):
@@ -617,9 +617,9 @@ def support_container_context(
def create_support_container_context(
- args: EnvironmentConfig,
- ssh: t.Optional[SshConnectionDetail],
- containers: ContainerDatabase,
+ args: EnvironmentConfig,
+ ssh: t.Optional[SshConnectionDetail],
+ containers: ContainerDatabase,
) -> SupportContainerContext:
"""Context manager that provides SSH port forwards. Returns updated container metadata."""
host_type = HostType.control
@@ -819,9 +819,9 @@ def create_hosts_entries(context: dict[str, ContainerAccess]) -> list[str]:
def create_container_hooks(
- args: IntegrationConfig,
- control_connections: list[SshConnectionDetail],
- managed_connections: t.Optional[list[SshConnectionDetail]],
+ args: IntegrationConfig,
+ control_connections: list[SshConnectionDetail],
+ managed_connections: t.Optional[list[SshConnectionDetail]],
) -> tuple[t.Optional[c.Callable[[IntegrationTarget], None]], t.Optional[c.Callable[[IntegrationTarget], None]]]:
"""Return pre and post target callbacks for enabling and disabling container access for each test target."""
containers = get_container_database(args)
@@ -844,12 +844,12 @@ def create_container_hooks(
control_state: dict[str, tuple[list[str], list[SshProcess]]] = {}
managed_state: dict[str, tuple[list[str], list[SshProcess]]] = {}
- def pre_target(target):
+ def pre_target(target: IntegrationTarget) -> None:
"""Configure hosts for SSH port forwarding required by the specified target."""
forward_ssh_ports(args, control_connections, '%s_hosts_prepare.yml' % control_type, control_state, target, HostType.control, control_contexts)
forward_ssh_ports(args, managed_connections, '%s_hosts_prepare.yml' % managed_type, managed_state, target, HostType.managed, managed_contexts)
- def post_target(target):
+ def post_target(target: IntegrationTarget) -> None:
"""Clean up previously configured SSH port forwarding which was required by the specified target."""
cleanup_ssh_ports(args, control_connections, '%s_hosts_restore.yml' % control_type, control_state, target, HostType.control)
cleanup_ssh_ports(args, managed_connections, '%s_hosts_restore.yml' % managed_type, managed_state, target, HostType.managed)
@@ -873,13 +873,13 @@ def create_managed_contexts(control_contexts: dict[str, dict[str, ContainerAcces
def forward_ssh_ports(
- args: IntegrationConfig,
- ssh_connections: t.Optional[list[SshConnectionDetail]],
- playbook: str,
- target_state: dict[str, tuple[list[str], list[SshProcess]]],
- target: IntegrationTarget,
- host_type: str,
- contexts: dict[str, dict[str, ContainerAccess]],
+ args: IntegrationConfig,
+ ssh_connections: t.Optional[list[SshConnectionDetail]],
+ playbook: str,
+ target_state: dict[str, tuple[list[str], list[SshProcess]]],
+ target: IntegrationTarget,
+ host_type: str,
+ contexts: dict[str, dict[str, ContainerAccess]],
) -> None:
"""Configure port forwarding using SSH and write hosts file entries."""
if ssh_connections is None:
@@ -944,12 +944,12 @@ def forward_ssh_ports(
def cleanup_ssh_ports(
- args: IntegrationConfig,
- ssh_connections: list[SshConnectionDetail],
- playbook: str,
- target_state: dict[str, tuple[list[str], list[SshProcess]]],
- target: IntegrationTarget,
- host_type: str,
+ args: IntegrationConfig,
+ ssh_connections: list[SshConnectionDetail],
+ playbook: str,
+ target_state: dict[str, tuple[list[str], list[SshProcess]]],
+ target: IntegrationTarget,
+ host_type: str,
) -> None:
"""Stop previously configured SSH port forwarding and remove previously written hosts file entries."""
state = target_state.pop(target.name, None)
diff --git a/test/lib/ansible_test/_internal/core_ci.py b/test/lib/ansible_test/_internal/core_ci.py
index cc2a868f..d62b9039 100644
--- a/test/lib/ansible_test/_internal/core_ci.py
+++ b/test/lib/ansible_test/_internal/core_ci.py
@@ -8,7 +8,6 @@ import os
import re
import traceback
import uuid
-import errno
import time
import typing as t
@@ -114,10 +113,10 @@ class AnsibleCoreCI:
DEFAULT_ENDPOINT = 'https://ansible-core-ci.testing.ansible.com'
def __init__(
- self,
- args: EnvironmentConfig,
- resource: Resource,
- load: bool = True,
+ self,
+ args: EnvironmentConfig,
+ resource: Resource,
+ load: bool = True,
) -> None:
self.args = args
self.resource = resource
@@ -174,11 +173,11 @@ class AnsibleCoreCI:
self.endpoint = self.default_endpoint
@property
- def available(self):
+ def available(self) -> bool:
"""Return True if Ansible Core CI is supported."""
return self.ci_provider.supports_core_ci_auth()
- def start(self):
+ def start(self) -> t.Optional[dict[str, t.Any]]:
"""Start instance."""
if self.started:
display.info(f'Skipping started {self.label} instance.', verbosity=1)
@@ -186,7 +185,7 @@ class AnsibleCoreCI:
return self._start(self.ci_provider.prepare_core_ci_auth())
- def stop(self):
+ def stop(self) -> None:
"""Stop instance."""
if not self.started:
display.info(f'Skipping invalid {self.label} instance.', verbosity=1)
@@ -280,10 +279,10 @@ class AnsibleCoreCI:
raise ApplicationError(f'Timeout waiting for {self.label} instance.')
@property
- def _uri(self):
+ def _uri(self) -> str:
return f'{self.endpoint}/{self.stage}/{self.provider}/{self.instance_id}'
- def _start(self, auth):
+ def _start(self, auth) -> dict[str, t.Any]:
"""Start instance."""
display.info(f'Initializing new {self.label} instance using: {self._uri}', verbosity=1)
@@ -342,23 +341,19 @@ class AnsibleCoreCI:
display.warning(f'{error}. Trying again after {sleep} seconds.')
time.sleep(sleep)
- def _clear(self):
+ def _clear(self) -> None:
"""Clear instance information."""
try:
self.connection = None
os.remove(self.path)
- except OSError as ex:
- if ex.errno != errno.ENOENT:
- raise
+ except FileNotFoundError:
+ pass
- def _load(self):
+ def _load(self) -> bool:
"""Load instance information."""
try:
data = read_text_file(self.path)
- except IOError as ex:
- if ex.errno != errno.ENOENT:
- raise
-
+ except FileNotFoundError:
return False
if not data.startswith('{'):
diff --git a/test/lib/ansible_test/_internal/coverage_util.py b/test/lib/ansible_test/_internal/coverage_util.py
index 43d10718..0f445059 100644
--- a/test/lib/ansible_test/_internal/coverage_util.py
+++ b/test/lib/ansible_test/_internal/coverage_util.py
@@ -143,14 +143,14 @@ def get_sqlite_schema_version(path: str) -> int:
def cover_python(
- args: TestConfig,
- python: PythonConfig,
- cmd: list[str],
- target_name: str,
- env: dict[str, str],
- capture: bool,
- data: t.Optional[str] = None,
- cwd: t.Optional[str] = None,
+ args: TestConfig,
+ python: PythonConfig,
+ cmd: list[str],
+ target_name: str,
+ env: dict[str, str],
+ capture: bool,
+ data: t.Optional[str] = None,
+ cwd: t.Optional[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run a command while collecting Python code coverage."""
if args.coverage:
@@ -176,9 +176,9 @@ def get_coverage_platform(config: HostConfig) -> str:
def get_coverage_environment(
- args: TestConfig,
- target_name: str,
- version: str,
+ args: TestConfig,
+ target_name: str,
+ version: str,
) -> dict[str, str]:
"""Return environment variables needed to collect code coverage."""
# unit tests, sanity tests and other special cases (localhost only)
diff --git a/test/lib/ansible_test/_internal/data.py b/test/lib/ansible_test/_internal/data.py
index 66e21543..635b0c32 100644
--- a/test/lib/ansible_test/_internal/data.py
+++ b/test/lib/ansible_test/_internal/data.py
@@ -52,7 +52,7 @@ from .provider.layout.unsupported import (
class DataContext:
"""Data context providing details about the current execution environment for ansible-test."""
- def __init__(self):
+ def __init__(self) -> None:
content_path = os.environ.get('ANSIBLE_TEST_CONTENT_ROOT')
current_path = os.getcwd()
@@ -245,7 +245,7 @@ class PluginInfo:
@cache
-def content_plugins():
+def content_plugins() -> dict[str, dict[str, PluginInfo]]:
"""
Analyze content.
The primary purpose of this analysis is to facilitate mapping of integration tests to the plugin(s) they are intended to test.
@@ -256,7 +256,7 @@ def content_plugins():
plugin_paths = sorted(data_context().content.walk_files(plugin_directory))
plugin_directory_offset = len(plugin_directory.split(os.path.sep))
- plugin_files = {}
+ plugin_files: dict[str, list[str]] = {}
for plugin_path in plugin_paths:
plugin_filename = os.path.basename(plugin_path)
diff --git a/test/lib/ansible_test/_internal/delegation.py b/test/lib/ansible_test/_internal/delegation.py
index 8c6879d2..0f181a23 100644
--- a/test/lib/ansible_test/_internal/delegation.py
+++ b/test/lib/ansible_test/_internal/delegation.py
@@ -226,7 +226,7 @@ def delegate_command(args: EnvironmentConfig, host_state: HostState, exclude: li
target.on_target_failure() # when the controller is delegated, report failures after delegation fails
-def insert_options(command, options):
+def insert_options(command: list[str], options: list[str]) -> list[str]:
"""Insert addition command line options into the given command and return the result."""
result = []
@@ -267,12 +267,12 @@ def download_results(args: EnvironmentConfig, con: Connection, content_root: str
def generate_command(
- args: EnvironmentConfig,
- python: PythonConfig,
- ansible_bin_path: str,
- content_root: str,
- exclude: list[str],
- require: list[str],
+ args: EnvironmentConfig,
+ python: PythonConfig,
+ ansible_bin_path: str,
+ content_root: str,
+ exclude: list[str],
+ require: list[str],
) -> list[str]:
"""Generate the command necessary to delegate ansible-test."""
cmd = [os.path.join(ansible_bin_path, 'ansible-test')]
@@ -319,10 +319,10 @@ def generate_command(
def filter_options(
- args: EnvironmentConfig,
- argv: list[str],
- exclude: list[str],
- require: list[str],
+ args: EnvironmentConfig,
+ argv: list[str],
+ exclude: list[str],
+ require: list[str],
) -> c.Iterable[str]:
"""Return an iterable that filters out unwanted CLI options and injects new ones as requested."""
replace: list[tuple[str, int, t.Optional[t.Union[bool, str, list[str]]]]] = [
diff --git a/test/lib/ansible_test/_internal/dev/container_probe.py b/test/lib/ansible_test/_internal/dev/container_probe.py
index 84b88f4b..be22e01c 100644
--- a/test/lib/ansible_test/_internal/dev/container_probe.py
+++ b/test/lib/ansible_test/_internal/dev/container_probe.py
@@ -184,7 +184,7 @@ def check_container_cgroup_status(args: EnvironmentConfig, config: DockerConfig,
write_text_file(os.path.join(args.dev_probe_cgroups, f'{identity}.log'), message)
-def get_identity(args: EnvironmentConfig, config: DockerConfig, container_name: str):
+def get_identity(args: EnvironmentConfig, config: DockerConfig, container_name: str) -> str:
"""Generate and return an identity string to use when logging test results."""
engine = require_docker().command
diff --git a/test/lib/ansible_test/_internal/docker_util.py b/test/lib/ansible_test/_internal/docker_util.py
index 77cdd4ee..6c38ddbd 100644
--- a/test/lib/ansible_test/_internal/docker_util.py
+++ b/test/lib/ansible_test/_internal/docker_util.py
@@ -401,11 +401,11 @@ def detect_host_properties(args: CommonConfig) -> ContainerHostProperties:
def run_utility_container(
- args: CommonConfig,
- name: str,
- cmd: list[str],
- options: list[str],
- data: t.Optional[str] = None,
+ args: CommonConfig,
+ name: str,
+ cmd: list[str],
+ options: list[str],
+ data: t.Optional[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run the specified command using the ansible-test utility container, returning stdout and stderr."""
options = options + [
@@ -670,30 +670,30 @@ def docker_cp_to(args: CommonConfig, container_id: str, src: str, dst: str) -> N
def docker_create(
- args: CommonConfig,
- image: str,
- options: list[str],
- cmd: list[str] = None,
+ args: CommonConfig,
+ image: str,
+ options: list[str],
+ cmd: list[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Create a container using the given docker image."""
return docker_command(args, ['create'] + options + [image] + cmd, capture=True)
def docker_run(
- args: CommonConfig,
- image: str,
- options: list[str],
- cmd: list[str] = None,
- data: t.Optional[str] = None,
+ args: CommonConfig,
+ image: str,
+ options: list[str],
+ cmd: list[str] = None,
+ data: t.Optional[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run a container using the given docker image."""
return docker_command(args, ['run'] + options + [image] + cmd, data=data, capture=True)
def docker_start(
- args: CommonConfig,
- container_id: str,
- options: list[str],
+ args: CommonConfig,
+ container_id: str,
+ options: list[str],
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Start a container by name or ID."""
return docker_command(args, ['start'] + options + [container_id], capture=True)
@@ -720,7 +720,7 @@ class DockerError(Exception):
class ContainerNotFoundError(DockerError):
"""The container identified by `identifier` was not found."""
- def __init__(self, identifier):
+ def __init__(self, identifier: str) -> None:
super().__init__('The container "%s" was not found.' % identifier)
self.identifier = identifier
@@ -943,16 +943,16 @@ def docker_logs(args: CommonConfig, container_id: str) -> None:
def docker_exec(
- args: CommonConfig,
- container_id: str,
- cmd: list[str],
- capture: bool,
- options: t.Optional[list[str]] = None,
- stdin: t.Optional[t.IO[bytes]] = None,
- stdout: t.Optional[t.IO[bytes]] = None,
- interactive: bool = False,
- output_stream: t.Optional[OutputStream] = None,
- data: t.Optional[str] = None,
+ args: CommonConfig,
+ container_id: str,
+ cmd: list[str],
+ capture: bool,
+ options: t.Optional[list[str]] = None,
+ stdin: t.Optional[t.IO[bytes]] = None,
+ stdout: t.Optional[t.IO[bytes]] = None,
+ interactive: bool = False,
+ output_stream: t.Optional[OutputStream] = None,
+ data: t.Optional[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Execute the given command in the specified container."""
if not options:
@@ -966,15 +966,15 @@ def docker_exec(
def docker_command(
- args: CommonConfig,
- cmd: list[str],
- capture: bool,
- stdin: t.Optional[t.IO[bytes]] = None,
- stdout: t.Optional[t.IO[bytes]] = None,
- interactive: bool = False,
- output_stream: t.Optional[OutputStream] = None,
- always: bool = False,
- data: t.Optional[str] = None,
+ args: CommonConfig,
+ cmd: list[str],
+ capture: bool,
+ stdin: t.Optional[t.IO[bytes]] = None,
+ stdout: t.Optional[t.IO[bytes]] = None,
+ interactive: bool = False,
+ output_stream: t.Optional[OutputStream] = None,
+ always: bool = False,
+ data: t.Optional[str] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run the specified docker command."""
env = docker_environment()
diff --git a/test/lib/ansible_test/_internal/executor.py b/test/lib/ansible_test/_internal/executor.py
index b079df9f..0c94cf3b 100644
--- a/test/lib/ansible_test/_internal/executor.py
+++ b/test/lib/ansible_test/_internal/executor.py
@@ -81,13 +81,13 @@ def detect_changes(args: TestConfig) -> t.Optional[list[str]]:
class NoChangesDetected(ApplicationWarning):
"""Exception when change detection was performed, but no changes were found."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('No changes detected.')
class NoTestsForChanges(ApplicationWarning):
"""Exception when changes detected, but no tests trigger as a result."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('No tests found for detected changes.')
@@ -111,5 +111,5 @@ class ListTargets(Exception):
class AllTargetsSkipped(ApplicationWarning):
"""All targets skipped."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('All targets skipped.')
diff --git a/test/lib/ansible_test/_internal/host_configs.py b/test/lib/ansible_test/_internal/host_configs.py
index d7671c7f..48d5fd31 100644
--- a/test/lib/ansible_test/_internal/host_configs.py
+++ b/test/lib/ansible_test/_internal/host_configs.py
@@ -48,7 +48,7 @@ from .util import (
@dataclasses.dataclass(frozen=True)
class OriginCompletionConfig(PosixCompletionConfig):
"""Pseudo completion config for the origin."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__(name='origin')
@property
@@ -65,7 +65,7 @@ class OriginCompletionConfig(PosixCompletionConfig):
return version
@property
- def is_default(self):
+ def is_default(self) -> bool:
"""True if the completion entry is only used for defaults, otherwise False."""
return False
@@ -513,7 +513,7 @@ class HostSettings:
with open_binary_file(path) as settings_file:
return pickle.load(settings_file)
- def apply_defaults(self):
+ def apply_defaults(self) -> None:
"""Apply defaults to the host settings."""
context = HostContext(controller_config=None)
self.controller.apply_defaults(context, self.controller.get_defaults(context))
diff --git a/test/lib/ansible_test/_internal/host_profiles.py b/test/lib/ansible_test/_internal/host_profiles.py
index 6575e7c1..0abc9961 100644
--- a/test/lib/ansible_test/_internal/host_profiles.py
+++ b/test/lib/ansible_test/_internal/host_profiles.py
@@ -351,7 +351,7 @@ class RemoteProfile(SshTargetHostProfile[TRemoteConfig], metaclass=abc.ABCMeta):
return self.core_ci
- def delete_instance(self):
+ def delete_instance(self) -> None:
"""Delete the AnsibleCoreCI VM instance."""
core_ci = self.get_instance()
@@ -506,6 +506,13 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
cgroup_version = get_docker_info(self.args).cgroup_version
+ # Podman 4.4.0 updated containers/common to 0.51.0, which removed the SYS_CHROOT capability from the default list.
+ # This capability is needed by services such as sshd, so is unconditionally added here.
+ # See: https://github.com/containers/podman/releases/tag/v4.4.0
+ # See: https://github.com/containers/common/releases/tag/v0.51.0
+ # See: https://github.com/containers/common/pull/1240
+ options.extend(('--cap-add', 'SYS_CHROOT'))
+
# Without AUDIT_WRITE the following errors may appear in the system logs of a container after attempting to log in using SSH:
#
# fatal: linux_audit_write_entry failed: Operation not permitted
@@ -892,7 +899,7 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
return message
- def check_cgroup_requirements(self):
+ def check_cgroup_requirements(self) -> None:
"""Check cgroup requirements for the container."""
cgroup_version = get_docker_info(self.args).cgroup_version
@@ -1411,9 +1418,9 @@ def get_config_profile_type_map() -> dict[t.Type[HostConfig], t.Type[HostProfile
def create_host_profile(
- args: EnvironmentConfig,
- config: HostConfig,
- controller: bool,
+ args: EnvironmentConfig,
+ config: HostConfig,
+ controller: bool,
) -> HostProfile:
"""Create and return a host profile from the given host configuration."""
profile_type = get_config_profile_type_map()[type(config)]
diff --git a/test/lib/ansible_test/_internal/io.py b/test/lib/ansible_test/_internal/io.py
index a7e9f1b2..80d47699 100644
--- a/test/lib/ansible_test/_internal/io.py
+++ b/test/lib/ansible_test/_internal/io.py
@@ -1,7 +1,6 @@
"""Functions for disk IO."""
from __future__ import annotations
-import errno
import io
import json
import os
@@ -32,11 +31,7 @@ def read_binary_file(path: str) -> bytes:
def make_dirs(path: str) -> None:
"""Create a directory at path, including any necessary parent directories."""
- try:
- os.makedirs(to_bytes(path))
- except OSError as ex:
- if ex.errno != errno.EEXIST:
- raise
+ os.makedirs(to_bytes(path), exist_ok=True)
def write_json_file(path: str,
@@ -85,7 +80,7 @@ def open_binary_file(path: str, mode: str = 'rb') -> t.IO[bytes]:
class SortedSetEncoder(json.JSONEncoder):
"""Encode sets as sorted lists."""
- def default(self, o):
+ def default(self, o: t.Any) -> t.Any:
"""Return a serialized version of the `o` object."""
if isinstance(o, set):
return sorted(o)
diff --git a/test/lib/ansible_test/_internal/metadata.py b/test/lib/ansible_test/_internal/metadata.py
index e969f029..94bbc34a 100644
--- a/test/lib/ansible_test/_internal/metadata.py
+++ b/test/lib/ansible_test/_internal/metadata.py
@@ -19,7 +19,7 @@ from .diff import (
class Metadata:
"""Metadata object for passing data to delegated tests."""
- def __init__(self):
+ def __init__(self) -> None:
"""Initialize metadata."""
self.changes: dict[str, tuple[tuple[int, int], ...]] = {}
self.cloud_config: t.Optional[dict[str, dict[str, t.Union[int, str, bool]]]] = None
@@ -82,7 +82,7 @@ class Metadata:
class ChangeDescription:
"""Description of changes."""
- def __init__(self):
+ def __init__(self) -> None:
self.command: str = ''
self.changed_paths: list[str] = []
self.deleted_paths: list[str] = []
diff --git a/test/lib/ansible_test/_internal/provider/layout/__init__.py b/test/lib/ansible_test/_internal/provider/layout/__init__.py
index 2e8026bf..aa6693f0 100644
--- a/test/lib/ansible_test/_internal/provider/layout/__init__.py
+++ b/test/lib/ansible_test/_internal/provider/layout/__init__.py
@@ -150,7 +150,7 @@ class ContentLayout(Layout):
class LayoutMessages:
"""Messages generated during layout creation that should be deferred for later display."""
- def __init__(self):
+ def __init__(self) -> None:
self.info: list[str] = []
self.warning: list[str] = []
self.error: list[str] = []
diff --git a/test/lib/ansible_test/_internal/provisioning.py b/test/lib/ansible_test/_internal/provisioning.py
index 8f914c2a..7547a302 100644
--- a/test/lib/ansible_test/_internal/provisioning.py
+++ b/test/lib/ansible_test/_internal/provisioning.py
@@ -97,10 +97,10 @@ class HostState:
def prepare_profiles(
- args: TEnvironmentConfig,
- targets_use_pypi: bool = False,
- skip_setup: bool = False,
- requirements: t.Optional[c.Callable[[HostProfile], None]] = None,
+ args: TEnvironmentConfig,
+ targets_use_pypi: bool = False,
+ skip_setup: bool = False,
+ requirements: t.Optional[c.Callable[[HostProfile], None]] = None,
) -> HostState:
"""
Create new profiles, or load existing ones, and return them.
diff --git a/test/lib/ansible_test/_internal/pypi_proxy.py b/test/lib/ansible_test/_internal/pypi_proxy.py
index fa26b5fd..97663ead 100644
--- a/test/lib/ansible_test/_internal/pypi_proxy.py
+++ b/test/lib/ansible_test/_internal/pypi_proxy.py
@@ -119,7 +119,7 @@ def configure_target_pypi_proxy(args: EnvironmentConfig, profile: HostProfile, p
create_posix_inventory(args, inventory_path, [profile])
- def cleanup_pypi_proxy():
+ def cleanup_pypi_proxy() -> None:
"""Undo changes made to configure the PyPI proxy."""
run_playbook(args, inventory_path, 'pypi_proxy_restore.yml', capture=True)
diff --git a/test/lib/ansible_test/_internal/python_requirements.py b/test/lib/ansible_test/_internal/python_requirements.py
index 44cf53ae..e3733a5c 100644
--- a/test/lib/ansible_test/_internal/python_requirements.py
+++ b/test/lib/ansible_test/_internal/python_requirements.py
@@ -122,14 +122,14 @@ class PipBootstrap(PipCommand):
def install_requirements(
- args: EnvironmentConfig,
- python: PythonConfig,
- ansible: bool = False,
- command: bool = False,
- coverage: bool = False,
- virtualenv: bool = False,
- controller: bool = True,
- connection: t.Optional[Connection] = None,
+ args: EnvironmentConfig,
+ python: PythonConfig,
+ ansible: bool = False,
+ command: bool = False,
+ coverage: bool = False,
+ virtualenv: bool = False,
+ controller: bool = True,
+ connection: t.Optional[Connection] = None,
) -> None:
"""Install requirements for the given Python using the specified arguments."""
create_result_directories(args)
@@ -197,15 +197,15 @@ def collect_bootstrap(python: PythonConfig) -> list[PipCommand]:
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],
+ python: PythonConfig,
+ controller: bool,
+ ansible: bool,
+ cryptography: bool,
+ coverage: bool,
+ virtualenv: bool,
+ minimize: bool,
+ command: t.Optional[str],
+ sanity: t.Optional[str],
) -> list[PipCommand]:
"""Collect requirements for the given Python using the specified arguments."""
commands: list[PipCommand] = []
@@ -252,10 +252,10 @@ def collect_requirements(
def run_pip(
- args: EnvironmentConfig,
- python: PythonConfig,
- commands: list[PipCommand],
- connection: t.Optional[Connection],
+ args: EnvironmentConfig,
+ python: PythonConfig,
+ commands: list[PipCommand],
+ connection: t.Optional[Connection],
) -> None:
"""Run the specified pip commands for the given Python, and optionally the specified host."""
connection = connection or LocalConnection(args)
@@ -367,10 +367,10 @@ def collect_integration_install(command: str, controller: bool) -> list[PipInsta
def collect_install(
- requirements_paths: list[tuple[str, str]],
- constraints_paths: list[tuple[str, str]],
- packages: t.Optional[list[str]] = None,
- constraints: bool = True,
+ requirements_paths: list[tuple[str, str]],
+ constraints_paths: list[tuple[str, str]],
+ packages: t.Optional[list[str]] = None,
+ constraints: bool = True,
) -> list[PipInstall]:
"""Build a pip install list from the given requirements, constraints and packages."""
# listing content constraints first gives them priority over constraints provided by ansible-test
diff --git a/test/lib/ansible_test/_internal/ssh.py b/test/lib/ansible_test/_internal/ssh.py
index fd01ff25..840edf62 100644
--- a/test/lib/ansible_test/_internal/ssh.py
+++ b/test/lib/ansible_test/_internal/ssh.py
@@ -151,10 +151,10 @@ class SshProcess:
def create_ssh_command(
- ssh: SshConnectionDetail,
- options: t.Optional[dict[str, t.Union[str, int]]] = None,
- cli_args: list[str] = None,
- command: t.Optional[str] = None,
+ ssh: SshConnectionDetail,
+ options: t.Optional[dict[str, t.Union[str, int]]] = None,
+ cli_args: list[str] = None,
+ command: t.Optional[str] = None,
) -> list[str]:
"""Create an SSH command using the specified options."""
cmd = [
@@ -207,11 +207,11 @@ def ssh_options_to_str(options: t.Union[dict[str, t.Union[int, str]], dict[str,
def run_ssh_command(
- args: EnvironmentConfig,
- ssh: SshConnectionDetail,
- options: t.Optional[dict[str, t.Union[str, int]]] = None,
- cli_args: list[str] = None,
- command: t.Optional[str] = None,
+ args: EnvironmentConfig,
+ ssh: SshConnectionDetail,
+ options: t.Optional[dict[str, t.Union[str, int]]] = None,
+ cli_args: list[str] = None,
+ command: t.Optional[str] = None,
) -> SshProcess:
"""Run the specified SSH command, returning the created SshProcess instance created."""
cmd = create_ssh_command(ssh, options, cli_args, command)
@@ -233,9 +233,9 @@ def run_ssh_command(
def create_ssh_port_forwards(
- args: EnvironmentConfig,
- ssh: SshConnectionDetail,
- forwards: list[tuple[str, int]],
+ args: EnvironmentConfig,
+ ssh: SshConnectionDetail,
+ forwards: list[tuple[str, int]],
) -> SshProcess:
"""
Create SSH port forwards using the provided list of tuples (target_host, target_port).
@@ -257,9 +257,9 @@ def create_ssh_port_forwards(
def create_ssh_port_redirects(
- args: EnvironmentConfig,
- ssh: SshConnectionDetail,
- redirects: list[tuple[int, str, int]],
+ args: EnvironmentConfig,
+ ssh: SshConnectionDetail,
+ redirects: list[tuple[int, str, int]],
) -> SshProcess:
"""Create SSH port redirections using the provided list of tuples (bind_port, target_host, target_port)."""
options: dict[str, t.Union[str, int]] = {}
diff --git a/test/lib/ansible_test/_internal/target.py b/test/lib/ansible_test/_internal/target.py
index 4e04b10a..80411483 100644
--- a/test/lib/ansible_test/_internal/target.py
+++ b/test/lib/ansible_test/_internal/target.py
@@ -65,31 +65,30 @@ def walk_completion_targets(targets: c.Iterable[CompletionTarget], prefix: str,
def walk_internal_targets(
- targets: c.Iterable[TCompletionTarget],
- includes: t.Optional[list[str]] = None,
- excludes: t.Optional[list[str]] = None,
- requires: t.Optional[list[str]] = None,
+ targets: c.Iterable[TCompletionTarget],
+ includes: t.Optional[list[str]] = None,
+ excludes: t.Optional[list[str]] = None,
+ requires: t.Optional[list[str]] = None,
) -> tuple[TCompletionTarget, ...]:
"""Return a tuple of matching completion targets."""
targets = tuple(targets)
- include_targets = sorted(filter_targets(targets, includes, directories=False), key=lambda include_target: include_target.name)
+ include_targets = sorted(filter_targets(targets, includes), key=lambda include_target: include_target.name)
if requires:
- require_targets = set(filter_targets(targets, requires, directories=False))
+ require_targets = set(filter_targets(targets, requires))
include_targets = [require_target for require_target in include_targets if require_target in require_targets]
if excludes:
- list(filter_targets(targets, excludes, include=False, directories=False))
+ list(filter_targets(targets, excludes, include=False))
- internal_targets = set(filter_targets(include_targets, excludes, errors=False, include=False, directories=False))
+ internal_targets = set(filter_targets(include_targets, excludes, errors=False, include=False))
return tuple(sorted(internal_targets, key=lambda sort_target: sort_target.name))
def filter_targets(targets: c.Iterable[TCompletionTarget],
patterns: list[str],
include: bool = True,
- directories: bool = True,
errors: bool = True,
) -> c.Iterable[TCompletionTarget]:
"""Iterate over the given targets and filter them based on the supplied arguments."""
@@ -130,20 +129,15 @@ def filter_targets(targets: c.Iterable[TCompletionTarget],
if match != include:
continue
- if directories and matched_directories:
- yield DirectoryTarget(to_text(sorted(matched_directories, key=len)[0]), target.modules)
- else:
- yield target
+ yield target
if errors:
if unmatched:
raise TargetPatternsNotMatched(unmatched)
-def walk_module_targets():
- """
- :rtype: collections.Iterable[TestTarget]
- """
+def walk_module_targets() -> c.Iterable[TestTarget]:
+ """Iterate through the module test targets."""
for target in walk_test_targets(path=data_context().content.module_path, module_path=data_context().content.module_path, extensions=MODULE_EXTENSIONS):
if not target.module:
continue
@@ -248,10 +242,8 @@ def walk_integration_targets() -> c.Iterable[IntegrationTarget]:
yield IntegrationTarget(to_text(path), modules, prefixes)
-def load_integration_prefixes():
- """
- :rtype: dict[str, str]
- """
+def load_integration_prefixes() -> dict[str, str]:
+ """Load and return the integration test prefixes."""
path = data_context().content.integration_path
file_paths = sorted(f for f in data_context().content.get_files(path) if os.path.splitext(os.path.basename(f))[0] == 'target-prefixes')
prefixes = {}
@@ -264,13 +256,13 @@ def load_integration_prefixes():
def walk_test_targets(
- path: t.Optional[str] = None,
- module_path: t.Optional[str] = None,
- extensions: t.Optional[tuple[str, ...]] = None,
- prefix: t.Optional[str] = None,
- extra_dirs: t.Optional[tuple[str, ...]] = None,
- include_symlinks: bool = False,
- include_symlinked_directories: bool = False,
+ path: t.Optional[str] = None,
+ module_path: t.Optional[str] = None,
+ extensions: t.Optional[tuple[str, ...]] = None,
+ prefix: t.Optional[str] = None,
+ extra_dirs: t.Optional[tuple[str, ...]] = None,
+ include_symlinks: bool = False,
+ include_symlinked_directories: bool = False,
) -> c.Iterable[TestTarget]:
"""Iterate over available test targets."""
if path:
@@ -317,7 +309,7 @@ def analyze_integration_target_dependencies(integration_targets: list[Integratio
role_targets = [target for target in integration_targets if target.type == 'role']
hidden_role_target_names = set(target.name for target in role_targets if 'hidden/' in target.aliases)
- dependencies = collections.defaultdict(set)
+ dependencies: collections.defaultdict[str, set[str]] = collections.defaultdict(set)
# handle setup dependencies
for target in integration_targets:
@@ -409,12 +401,12 @@ def analyze_integration_target_dependencies(integration_targets: list[Integratio
class CompletionTarget(metaclass=abc.ABCMeta):
"""Command-line argument completion target base class."""
- def __init__(self):
- self.name = None
- self.path = None
- self.base_path = None
- self.modules = tuple()
- self.aliases = tuple()
+ def __init__(self) -> None:
+ self.name = ''
+ self.path = ''
+ self.base_path: t.Optional[str] = None
+ self.modules: tuple[str, ...] = tuple()
+ self.aliases: tuple[str, ...] = tuple()
def __eq__(self, other):
if isinstance(other, CompletionTarget):
@@ -441,26 +433,16 @@ class CompletionTarget(metaclass=abc.ABCMeta):
return self.name
-class DirectoryTarget(CompletionTarget):
- """Directory target."""
- def __init__(self, path: str, modules: tuple[str, ...]) -> None:
- super().__init__()
-
- self.name = path
- self.path = path
- self.modules = modules
-
-
class TestTarget(CompletionTarget):
"""Generic test target."""
def __init__(
- self,
- path: str,
- module_path: t.Optional[str],
- module_prefix: t.Optional[str],
- base_path: str,
- symlink: t.Optional[bool] = None,
- ):
+ self,
+ path: str,
+ module_path: t.Optional[str],
+ module_prefix: t.Optional[str],
+ base_path: str,
+ symlink: t.Optional[bool] = None,
+ ) -> None:
super().__init__()
if symlink is None:
@@ -679,8 +661,6 @@ class IntegrationTarget(CompletionTarget):
target_type, actual_type = categorize_integration_test(self.name, list(static_aliases), force_target)
- self._remove_group(groups, 'context')
-
groups.extend(['context/', f'context/{target_type.name.lower()}'])
if target_type != actual_type:
@@ -709,10 +689,6 @@ class IntegrationTarget(CompletionTarget):
self.setup_always = tuple(sorted(set(g.split('/')[2] for g in groups if g.startswith('setup/always/'))))
self.needs_target = tuple(sorted(set(g.split('/')[2] for g in groups if g.startswith('needs/target/'))))
- @staticmethod
- def _remove_group(groups, group):
- return [g for g in groups if g != group and not g.startswith('%s/' % group)]
-
class TargetPatternsNotMatched(ApplicationError):
"""One or more targets were not matched when a match was required."""
diff --git a/test/lib/ansible_test/_internal/test.py b/test/lib/ansible_test/_internal/test.py
index da6af355..211635c5 100644
--- a/test/lib/ansible_test/_internal/test.py
+++ b/test/lib/ansible_test/_internal/test.py
@@ -215,12 +215,12 @@ class TestSkipped(TestResult):
class TestFailure(TestResult):
"""Test failure."""
def __init__(
- self,
- command: str,
- test: str,
- python_version: t.Optional[str] = None,
- messages: t.Optional[c.Sequence[TestMessage]] = None,
- summary: t.Optional[str] = None,
+ self,
+ command: str,
+ test: str,
+ python_version: t.Optional[str] = None,
+ messages: t.Optional[c.Sequence[TestMessage]] = None,
+ summary: t.Optional[str] = None,
):
super().__init__(command, test, python_version)
@@ -333,10 +333,8 @@ class TestFailure(TestResult):
return command
- def find_docs(self):
- """
- :rtype: str
- """
+ def find_docs(self) -> t.Optional[str]:
+ """Return the docs URL for this test or None if there is no docs URL."""
if self.command != 'sanity':
return None # only sanity tests have docs links
@@ -381,14 +379,14 @@ class TestFailure(TestResult):
class TestMessage:
"""Single test message for one file."""
def __init__(
- self,
- message: str,
- path: str,
- line: int = 0,
- column: int = 0,
- level: str = 'error',
- code: t.Optional[str] = None,
- confidence: t.Optional[int] = None,
+ self,
+ message: str,
+ path: str,
+ line: int = 0,
+ column: int = 0,
+ level: str = 'error',
+ code: t.Optional[str] = None,
+ confidence: t.Optional[int] = None,
):
self.__path = path
self.__line = line
diff --git a/test/lib/ansible_test/_internal/thread.py b/test/lib/ansible_test/_internal/thread.py
index d0ed1bab..edaf1b5c 100644
--- a/test/lib/ansible_test/_internal/thread.py
+++ b/test/lib/ansible_test/_internal/thread.py
@@ -21,7 +21,7 @@ class WrappedThread(threading.Thread):
self.action = action
self.result = None
- def run(self):
+ def run(self) -> None:
"""
Run action and capture results or exception.
Do not override. Do not call directly. Executed by the start() method.
@@ -35,11 +35,8 @@ class WrappedThread(threading.Thread):
except: # noqa
self._result.put((None, sys.exc_info()))
- def wait_for_result(self):
- """
- Wait for thread to exit and return the result or raise an exception.
- :rtype: any
- """
+ def wait_for_result(self) -> t.Any:
+ """Wait for thread to exit and return the result or raise an exception."""
result, exception = self._result.get()
if exception:
diff --git a/test/lib/ansible_test/_internal/timeout.py b/test/lib/ansible_test/_internal/timeout.py
index da5cfceb..90ba5835 100644
--- a/test/lib/ansible_test/_internal/timeout.py
+++ b/test/lib/ansible_test/_internal/timeout.py
@@ -75,7 +75,7 @@ def configure_test_timeout(args: TestConfig) -> None:
display.info('The %d minute test timeout expires in %s at %s.' % (
timeout_duration, timeout_remaining, timeout_deadline), verbosity=1)
- def timeout_handler(_dummy1, _dummy2):
+ def timeout_handler(_dummy1: t.Any, _dummy2: t.Any) -> None:
"""Runs when SIGUSR1 is received."""
test_timeout.write(args)
diff --git a/test/lib/ansible_test/_internal/util.py b/test/lib/ansible_test/_internal/util.py
index 12316239..ec485a2b 100644
--- a/test/lib/ansible_test/_internal/util.py
+++ b/test/lib/ansible_test/_internal/util.py
@@ -3,7 +3,6 @@ from __future__ import annotations
import abc
import collections.abc as c
-import errno
import enum
import fcntl
import importlib.util
@@ -346,19 +345,19 @@ def get_available_python_versions() -> dict[str, str]:
def raw_command(
- cmd: c.Iterable[str],
- capture: bool,
- env: t.Optional[dict[str, str]] = None,
- data: t.Optional[str] = None,
- cwd: t.Optional[str] = None,
- explain: bool = False,
- stdin: t.Optional[t.Union[t.IO[bytes], int]] = None,
- stdout: t.Optional[t.Union[t.IO[bytes], int]] = None,
- interactive: bool = False,
- output_stream: t.Optional[OutputStream] = None,
- cmd_verbosity: int = 1,
- str_errors: str = 'strict',
- error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
+ cmd: c.Iterable[str],
+ capture: bool,
+ env: t.Optional[dict[str, str]] = None,
+ data: t.Optional[str] = None,
+ cwd: t.Optional[str] = None,
+ explain: bool = False,
+ stdin: t.Optional[t.Union[t.IO[bytes], int]] = None,
+ stdout: t.Optional[t.Union[t.IO[bytes], int]] = None,
+ interactive: bool = False,
+ output_stream: t.Optional[OutputStream] = None,
+ cmd_verbosity: int = 1,
+ str_errors: str = 'strict',
+ error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run the specified command and return stdout and stderr as a tuple."""
output_stream = output_stream or OutputStream.AUTO
@@ -467,10 +466,8 @@ def raw_command(
cmd_bytes = [to_bytes(arg) for arg in cmd]
env_bytes = dict((to_bytes(k), to_bytes(v)) for k, v in env.items())
process = subprocess.Popen(cmd_bytes, env=env_bytes, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd) # pylint: disable=consider-using-with
- except OSError as ex:
- if ex.errno == errno.ENOENT:
- raise ApplicationError('Required program "%s" not found.' % cmd[0])
- raise
+ except FileNotFoundError as ex:
+ raise ApplicationError('Required program "%s" not found.' % cmd[0]) from ex
if communicate:
data_bytes = to_optional_bytes(data)
@@ -499,12 +496,12 @@ def raw_command(
def communicate_with_process(
- process: subprocess.Popen,
- stdin: t.Optional[bytes],
- stdout: bool,
- stderr: bool,
- capture: bool,
- output_stream: OutputStream,
+ process: subprocess.Popen,
+ stdin: t.Optional[bytes],
+ stdout: bool,
+ stderr: bool,
+ capture: bool,
+ output_stream: OutputStream,
) -> tuple[bytes, bytes]:
"""Communicate with the specified process, handling stdin/stdout/stderr as requested."""
threads: list[WrappedThread] = []
@@ -614,7 +611,7 @@ class OutputThread(ReaderThread):
src.close()
-def common_environment():
+def common_environment() -> dict[str, str]:
"""Common environment used for executing all programs."""
env = dict(
LC_ALL=CONFIGURED_LOCALE,
@@ -694,12 +691,11 @@ def verified_chmod(path: str, mode: int) -> None:
def remove_tree(path: str) -> None:
- """Remove the specified directory, siliently continuing if the directory does not exist."""
+ """Remove the specified directory, silently continuing if the directory does not exist."""
try:
shutil.rmtree(to_bytes(path))
- except OSError as ex:
- if ex.errno != errno.ENOENT:
- raise
+ except FileNotFoundError:
+ pass
def is_binary_file(path: str) -> bool:
@@ -797,17 +793,17 @@ class Display:
3: cyan,
}
- def __init__(self):
+ def __init__(self) -> None:
self.verbosity = 0
self.color = sys.stdout.isatty()
- self.warnings = []
- self.warnings_unique = set()
+ self.warnings: list[str] = []
+ self.warnings_unique: set[str] = set()
self.fd = sys.stderr # default to stderr until config is initialized to avoid early messages going to stdout
self.rows = 0
self.columns = 0
self.truncate = 0
self.redact = True
- self.sensitive = set()
+ self.sensitive: set[str] = set()
if os.isatty(0):
self.rows, self.columns = unpack('HHHH', fcntl.ioctl(0, TIOCGWINSZ, pack('HHHH', 0, 0, 0, 0)))[:2]
@@ -859,11 +855,11 @@ class Display:
self.print_message(message, color=color, truncate=truncate)
def print_message( # pylint: disable=locally-disabled, invalid-name
- self,
- message: str,
- color: t.Optional[str] = None,
- stderr: bool = False,
- truncate: bool = False,
+ self,
+ message: str,
+ color: t.Optional[str] = None,
+ stderr: bool = False,
+ truncate: bool = False,
) -> None:
"""Display a message."""
if self.redact and self.sensitive:
@@ -905,13 +901,13 @@ class ApplicationWarning(Exception):
class SubprocessError(ApplicationError):
"""Error resulting from failed subprocess execution."""
def __init__(
- self,
- cmd: list[str],
- status: int = 0,
- stdout: t.Optional[str] = None,
- stderr: t.Optional[str] = None,
- runtime: t.Optional[float] = None,
- error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
+ self,
+ cmd: list[str],
+ status: int = 0,
+ stdout: t.Optional[str] = None,
+ stderr: t.Optional[str] = None,
+ runtime: t.Optional[float] = None,
+ error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
) -> None:
message = 'Command "%s" returned exit status %s.\n' % (shlex.join(cmd), status)
@@ -963,7 +959,7 @@ class HostConnectionError(ApplicationError):
self._callback()
-def retry(func, ex_type=SubprocessError, sleep=10, attempts=10, warn=True):
+def retry(func: t.Callable[..., TValue], ex_type: t.Type[BaseException] = SubprocessError, sleep: int = 10, attempts: int = 10, warn: bool = True) -> TValue:
"""Retry the specified function on failure."""
for dummy in range(1, attempts):
try:
@@ -1094,7 +1090,7 @@ def load_module(path: str, name: str) -> None:
spec.loader.exec_module(module)
-def sanitize_host_name(name):
+def sanitize_host_name(name: str) -> str:
"""Return a sanitized version of the given name, suitable for use as a hostname."""
return re.sub('[^A-Za-z0-9]+', '-', name)[:63].strip('-')
diff --git a/test/lib/ansible_test/_internal/util_common.py b/test/lib/ansible_test/_internal/util_common.py
index fbd9e71d..1dfc7f38 100644
--- a/test/lib/ansible_test/_internal/util_common.py
+++ b/test/lib/ansible_test/_internal/util_common.py
@@ -96,7 +96,7 @@ class ResultType:
TMP: ResultType = None
@staticmethod
- def _populate():
+ def _populate() -> None:
ResultType.BOT = ResultType('bot')
ResultType.COVERAGE = ResultType('coverage')
ResultType.DATA = ResultType('data')
@@ -288,7 +288,7 @@ def get_injector_path() -> str:
verified_chmod(injector_path, MODE_DIRECTORY)
- def cleanup_injector():
+ def cleanup_injector() -> None:
"""Remove the temporary injector directory."""
remove_tree(injector_path)
@@ -388,7 +388,7 @@ def create_interpreter_wrapper(interpreter: str, injected_interpreter: str) -> N
verified_chmod(injected_interpreter, MODE_FILE_EXECUTE)
-def cleanup_python_paths():
+def cleanup_python_paths() -> None:
"""Clean up all temporary python directories."""
for path in sorted(PYTHON_PATHS.values()):
display.info('Cleaning up temporary python directory: %s' % path, verbosity=2)
@@ -396,14 +396,14 @@ def cleanup_python_paths():
def intercept_python(
- args: CommonConfig,
- python: PythonConfig,
- cmd: list[str],
- env: dict[str, str],
- capture: bool,
- data: t.Optional[str] = None,
- cwd: t.Optional[str] = None,
- always: bool = False,
+ args: CommonConfig,
+ python: PythonConfig,
+ cmd: list[str],
+ env: dict[str, str],
+ capture: bool,
+ data: t.Optional[str] = None,
+ cwd: t.Optional[str] = None,
+ always: bool = False,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""
Run a command while intercepting invocations of Python to control the version used.
@@ -428,20 +428,20 @@ def intercept_python(
def run_command(
- args: CommonConfig,
- cmd: c.Iterable[str],
- capture: bool,
- env: t.Optional[dict[str, str]] = None,
- data: t.Optional[str] = None,
- cwd: t.Optional[str] = None,
- always: bool = False,
- stdin: t.Optional[t.IO[bytes]] = None,
- stdout: t.Optional[t.IO[bytes]] = None,
- interactive: bool = False,
- output_stream: t.Optional[OutputStream] = None,
- cmd_verbosity: int = 1,
- str_errors: str = 'strict',
- error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
+ args: CommonConfig,
+ cmd: c.Iterable[str],
+ capture: bool,
+ env: t.Optional[dict[str, str]] = None,
+ data: t.Optional[str] = None,
+ cwd: t.Optional[str] = None,
+ always: bool = False,
+ stdin: t.Optional[t.IO[bytes]] = None,
+ stdout: t.Optional[t.IO[bytes]] = None,
+ interactive: bool = False,
+ output_stream: t.Optional[OutputStream] = None,
+ cmd_verbosity: int = 1,
+ str_errors: str = 'strict',
+ error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None,
) -> tuple[t.Optional[str], t.Optional[str]]:
"""Run the specified command and return stdout and stderr as a tuple."""
explain = args.explain and not always
@@ -449,7 +449,7 @@ def run_command(
output_stream=output_stream, cmd_verbosity=cmd_verbosity, str_errors=str_errors, error_callback=error_callback)
-def yamlcheck(python):
+def yamlcheck(python: PythonConfig) -> t.Optional[bool]:
"""Return True if PyYAML has libyaml support, False if it does not and None if it was not found."""
result = json.loads(raw_command([python.path, os.path.join(ANSIBLE_TEST_TARGET_TOOLS_ROOT, 'yamlcheck.py')], capture=True)[0])
diff --git a/test/lib/ansible_test/_internal/venv.py b/test/lib/ansible_test/_internal/venv.py
index 9e999c16..ec498ed9 100644
--- a/test/lib/ansible_test/_internal/venv.py
+++ b/test/lib/ansible_test/_internal/venv.py
@@ -41,8 +41,8 @@ from .python_requirements import (
def get_virtual_python(
- args: EnvironmentConfig,
- python: VirtualPythonConfig,
+ args: EnvironmentConfig,
+ python: VirtualPythonConfig,
) -> VirtualPythonConfig:
"""Create a virtual environment for the given Python and return the path to its root."""
if python.system_site_packages:
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 9513ed30..270c9f44 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
@@ -22,7 +22,6 @@ import argparse
import ast
import datetime
import json
-import errno
import os
import re
import subprocess
@@ -2467,12 +2466,9 @@ class GitCache:
self.head_tree = self._get_module_files()
else:
raise
- except OSError as ex:
- if ex.errno == errno.ENOENT:
- # fallback when git is not installed
- self.head_tree = self._get_module_files()
- else:
- raise
+ except FileNotFoundError:
+ # fallback when git is not installed
+ self.head_tree = self._get_module_files()
allowed_exts = ('.py', '.ps1')
if plugin_type != 'module':
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 3180530c..44a5ddc9 100644
--- a/test/lib/ansible_test/_util/target/sanity/import/importer.py
+++ b/test/lib/ansible_test/_util/target/sanity/import/importer.py
@@ -44,7 +44,8 @@ def main():
# noinspection PyCompatibility
from importlib import import_module
except ImportError:
- def import_module(name):
+ def import_module(name, package=None): # type: (str, str | None) -> types.ModuleType
+ assert package is None
__import__(name)
return sys.modules[name]
diff --git a/test/units/playbook/test_helpers.py b/test/units/playbook/test_helpers.py
index e784312f..a89730ca 100644
--- a/test/units/playbook/test_helpers.py
+++ b/test/units/playbook/test_helpers.py
@@ -160,20 +160,10 @@ class TestLoadListOfTasks(unittest.TestCase, MixinForMocks):
self.assertIsInstance(block.always, list)
self.assertEqual(len(block.always), 0)
- def test_block_unknown_action_use_handlers(self):
- ds = [{
- 'block': [{'action': 'foo_test_block_unknown_action'}]
- }]
- res = helpers.load_list_of_tasks(ds, play=self.mock_play, use_handlers=True,
- variable_manager=self.mock_variable_manager, loader=self.fake_loader)
- self._assert_is_task_list_or_blocks(res)
- self.assertIsInstance(res[0], Block)
- self._assert_default_block(res[0])
-
- def test_one_bogus_block_use_handlers(self):
+ def test_block_use_handlers(self):
ds = [{'block': True}]
self.assertRaisesRegex(errors.AnsibleParserError,
- "A malformed block was encountered",
+ "Using a block as a handler is not supported.",
helpers.load_list_of_tasks,
ds, play=self.mock_play, use_handlers=True,
variable_manager=self.mock_variable_manager, loader=self.fake_loader)