Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ansible/ansible.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Martz <matt@sivel.net>2022-07-20 18:52:32 +0300
committerGitHub <noreply@github.com>2022-07-20 18:52:32 +0300
commit884244f1b2da3c3f367e064ef4ac0123fcb12675 (patch)
tree9eaa218f4e9fee80263d8f0948e03ec1aa1cb33c
parent90ef914f77da5cc0b45e54b23ca0cc9dbc58e642 (diff)
Python 3.9 min for controller (#77566)
-rw-r--r--changelogs/fragments/python39-min-controller.yml4
-rw-r--r--lib/ansible/__main__.py2
-rw-r--r--lib/ansible/cli/__init__.py4
-rw-r--r--lib/ansible/executor/task_queue_manager.py2
-rw-r--r--lib/ansible/playbook/block.py4
-rw-r--r--lib/ansible/playbook/included_file.py2
-rw-r--r--lib/ansible/playbook/playbook_include.py5
-rw-r--r--lib/ansible/playbook/role/definition.py2
-rw-r--r--lib/ansible/playbook/role_include.py6
-rw-r--r--lib/ansible/playbook/task.py14
-rw-r--r--lib/ansible/playbook/task_include.py6
-rw-r--r--lib/ansible/plugins/action/gather_facts.py2
-rw-r--r--lib/ansible/plugins/action/template.py3
-rw-r--r--lib/ansible/plugins/action/uri.py3
-rw-r--r--lib/ansible/plugins/loader.py2
-rw-r--r--lib/ansible/plugins/strategy/__init__.py3
-rw-r--r--lib/ansible/template/vars.py3
-rw-r--r--lib/ansible/utils/collection_loader/_collection_finder.py2
-rw-r--r--lib/ansible/utils/vars.py3
-rw-r--r--lib/ansible/vars/manager.py10
-rw-r--r--lib/ansible/vars/plugins.py4
-rw-r--r--setup.cfg3
22 files changed, 43 insertions, 46 deletions
diff --git a/changelogs/fragments/python39-min-controller.yml b/changelogs/fragments/python39-min-controller.yml
new file mode 100644
index 00000000000..caaa1c7ac8f
--- /dev/null
+++ b/changelogs/fragments/python39-min-controller.yml
@@ -0,0 +1,4 @@
+major_changes:
+ - ansible - Increase minimum Python requirement to Python 3.9 for CLI utilities and controller code
+breaking_changes:
+ - ansible - Increase minimum Python requirement to Python 3.9 for CLI utilities and controller code
diff --git a/lib/ansible/__main__.py b/lib/ansible/__main__.py
index f670d9adfde..5a753ec05c7 100644
--- a/lib/ansible/__main__.py
+++ b/lib/ansible/__main__.py
@@ -10,7 +10,7 @@ from importlib.metadata import distribution
def _short_name(name):
- return name.replace('ansible-', '').replace('ansible', 'adhoc')
+ return name.removeprefix('ansible-').replace('ansible', 'adhoc')
def main():
diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py
index 7e39627f5e8..da62cfe2837 100644
--- a/lib/ansible/cli/__init__.py
+++ b/lib/ansible/cli/__init__.py
@@ -13,9 +13,9 @@ import sys
# Used for determining if the system is running a new enough python version
# and should only restrict on our documented minimum versions
-if sys.version_info < (3, 8):
+if sys.version_info < (3, 9):
raise SystemExit(
- 'ERROR: Ansible requires Python 3.8 or newer on the controller. '
+ 'ERROR: Ansible requires Python 3.9 or newer on the controller. '
'Current version: %s' % ''.join(sys.version.splitlines())
)
diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py
index e37d0f7c149..dcfc38a7eb4 100644
--- a/lib/ansible/executor/task_queue_manager.py
+++ b/lib/ansible/executor/task_queue_manager.py
@@ -418,7 +418,7 @@ class TaskQueueManager:
for possible in [method_name, 'v2_on_any']:
gotit = getattr(callback_plugin, possible, None)
if gotit is None:
- gotit = getattr(callback_plugin, possible.replace('v2_', ''), None)
+ gotit = getattr(callback_plugin, possible.removeprefix('v2_'), None)
if gotit is not None:
methods.append(gotit)
diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py
index a77aea4106c..45f7a38a77c 100644
--- a/lib/ansible/playbook/block.py
+++ b/lib/ansible/playbook/block.py
@@ -82,9 +82,9 @@ class Block(Base, Conditional, CollectionSearch, Taggable):
all_vars = {}
if self._parent:
- all_vars.update(self._parent.get_vars())
+ all_vars |= self._parent.get_vars()
- all_vars.update(self.vars.copy())
+ all_vars |= self.vars.copy()
return all_vars
diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py
index c256d70b740..409eaec795a 100644
--- a/lib/ansible/playbook/included_file.py
+++ b/lib/ansible/playbook/included_file.py
@@ -190,7 +190,7 @@ class IncludedFile:
new_task._role_name = role_name
for from_arg in new_task.FROM_ARGS:
if from_arg in include_args:
- from_key = from_arg.replace('_from', '')
+ from_key = from_arg.removesuffix('_from')
new_task._from_files[from_key] = templar.template(include_args.pop(from_arg))
omit_token = task_vars.get('omit')
diff --git a/lib/ansible/playbook/playbook_include.py b/lib/ansible/playbook/playbook_include.py
index 5cc6c395010..03210ea37dc 100644
--- a/lib/ansible/playbook/playbook_include.py
+++ b/lib/ansible/playbook/playbook_include.py
@@ -65,7 +65,7 @@ class PlaybookInclude(Base, Conditional, Taggable):
all_vars = self.vars.copy()
if variable_manager:
- all_vars.update(variable_manager.get_vars())
+ all_vars |= variable_manager.get_vars()
templar = Templar(loader=loader, variables=all_vars)
@@ -105,8 +105,7 @@ class PlaybookInclude(Base, Conditional, Taggable):
if new_obj.when and isinstance(entry, Play):
entry._included_conditional = new_obj.when[:]
- temp_vars = entry.vars.copy()
- temp_vars.update(new_obj.vars)
+ temp_vars = entry.vars | new_obj.vars
param_tags = temp_vars.pop('tags', None)
if param_tags is not None:
entry.tags.extend(param_tags.split(','))
diff --git a/lib/ansible/playbook/role/definition.py b/lib/ansible/playbook/role/definition.py
index b44ef762bf7..b27a23174ba 100644
--- a/lib/ansible/playbook/role/definition.py
+++ b/lib/ansible/playbook/role/definition.py
@@ -99,7 +99,7 @@ class RoleDefinition(Base, Conditional, Taggable, CollectionSearch):
# result and the role name
if isinstance(ds, dict):
(new_role_def, role_params) = self._split_role_params(ds)
- new_ds.update(new_role_def)
+ new_ds |= new_role_def
self._role_params = role_params
# set the role name in the new ds
diff --git a/lib/ansible/playbook/role_include.py b/lib/ansible/playbook/role_include.py
index 2eae68c3fcf..394603729d0 100644
--- a/lib/ansible/playbook/role_include.py
+++ b/lib/ansible/playbook/role_include.py
@@ -78,7 +78,7 @@ class IncludeRole(TaskInclude):
myplay = play
ri = RoleInclude.load(self._role_name, play=myplay, variable_manager=variable_manager, loader=loader, collection_list=self.collections)
- ri.vars.update(self.vars)
+ ri.vars |= self.vars
if variable_manager is not None:
available_variables = variable_manager.get_vars(play=myplay, task=self)
@@ -147,7 +147,7 @@ class IncludeRole(TaskInclude):
# build options for role includes
for key in my_arg_names.intersection(IncludeRole.FROM_ARGS):
- from_key = key.replace('_from', '')
+ from_key = key.removesuffix('_from')
args_value = ir.args.get(key)
if not isinstance(args_value, string_types):
raise AnsibleParserError('Expected a string for %s but got %s instead' % (key, type(args_value)))
@@ -179,7 +179,7 @@ class IncludeRole(TaskInclude):
def get_include_params(self):
v = super(IncludeRole, self).get_include_params()
if self._parent_role:
- v.update(self._parent_role.get_role_params())
+ v |= self._parent_role.get_role_params()
v.setdefault('ansible_parent_role_names', []).insert(0, self._parent_role.get_name())
v.setdefault('ansible_parent_role_paths', []).insert(0, self._parent_role._role_path)
return v
diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py
index bf81fbf6012..bb8b651d695 100644
--- a/lib/ansible/playbook/task.py
+++ b/lib/ansible/playbook/task.py
@@ -146,7 +146,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
def _preprocess_with_loop(self, ds, new_ds, k, v):
''' take a lookup plugin name and store it correctly '''
- loop_name = k.replace("with_", "")
+ loop_name = k.removeprefix("with_")
if new_ds.get('loop') is not None or new_ds.get('loop_with') is not None:
raise AnsibleError("duplicate loop in task: %s" % loop_name, obj=ds)
if v is None:
@@ -241,7 +241,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
if k in ('action', 'local_action', 'args', 'delegate_to') or k == action or k == 'shell':
# we don't want to re-assign these values, which were determined by the ModuleArgsParser() above
continue
- elif k.startswith('with_') and k.replace("with_", "") in lookup_loader:
+ elif k.startswith('with_') and k.removeprefix("with_") in lookup_loader:
# transform into loop property
self._preprocess_with_loop(ds, new_ds, k, v)
elif C.INVALID_TASK_ATTRIBUTE_FAILED or k in self._valid_attrs:
@@ -323,7 +323,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
else:
isdict = templar.template(env_item, convert_bare=False)
if isinstance(isdict, dict):
- env.update(isdict)
+ env |= isdict
else:
display.warning("could not parse environment value, skipping: %s" % value)
@@ -362,9 +362,9 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
def get_vars(self):
all_vars = dict()
if self._parent:
- all_vars.update(self._parent.get_vars())
+ all_vars |= self._parent.get_vars()
- all_vars.update(self.vars)
+ all_vars |= self.vars
if 'tags' in all_vars:
del all_vars['tags']
@@ -376,9 +376,9 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
def get_include_params(self):
all_vars = dict()
if self._parent:
- all_vars.update(self._parent.get_include_params())
+ all_vars |= self._parent.get_include_params()
if self.action in C._ACTION_ALL_INCLUDES:
- all_vars.update(self.vars)
+ all_vars |= self.vars
return all_vars
def copy(self, exclude_parent=False, exclude_tasks=False):
diff --git a/lib/ansible/playbook/task_include.py b/lib/ansible/playbook/task_include.py
index c1e149f2b53..9c335c6e07e 100644
--- a/lib/ansible/playbook/task_include.py
+++ b/lib/ansible/playbook/task_include.py
@@ -116,10 +116,10 @@ class TaskInclude(Task):
else:
all_vars = dict()
if self._parent:
- all_vars.update(self._parent.get_vars())
+ all_vars |= self._parent.get_vars()
- all_vars.update(self.vars)
- all_vars.update(self.args)
+ all_vars |= self.vars
+ all_vars |= self.args
if 'tags' in all_vars:
del all_vars['tags']
diff --git a/lib/ansible/plugins/action/gather_facts.py b/lib/ansible/plugins/action/gather_facts.py
index 50e13b660bb..c2bb544e80f 100644
--- a/lib/ansible/plugins/action/gather_facts.py
+++ b/lib/ansible/plugins/action/gather_facts.py
@@ -25,7 +25,7 @@ class ActionModule(ActionBase):
# TODO: remove in favor of controller side argspec detecing valid arguments
# network facts modules must support gather_subset
try:
- name = self._connection.redirected_names[-1].replace('ansible.netcommon.', '', 1)
+ name = self._connection.redirected_names[-1].removeprefix('ansible.netcommon.')
except (IndexError, AttributeError):
name = self._connection._load_name.split('.')[-1]
if name not in ('network_cli', 'httpapi', 'netconf'):
diff --git a/lib/ansible/plugins/action/template.py b/lib/ansible/plugins/action/template.py
index d4b26e70349..2e3d3641bf4 100644
--- a/lib/ansible/plugins/action/template.py
+++ b/lib/ansible/plugins/action/template.py
@@ -118,8 +118,7 @@ class ActionModule(ActionBase):
searchpath = newsearchpath
# add ansible 'template' vars
- temp_vars = task_vars.copy()
- temp_vars.update(generate_ansible_template_vars(self._task.args.get('src', None), source, dest))
+ temp_vars = task_vars | generate_ansible_template_vars(self._task.args.get('src', None), source, dest)
# force templar to use AnsibleEnvironment to prevent issues with native types
# https://github.com/ansible/ansible/issues/46169
diff --git a/lib/ansible/plugins/action/uri.py b/lib/ansible/plugins/action/uri.py
index e28d9585868..bbaf092e1d0 100644
--- a/lib/ansible/plugins/action/uri.py
+++ b/lib/ansible/plugins/action/uri.py
@@ -82,8 +82,7 @@ class ActionModule(ActionBase):
self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
kwargs['body'] = body
- new_module_args = self._task.args.copy()
- new_module_args.update(kwargs)
+ new_module_args = self._task.args | kwargs
# call with ansible.legacy prefix to prevent collections collisions while allowing local override
result.update(self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py
index 7970e31e1e4..35f16b2fbbc 100644
--- a/lib/ansible/plugins/loader.py
+++ b/lib/ansible/plugins/loader.py
@@ -628,7 +628,7 @@ class PluginLoader:
if candidate_name.startswith('ansible.legacy'):
# 'ansible.legacy' refers to the plugin finding behavior used before collections existed.
# They need to search 'library' and the various '*_plugins' directories in order to find the file.
- plugin_load_context = self._find_plugin_legacy(name.replace('ansible.legacy.', '', 1),
+ plugin_load_context = self._find_plugin_legacy(name.removeprefix('ansible.legacy.'),
plugin_load_context, ignore_deprecated, check_aliases, suffix)
else:
# 'ansible.builtin' should be handled here. This means only internal, or builtin, paths are searched.
diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index e9c675accc2..4ff03b0a2b3 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -883,8 +883,7 @@ class StrategyBase:
ti_copy = included_file._task.copy(exclude_parent=True)
ti_copy._parent = included_file._task._parent
- temp_vars = ti_copy.vars.copy()
- temp_vars.update(included_file._vars)
+ temp_vars = ti_copy.vars | included_file._vars
ti_copy.vars = temp_vars
diff --git a/lib/ansible/template/vars.py b/lib/ansible/template/vars.py
index be2af9fc3b8..fd1b812458f 100644
--- a/lib/ansible/template/vars.py
+++ b/lib/ansible/template/vars.py
@@ -123,7 +123,6 @@ class AnsibleJ2Vars(Mapping):
# prior to version 2.9, locals contained all of the vars and not just the current
# local vars so this was not necessary for locals to propagate down to nested includes
- new_locals = self._locals.copy()
- new_locals.update(locals)
+ new_locals = self._locals | locals
return AnsibleJ2Vars(self._templar, self._globals, locals=new_locals)
diff --git a/lib/ansible/utils/collection_loader/_collection_finder.py b/lib/ansible/utils/collection_loader/_collection_finder.py
index 662346f3f74..d3a8765c779 100644
--- a/lib/ansible/utils/collection_loader/_collection_finder.py
+++ b/lib/ansible/utils/collection_loader/_collection_finder.py
@@ -914,7 +914,7 @@ class AnsibleCollectionRef:
"""
legacy_plugin_dir_name = to_text(legacy_plugin_dir_name)
- plugin_type = legacy_plugin_dir_name.replace(u'_plugins', u'')
+ plugin_type = legacy_plugin_dir_name.removesuffix(u'_plugins')
if plugin_type == u'library':
plugin_type = u'modules'
diff --git a/lib/ansible/utils/vars.py b/lib/ansible/utils/vars.py
index 74bf1425b67..a3224c8b5c1 100644
--- a/lib/ansible/utils/vars.py
+++ b/lib/ansible/utils/vars.py
@@ -88,8 +88,7 @@ def combine_vars(a, b, merge=None):
else:
# HASH_BEHAVIOUR == 'replace'
_validate_mutable_mappings(a, b)
- result = a.copy()
- result.update(b)
+ result = a | b
return result
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
index 93538f70df9..a09704e038b 100644
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -232,9 +232,9 @@ class VariableManager:
try:
for entity in entities:
if isinstance(entity, Host):
- data.update(plugin.get_host_vars(entity.name))
+ data |= plugin.get_host_vars(entity.name)
else:
- data.update(plugin.get_group_vars(entity.name))
+ data |= plugin.get_group_vars(entity.name)
except AttributeError:
if hasattr(plugin, 'run'):
raise AnsibleError("Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
@@ -305,7 +305,7 @@ class VariableManager:
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
try:
facts = wrap_var(self._fact_cache.get(host.name, {}))
- all_vars.update(namespace_facts(facts))
+ all_vars |= namespace_facts(facts)
# push facts to main namespace
if C.INJECT_FACTS_AS_VARS:
@@ -670,7 +670,7 @@ class VariableManager:
raise TypeError('The object retrieved for {0} must be a MutableMapping but was'
' a {1}'.format(host, type(host_cache)))
# Update the existing facts
- host_cache.update(facts)
+ host_cache |= facts
# Save the facts back to the backing store
self._fact_cache[host] = host_cache
@@ -684,7 +684,7 @@ class VariableManager:
raise AnsibleAssertionError("the type of 'facts' to set for nonpersistent_facts should be a Mapping but is a %s" % type(facts))
try:
- self._nonpersistent_fact_cache[host].update(facts)
+ self._nonpersistent_fact_cache[host] |= facts
except KeyError:
self._nonpersistent_fact_cache[host] = facts
diff --git a/lib/ansible/vars/plugins.py b/lib/ansible/vars/plugins.py
index 82beb3ad2ed..c4b7dd8f8ae 100644
--- a/lib/ansible/vars/plugins.py
+++ b/lib/ansible/vars/plugins.py
@@ -28,9 +28,9 @@ def get_plugin_vars(loader, plugin, path, entities):
try:
for entity in entities:
if isinstance(entity, Host):
- data.update(plugin.get_host_vars(entity.name))
+ data |= plugin.get_host_vars(entity.name)
else:
- data.update(plugin.get_group_vars(entity.name))
+ data |= plugin.get_group_vars(entity.name)
except AttributeError:
if hasattr(plugin, 'run'):
raise AnsibleError("Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
diff --git a/setup.cfg b/setup.cfg
index cda414d0197..5fc4433bb5a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -26,7 +26,6 @@ classifiers =
Natural Language :: English
Operating System :: POSIX
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
@@ -37,7 +36,7 @@ classifiers =
[options]
zip_safe = False
-python_requires = >=3.8
+python_requires = >=3.9
include_package_data = True
# keep ansible-test as a verbatim script to work with editable installs, since it needs to do its
# own package redirection magic that's beyond the scope of the normal `ansible` path redirection