From 95f4f2a988eaa62601454fb25e4232632365e892 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Tue, 7 Jan 2025 15:34:10 +0000 Subject: [PATCH] Enable support for ansible 2.18, drop 2.14/2.15 and py39 Related: https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html --- .config/requirements.in | 3 +-- .github/workflows/tox.yml | 8 +++--- .readthedocs.yml | 46 ++++++++++----------------------- README.md | 2 +- pyproject.toml | 7 +++-- src/ansible_compat/constants.py | 2 +- src/ansible_compat/runtime.py | 34 ++++++++++-------------- src/ansible_compat/types.py | 21 ++++----------- test/conftest.py | 3 +-- test/test_config.py | 2 +- tox.ini | 20 +++++--------- 11 files changed, 49 insertions(+), 99 deletions(-) diff --git a/.config/requirements.in b/.config/requirements.in index 6a9241c9..a0b14838 100644 --- a/.config/requirements.in +++ b/.config/requirements.in @@ -1,7 +1,6 @@ # https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html -ansible-core>=2.14 +ansible-core>=2.16 packaging PyYAML subprocess-tee>=0.4.1 jsonschema>=4.6.0 -typing-extensions>=4.5.0;python_version<'3.10' diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 72e3dd8d..1d2a000a 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -20,19 +20,17 @@ jobs: tox: uses: ansible/team-devtools/.github/workflows/tox.yml@main with: - jobs_producing_coverage: 11 + jobs_producing_coverage: 9 other_names: | docs lint pkg - py39-ansible214 - py39-ansible215 - py310-ansible215 py310-ansible217 - py311-ansible215 py312-ansible216 py312-ansible217 + py312-ansible218 py312-devel + py313-ansible218 py313-devel py310-macos:tox -e py310 py313-macos:tox -e py313 diff --git a/.readthedocs.yml b/.readthedocs.yml index 66a051a5..4a8a4029 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,45 +1,25 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html -# for details - --- -# Required version: 2 -# Build documentation in the docs/ directory with Sphinx -sphinx: - # keep dirhtml for nice URLs without .html extension - builder: dirhtml - configuration: docs/conf.py +mkdocs: fail_on_warning: true - -# Build documentation with MkDocs -#mkdocs: -# configuration: mkdocs.yml -# fail_on_warning: true - -# Optionally build your docs in additional formats -# such as PDF and ePub -formats: [] - -submodules: - include: all # [] - exclude: [] - recursive: true + configuration: mkdocs.yml build: - image: latest - -# Optionally set the version of Python and requirements required -# to build docs + os: ubuntu-24.04 + tools: + python: "3.11" + commands: + - pip install --user tox + - python3 -m tox -e docs python: - version: "3.9" install: - # On https://readthedocs.org/dashboard/ansible-lint/environmentvariables/ we - # do have PIP_CONSTRAINTS=.config/constraints.txt which ensures we install only - # pinned requirements that that we know to be working. + - method: pip + path: tox - method: pip path: . extra_requirements: - docs - system_packages: false +submodules: + include: all + recursive: true diff --git a/README.md b/README.md index 6b8888ae..1cbf8b8a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![codecov.io](https://codecov.io/github/ansible/ansible-compat/coverage.svg?branch=main)](https://codecov.io/github/ansible/ansible-compat?branch=main) A python package contains functions that facilitate working with various -versions of Ansible 2.14 and newer. +versions of Ansible. ## Documentation diff --git a/pyproject.toml b/pyproject.toml index db35d865..1ad3076b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -36,7 +35,7 @@ maintainers = [{"email" = "ssbarnea@redhat.com", "name" = "Sorin Sbarnea"}] name = "ansible-compat" readme = "README.md" # https://peps.python.org/pep-0621/#readme -requires-python = ">=3.9" +requires-python = ">=3.10" [project.urls] changelog = "https://github.com/ansible/ansible-compat/releases" @@ -72,7 +71,7 @@ error_summary = true # warn_return_any = True # warn_unused_configs = True exclude = "test/local-content" -python_version = "3.9" +python_version = "3.10" [[tool.mypy.overrides]] ignore_missing_imports = true @@ -364,7 +363,7 @@ testpaths = ["test"] [tool.ruff] extend-include = ["src/ansible_compat/_version.py"] preview = true -target-version = "py39" +target-version = "py310" [tool.ruff.format] docstring-code-format = true diff --git a/src/ansible_compat/constants.py b/src/ansible_compat/constants.py index cb923170..e8876197 100644 --- a/src/ansible_compat/constants.py +++ b/src/ansible_compat/constants.py @@ -14,7 +14,7 @@ ] # Minimal version of Ansible we support for runtime -ANSIBLE_MIN_VERSION = "2.14" +ANSIBLE_MIN_VERSION = "2.16" # Based on https://docs.ansible.com/ansible/latest/reference_appendices/config.html ANSIBLE_DEFAULT_ROLES_PATH = ( diff --git a/src/ansible_compat/runtime.py b/src/ansible_compat/runtime.py index 717d4ae4..4f78f172 100644 --- a/src/ansible_compat/runtime.py +++ b/src/ansible_compat/runtime.py @@ -17,7 +17,7 @@ from collections import OrderedDict from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, no_type_check +from typing import TYPE_CHECKING, Any, no_type_check import subprocess_tee from packaging.version import Version @@ -46,6 +46,7 @@ # https://github.com/PyCQA/pylint/issues/3240 # pylint: disable=unsubscriptable-object CompletedProcess = subprocess.CompletedProcess[Any] + from collections.abc import Callable else: CompletedProcess = subprocess.CompletedProcess @@ -337,7 +338,7 @@ def _ensure_module_available(self) -> None: msg = f"Ansible CLI ({self.version}) and python module ({ansible_module_version}) versions do not match. This indicates a broken execution environment." raise RuntimeError(msg) - # For ansible 2.15+ we need to initialize the plugin loader + # We need to initialize the plugin loader # https://github.com/ansible/ansible-lint/issues/2945 if not Runtime.initialized: col_path = [f"{self.cache_dir}/collections"] @@ -346,24 +347,15 @@ def _ensure_module_available(self) -> None: _AnsibleCollectionFinder, # noqa: PLC2701 ) - if self.version >= Version("2.15.0.dev0"): - # pylint: disable=import-outside-toplevel,no-name-in-module - from ansible.plugins.loader import init_plugin_loader - - _AnsibleCollectionFinder( # noqa: SLF001 - paths=col_path, - )._remove() # pylint: disable=protected-access - init_plugin_loader(col_path) - else: - # noinspection PyProtectedMember - # pylint: disable=protected-access - col_path += self.config.collections_paths - col_path += os.path.dirname( # noqa: PTH120 - os.environ.get(ansible_collections_path(), "."), - ).split(":") - _AnsibleCollectionFinder( # noqa: SLF001 - paths=col_path, - )._install() # pylint: disable=protected-access + # noinspection PyProtectedMember + # pylint: disable=protected-access + col_path += self.config.collections_paths + col_path += os.path.dirname( # noqa: PTH120 + os.environ.get(ansible_collections_path(), "."), + ).split(":") + _AnsibleCollectionFinder( # noqa: SLF001 + paths=col_path, + )._install() # pylint: disable=protected-access Runtime.initialized = True def clean(self) -> None: @@ -559,7 +551,7 @@ def install_requirements( # noqa: C901 if not Path(requirement).exists(): return reqs_yaml = yaml_from_file(Path(requirement)) - if not isinstance(reqs_yaml, (dict, list)): + if not isinstance(reqs_yaml, dict | list): msg = f"{requirement} file is not a valid Ansible requirements file." raise InvalidPrerequisiteError(msg) diff --git a/src/ansible_compat/types.py b/src/ansible_compat/types.py index 752ed4be..a8255938 100644 --- a/src/ansible_compat/types.py +++ b/src/ansible_compat/types.py @@ -3,22 +3,11 @@ from __future__ import annotations from collections.abc import Mapping, Sequence -from typing import Union +from typing import TypeAlias -try: # py39 does not have TypeAlias - from typing_extensions import TypeAlias -except ImportError: - from typing import TypeAlias # type: ignore[no-redef,attr-defined] - -JSON: TypeAlias = Union[dict[str, "JSON"], list["JSON"], str, int, float, bool, None] -JSON_ro: TypeAlias = Union[ - Mapping[str, "JSON_ro"], - Sequence["JSON_ro"], - str, - int, - float, - bool, - None, -] +JSON: TypeAlias = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None +JSON_ro: TypeAlias = ( + Mapping[str, "JSON_ro"] | Sequence["JSON_ro"] | str | int | float | bool | None +) __all__ = ["JSON", "JSON_ro"] diff --git a/test/conftest.py b/test/conftest.py index 41761119..72e6bb07 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -5,9 +5,8 @@ import pathlib import subprocess import sys -from collections.abc import Generator +from collections.abc import Callable, Generator from pathlib import Path -from typing import Callable import pytest diff --git a/test/test_config.py b/test/test_config.py index 99c900dc..58d9e206 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -24,7 +24,7 @@ def test_config() -> None: assert isinstance(config.CONNECTION_FACTS_MODULES, dict) assert config.ANSIBLE_COW_PATH is None assert isinstance(config.NETWORK_GROUP_MODULES, list) - assert isinstance(config.DEFAULT_FORKS, (int, type(None))) + assert isinstance(config.DEFAULT_FORKS, int | type(None)) # check lowercase and older name aliasing assert isinstance(config.collections_paths, list) diff --git a/tox.ini b/tox.ini index 9dd36871..93cd172c 100644 --- a/tox.ini +++ b/tox.ini @@ -6,18 +6,14 @@ envlist = docs py py-devel - py39-ansible214 - py39-ansible215 - py310-ansible214 - py310-ansible215 py310-ansible216 py310-ansible217 - py311-ansible214 - py311-ansible215 py311-ansible216 py311-ansible217 py312-ansible216 py312-ansible217 + py312-ansible218 + py313-ansible218 isolated_build = true skip_missing_interpreters = True @@ -31,16 +27,14 @@ requires = description = Run the tests devel: ansible devel branch - ansible214: ansible-core 2.14 - ansible215: ansible-core 2.15 ansible216: ansible-core 2.16 ansible217: ansible-core 2.17 + ansible218: ansible-core 2.18 deps = - ansible214: ansible-core>=2.14,<2.15 - ansible215: ansible-core>=2.15,<2.16 ansible216: ansible-core>=2.16,<2.17 ansible217: ansible-core>=2.17,<2.18 + ansible218: ansible-core>=2.18,<2.19 devel: ansible-core @ git+https://github.com/ansible/ansible.git@c5d18c39d81e2b3b10856b2fb76747230e4fac4a # GPLv3+ # avoid installing ansible-core on -devel envs: @@ -60,7 +54,7 @@ commands = # pytest users to run coverage when they just want to run a single test with `pytest -k test` coverage run -m pytest {posargs:} # needed for upload to codecov.io - {py,py39,py310,py311,py312,py313}: sh -c "coverage combine -q --data-file={envdir}/.coverage {envdir}/.coverage.* && coverage xml --data-file={envdir}/.coverage -o {envdir}/coverage.xml --ignore-errors --fail-under=0 && COVERAGE_FILE={envdir}/.coverage coverage lcov --fail-under=0 --ignore-errors -q && COVERAGE_FILE={envdir}/.coverage coverage report --fail-under=0 --ignore-errors" + {py,py310,py311,py312,py313}: sh -c "coverage combine -q --data-file={envdir}/.coverage {envdir}/.coverage.* && coverage xml --data-file={envdir}/.coverage -o {envdir}/coverage.xml --ignore-errors --fail-under=0 && COVERAGE_FILE={envdir}/.coverage coverage lcov --fail-under=0 --ignore-errors -q && COVERAGE_FILE={envdir}/.coverage coverage report --fail-under=0 --ignore-errors" # lcov needed for vscode integration due to https://github.com/ryanluker/vscode-coverage-gutters/issues/403 passenv = @@ -153,10 +147,10 @@ commands = pip uninstall -y ansible-compat [testenv:py] -description = Run the tests with {basepython} ansible-core 2.14+ +description = Run the tests with {basepython} ansible-core 2.16+ deps = {[testenv]deps} - ansible-core>=2.14 + ansible-core>=2.16 [testenv:rpm] description = Use packit to build RPM (requires RPM based Linux distro)