ansible-test - Improve requirements handling. (#77825)

The requirements for virtualenv and coverage are now kept in a requirements file for easier container builds.

The test-constraints sanity test has been updated to make sure the requirements file is kept up-to-date.
pull/77832/head
Matt Clay 2 years ago committed by GitHub
parent fecc78b5fb
commit 9600e3064a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,4 @@
# The test-constraints sanity test verifies this file, but changes must be made manually to keep it in up-to-date.
virtualenv == 16.7.12 ; python_version < '3'
coverage == 6.3.3 ; python_version >= '3.7' and python_version <= '3.11'
coverage == 4.5.4 ; python_version >= '2.6' and python_version <= '3.6'

@ -63,12 +63,12 @@ class CoverageVersion:
COVERAGE_VERSIONS = ( COVERAGE_VERSIONS = (
# IMPORTANT: Keep this in sync with the ansible-test.txt requirements file.
CoverageVersion('6.3.3', 7, (3, 7), (3, 11)), CoverageVersion('6.3.3', 7, (3, 7), (3, 11)),
CoverageVersion('4.5.4', 0, (2, 6), (3, 7)), CoverageVersion('4.5.4', 0, (2, 6), (3, 6)),
) )
""" """
This tuple specifies the coverage version to use for Python version ranges. This tuple specifies the coverage version to use for Python version ranges.
When versions overlap, the latest version of coverage (listed first) will be used.
""" """
CONTROLLER_COVERAGE_VERSION = COVERAGE_VERSIONS[0] CONTROLLER_COVERAGE_VERSION = COVERAGE_VERSIONS[0]
@ -92,6 +92,9 @@ def get_coverage_version(version: str) -> CoverageVersion:
if not supported_versions: if not supported_versions:
raise InternalError(f'Python {version} has no matching entry in COVERAGE_VERSIONS.') raise InternalError(f'Python {version} has no matching entry in COVERAGE_VERSIONS.')
if len(supported_versions) > 1:
raise InternalError(f'Python {version} has multiple matching entries in COVERAGE_VERSIONS.')
coverage_version = supported_versions[0] coverage_version = supported_versions[0]
return coverage_version return coverage_version

@ -62,6 +62,8 @@ from .coverage_util import (
QUIET_PIP_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'quiet_pip.py') QUIET_PIP_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'quiet_pip.py')
REQUIREMENTS_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'requirements.py') REQUIREMENTS_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'requirements.py')
# IMPORTANT: Keep this in sync with the ansible-test.txt requirements file.
VIRTUALENV_VERSION = '16.7.12'
# Pip Abstraction # Pip Abstraction
@ -211,7 +213,7 @@ def collect_requirements(
if virtualenv: if virtualenv:
# sanity tests on Python 2.x install virtualenv when it is too old or is not already installed and the `--requirements` option is given # sanity tests on Python 2.x install virtualenv when it is too old or is not already installed and the `--requirements` option is given
# the last version of virtualenv with no dependencies is used to minimize the changes made outside a virtual environment # the last version of virtualenv with no dependencies is used to minimize the changes made outside a virtual environment
commands.extend(collect_package_install(packages=['virtualenv==16.7.12'], constraints=False)) commands.extend(collect_package_install(packages=[f'virtualenv=={VIRTUALENV_VERSION}'], constraints=False))
if coverage: if coverage:
commands.extend(collect_package_install(packages=[f'coverage=={get_coverage_version(python.version).coverage_version}'], constraints=False)) commands.extend(collect_package_install(packages=[f'coverage=={get_coverage_version(python.version).coverage_version}'], constraints=False))

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os import os
import pathlib
import re import re
import sys import sys
@ -14,9 +15,15 @@ def main():
if path == 'test/lib/ansible_test/_data/requirements/ansible.txt': if path == 'test/lib/ansible_test/_data/requirements/ansible.txt':
# This file is an exact copy of the ansible requirements.txt and should not conflict with other constraints. # This file is an exact copy of the ansible requirements.txt and should not conflict with other constraints.
continue continue
with open(path, 'r') as path_fd: with open(path, 'r') as path_fd:
requirements[path] = parse_requirements(path_fd.read().splitlines()) requirements[path] = parse_requirements(path_fd.read().splitlines())
if path == 'test/lib/ansible_test/_data/requirements/ansible-test.txt':
# Special handling is required for ansible-test's requirements file.
check_ansible_test(path, requirements.pop(path))
continue
frozen_sanity = {} frozen_sanity = {}
non_sanity_requirements = set() non_sanity_requirements = set()
@ -65,6 +72,33 @@ def main():
req[0], req[1], req[3].start('constraints') + 1, req[3].group('constraints'), name)) req[0], req[1], req[3].start('constraints') + 1, req[3].group('constraints'), name))
def check_ansible_test(path: str, requirements: list[tuple[int, str, re.Match]]) -> None:
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent.parent.joinpath('lib')))
from ansible_test._internal.python_requirements import VIRTUALENV_VERSION
from ansible_test._internal.coverage_util import COVERAGE_VERSIONS
from ansible_test._internal.util import version_to_str
expected_lines = set([
f"virtualenv == {VIRTUALENV_VERSION} ; python_version < '3'",
] + [
f"coverage == {item.coverage_version} ; python_version >= '{version_to_str(item.min_python)}' and python_version <= '{version_to_str(item.max_python)}'"
for item in COVERAGE_VERSIONS
])
for idx, requirement in enumerate(requirements):
lineno, line, match = requirement
if line in expected_lines:
expected_lines.remove(line)
continue
print('%s:%d:%d: unexpected line: %s' % (path, lineno, 1, line))
for expected_line in sorted(expected_lines):
print('%s:%d:%d: missing line: %s' % (path, requirements[-1][0] + 1, 1, expected_line))
def parse_requirements(lines): def parse_requirements(lines):
# see https://www.python.org/dev/peps/pep-0508/#names # see https://www.python.org/dev/peps/pep-0508/#names
pattern = re.compile(r'^(?P<name>[A-Z0-9][A-Z0-9._-]*[A-Z0-9]|[A-Z0-9])(?P<extras> *\[[^]]*])?(?P<constraints>[^;#]*)(?P<markers>[^#]*)(?P<comment>.*)$', pattern = re.compile(r'^(?P<name>[A-Z0-9][A-Z0-9._-]*[A-Z0-9]|[A-Z0-9])(?P<extras> *\[[^]]*])?(?P<constraints>[^;#]*)(?P<markers>[^#]*)(?P<comment>.*)$',

Loading…
Cancel
Save