mirror of
https://github.com/ansible/ansible.git
synced 2025-11-30 23:16:08 +07:00
Drop Python 3.11 controller support (#85590)
This commit is contained in:
@@ -112,10 +112,6 @@ stages:
|
||||
test: rhel/9.6
|
||||
- name: RHEL 10.0
|
||||
test: rhel/10.0
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- name: FreeBSD 14.3
|
||||
test: freebsd/14.3
|
||||
groups:
|
||||
- 3
|
||||
- 4
|
||||
@@ -183,9 +179,9 @@ stages:
|
||||
nameFormat: Python {0}
|
||||
testFormat: galaxy/{0}/1
|
||||
targets:
|
||||
- test: 3.11
|
||||
- test: 3.12
|
||||
- test: 3.13
|
||||
- test: 3.14
|
||||
- stage: Generic
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -194,9 +190,9 @@ stages:
|
||||
nameFormat: Python {0}
|
||||
testFormat: generic/{0}/1
|
||||
targets:
|
||||
- test: 3.11
|
||||
- test: 3.12
|
||||
- test: 3.13
|
||||
- test: 3.14
|
||||
- stage: Incidental_Windows
|
||||
displayName: Incidental Windows
|
||||
dependsOn: []
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
major_changes:
|
||||
- ansible - Add support for Python 3.14.
|
||||
- ansible - Drop support for Python 3.8 on targets.
|
||||
- ansible - Drop support for Python 3.11 on the controller.
|
||||
|
||||
@@ -5,7 +5,7 @@ env-setup
|
||||
---------
|
||||
|
||||
The 'env-setup' script modifies your environment to allow you to run
|
||||
ansible from a git checkout using python >= 3.11.
|
||||
ansible from a git checkout using a supported Python version.
|
||||
|
||||
First, set up your environment to run from the checkout:
|
||||
|
||||
|
||||
@@ -30,10 +30,7 @@ def import_controller_module(module_name: str, /) -> t.Any:
|
||||
return importlib.import_module(module_name)
|
||||
|
||||
|
||||
_T = t.TypeVar('_T')
|
||||
|
||||
|
||||
def experimental(obj: _T) -> _T:
|
||||
def experimental[T](obj: T) -> T:
|
||||
"""
|
||||
Decorator for experimental types and methods outside the `_internal` package which accept or expose internal types.
|
||||
As with internal APIs, these are subject to change at any time without notice.
|
||||
|
||||
@@ -9,8 +9,6 @@ from ansible.module_utils._internal._ansiballz import _extensions
|
||||
from ansible.module_utils._internal._ansiballz._extensions import _debugpy, _pydevd, _coverage
|
||||
from ansible.constants import config
|
||||
|
||||
_T = t.TypeVar('_T')
|
||||
|
||||
|
||||
class ExtensionManager:
|
||||
"""AnsiballZ extension manager."""
|
||||
@@ -101,7 +99,7 @@ class ExtensionManager:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_options(cls, name: str, config_type: type[_T], task_vars: dict[str, object]) -> _T | None:
|
||||
def _get_options[T](cls, name: str, config_type: type[T], task_vars: dict[str, object]) -> T | None:
|
||||
"""Parse configuration from the named environment variable as the specified type, or None if not configured."""
|
||||
if (value := config.get_config_value(name, variables=task_vars)) is None:
|
||||
return None
|
||||
|
||||
@@ -3,26 +3,24 @@ from __future__ import annotations as _annotations
|
||||
import collections.abc as _c
|
||||
import typing as _t
|
||||
|
||||
_T_co = _t.TypeVar('_T_co', covariant=True)
|
||||
|
||||
|
||||
class SequenceProxy(_c.Sequence[_T_co]):
|
||||
class SequenceProxy[T](_c.Sequence[T]):
|
||||
"""A read-only sequence proxy."""
|
||||
|
||||
# DTFIX5: needs unit test coverage
|
||||
|
||||
__slots__ = ('__value',)
|
||||
|
||||
def __init__(self, value: _c.Sequence[_T_co]) -> None:
|
||||
def __init__(self, value: _c.Sequence[T]) -> None:
|
||||
self.__value = value
|
||||
|
||||
@_t.overload
|
||||
def __getitem__(self, index: int) -> _T_co: ...
|
||||
def __getitem__(self, index: int) -> T: ...
|
||||
|
||||
@_t.overload
|
||||
def __getitem__(self, index: slice) -> _c.Sequence[_T_co]: ...
|
||||
def __getitem__(self, index: slice) -> _c.Sequence[T]: ...
|
||||
|
||||
def __getitem__(self, index: int | slice) -> _T_co | _c.Sequence[_T_co]:
|
||||
def __getitem__(self, index: int | slice) -> T | _c.Sequence[T]:
|
||||
if isinstance(index, slice):
|
||||
return self.__class__(self.__value[index])
|
||||
|
||||
@@ -34,10 +32,10 @@ class SequenceProxy(_c.Sequence[_T_co]):
|
||||
def __contains__(self, item: object) -> bool:
|
||||
return item in self.__value
|
||||
|
||||
def __iter__(self) -> _t.Iterator[_T_co]:
|
||||
def __iter__(self) -> _t.Iterator[T]:
|
||||
yield from self.__value
|
||||
|
||||
def __reversed__(self) -> _c.Iterator[_T_co]:
|
||||
def __reversed__(self) -> _c.Iterator[T]:
|
||||
return reversed(self.__value)
|
||||
|
||||
def index(self, *args) -> int:
|
||||
|
||||
@@ -24,7 +24,6 @@ from ansible._internal._templating import _transform
|
||||
from ansible.module_utils import _internal
|
||||
from ansible.module_utils._internal import _datatag
|
||||
|
||||
_T = t.TypeVar('_T')
|
||||
_sentinel = object()
|
||||
|
||||
|
||||
@@ -115,7 +114,7 @@ class AnsibleVariableVisitor:
|
||||
if func := getattr(super(), '__exit__', None):
|
||||
func(*args, **kwargs)
|
||||
|
||||
def visit(self, value: _T) -> _T:
|
||||
def visit[T](self, value: T) -> T:
|
||||
"""
|
||||
Enforces Ansible's variable type system restrictions before a var is accepted in inventory. Also, conditionally implements template trust
|
||||
compatibility, depending on the plugin's declared understanding (or lack thereof). This always recursively copies inputs to fully isolate
|
||||
@@ -143,7 +142,7 @@ class AnsibleVariableVisitor:
|
||||
|
||||
return self._visit(None, key) # key=None prevents state tracking from seeing the key as value
|
||||
|
||||
def _visit(self, key: t.Any, value: _T) -> _T:
|
||||
def _visit[T](self, key: t.Any, value: T) -> T:
|
||||
"""Internal implementation to recursively visit a data structure's contents."""
|
||||
self._current = key # supports StateTrackingMixIn
|
||||
|
||||
@@ -168,7 +167,7 @@ class AnsibleVariableVisitor:
|
||||
value = value._native_copy()
|
||||
value_type = type(value)
|
||||
|
||||
result: _T
|
||||
result: T
|
||||
|
||||
# DTFIX-FUTURE: Visitor generally ignores dict/mapping keys by default except for debugging and schema-aware checking.
|
||||
# It could be checking keys destined for variable storage to apply more strict rules about key shape and type.
|
||||
|
||||
@@ -29,7 +29,6 @@ from ._utils import LazyOptions, TemplateContext
|
||||
|
||||
_display = Display()
|
||||
|
||||
_TCallable = t.TypeVar("_TCallable", bound=t.Callable)
|
||||
_ITERATOR_TYPES: t.Final = (c.Iterator, c.ItemsView, c.KeysView, c.ValuesView, range)
|
||||
|
||||
|
||||
@@ -169,7 +168,7 @@ class _DirectCall:
|
||||
_marker_attr: t.Final[str] = "_directcall"
|
||||
|
||||
@classmethod
|
||||
def mark(cls, src: _TCallable) -> _TCallable:
|
||||
def mark[T: t.Callable](cls, src: T) -> T:
|
||||
setattr(src, cls._marker_attr, True)
|
||||
return src
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ if 1 <= len(sys.argv) <= 2 and os.path.basename(sys.argv[0]) == "ansible" and os
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY_MIN = (3, 11)
|
||||
_PY_MIN = (3, 12)
|
||||
|
||||
if sys.version_info < _PY_MIN:
|
||||
raise SystemExit(
|
||||
|
||||
@@ -1689,12 +1689,12 @@ INTERPRETER_PYTHON:
|
||||
INTERPRETER_PYTHON_FALLBACK:
|
||||
name: Ordered list of Python interpreters to check for in discovery
|
||||
default:
|
||||
- python3.14
|
||||
- python3.13
|
||||
- python3.12
|
||||
- python3.11
|
||||
- python3.10
|
||||
- python3.9
|
||||
- python3.8
|
||||
- /usr/bin/python3
|
||||
- python3
|
||||
vars:
|
||||
|
||||
@@ -1271,11 +1271,7 @@ def test_sdist() -> None:
|
||||
except FileNotFoundError:
|
||||
raise ApplicationError(f"Missing sdist: {sdist_file.relative_to(CHECKOUT_DIR)}") from None
|
||||
|
||||
# deprecated: description='extractall fallback without filter' python_version='3.11'
|
||||
if hasattr(tarfile, 'data_filter'):
|
||||
sdist.extractall(temp_dir, filter='data') # type: ignore[call-arg]
|
||||
else:
|
||||
sdist.extractall(temp_dir)
|
||||
sdist.extractall(temp_dir, filter='data')
|
||||
|
||||
pyc_glob = "*.pyc*"
|
||||
pyc_files = sorted(path.relative_to(temp_dir) for path in temp_dir.rglob(pyc_glob))
|
||||
|
||||
@@ -3,7 +3,7 @@ requires = ["setuptools >= 66.1.0, <= 80.3.1", "wheel == 0.45.1"] # lower bound
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.12"
|
||||
name = "ansible-core"
|
||||
authors = [
|
||||
{name = "Ansible Project"},
|
||||
@@ -20,9 +20,9 @@ classifiers = [
|
||||
"Natural Language :: English",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: System :: Installation/Setup",
|
||||
"Topic :: System :: Systems Administration",
|
||||
|
||||
@@ -160,11 +160,7 @@ class ValidateModulesTest(SanitySingleVersion):
|
||||
temp_dir = process_scoped_temporary_directory(args)
|
||||
|
||||
with tarfile.open(path) as file:
|
||||
# deprecated: description='extractall fallback without filter' python_version='3.11'
|
||||
if hasattr(tarfile, 'data_filter'):
|
||||
file.extractall(temp_dir, filter='data') # type: ignore[call-arg]
|
||||
else:
|
||||
file.extractall(temp_dir)
|
||||
file.extractall(temp_dir, filter='data')
|
||||
|
||||
cmd.extend([
|
||||
'--original-plugins', temp_dir,
|
||||
|
||||
@@ -7,10 +7,10 @@ from __future__ import annotations
|
||||
REMOTE_ONLY_PYTHON_VERSIONS = (
|
||||
'3.9',
|
||||
'3.10',
|
||||
'3.11',
|
||||
)
|
||||
|
||||
CONTROLLER_PYTHON_VERSIONS = (
|
||||
'3.11',
|
||||
'3.12',
|
||||
'3.13',
|
||||
'3.14',
|
||||
|
||||
@@ -187,12 +187,6 @@ bootstrap_remote_freebsd()
|
||||
# Declare platform/python version combinations which do not have supporting OS packages available.
|
||||
# For these combinations ansible-test will use pip to install the requirements instead.
|
||||
case "${platform_version}/${python_version}" in
|
||||
13.5/3.11)
|
||||
# defaults available
|
||||
;;
|
||||
14.3/3.11)
|
||||
# defaults available
|
||||
;;
|
||||
*)
|
||||
# just assume nothing is available
|
||||
jinja2_pkg="" # not available
|
||||
|
||||
@@ -54,7 +54,6 @@ lib/ansible/plugins/cache/base.py ansible-doc!skip # not a plugin, but a stub f
|
||||
lib/ansible/plugins/callback/__init__.py pylint:arguments-renamed
|
||||
lib/ansible/plugins/inventory/advanced_host_list.py pylint:arguments-renamed
|
||||
lib/ansible/plugins/inventory/host_list.py pylint:arguments-renamed
|
||||
lib/ansible/_internal/_wrapt.py mypy-3.11!skip # vendored code
|
||||
lib/ansible/_internal/_wrapt.py mypy-3.12!skip # vendored code
|
||||
lib/ansible/_internal/_wrapt.py mypy-3.13!skip # vendored code
|
||||
lib/ansible/_internal/_wrapt.py mypy-3.14!skip # vendored code
|
||||
@@ -237,3 +236,4 @@ lib/ansible/utils/encrypt.py pylint:ansible-deprecated-version # TODO: 2.20
|
||||
lib/ansible/utils/ssh_functions.py pylint:ansible-deprecated-version # TODO: 2.20
|
||||
lib/ansible/vars/manager.py pylint:ansible-deprecated-version-comment # TODO: 2.20
|
||||
lib/ansible/vars/plugins.py pylint:ansible-deprecated-version # TODO: 2.20
|
||||
lib/ansible/galaxy/role.py pylint:ansible-deprecated-python-version-comment # TODO: 2.20
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
bcrypt ; python_version >= '3.11' # controller only
|
||||
passlib ; python_version >= '3.11' # controller only
|
||||
pexpect ; python_version >= '3.11' # controller only
|
||||
pywinrm ; python_version >= '3.11' # controller only
|
||||
bcrypt ; python_version >= '3.12' # controller only
|
||||
passlib ; python_version >= '3.12' # controller only
|
||||
pexpect ; python_version >= '3.12' # controller only
|
||||
pywinrm ; python_version >= '3.12' # controller only
|
||||
typing_extensions; python_version < '3.11' # some unit tests need Annotated and get_type_hints(include_extras=True)
|
||||
|
||||
Reference in New Issue
Block a user