summaryrefslogtreecommitdiffstats
path: root/lib/ansible/utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/utils')
-rw-r--r--lib/ansible/utils/__init__.py4
-rw-r--r--lib/ansible/utils/cmd_functions.py3
-rw-r--r--lib/ansible/utils/collection_loader/__init__.py3
-rw-r--r--lib/ansible/utils/collection_loader/_collection_config.py3
-rw-r--r--lib/ansible/utils/collection_loader/_collection_finder.py3
-rw-r--r--lib/ansible/utils/collection_loader/_collection_meta.py3
-rw-r--r--lib/ansible/utils/color.py3
-rw-r--r--lib/ansible/utils/context_objects.py5
-rw-r--r--lib/ansible/utils/display.py119
-rw-r--r--lib/ansible/utils/encrypt.py109
-rw-r--r--lib/ansible/utils/fqcn.py3
-rw-r--r--lib/ansible/utils/galaxy.py4
-rw-r--r--lib/ansible/utils/hashing.py4
-rw-r--r--lib/ansible/utils/helpers.py4
-rw-r--r--lib/ansible/utils/jsonrpc.py3
-rw-r--r--lib/ansible/utils/listify.py4
-rw-r--r--lib/ansible/utils/lock.py4
-rw-r--r--lib/ansible/utils/multiprocessing.py4
-rw-r--r--lib/ansible/utils/native_jinja.py4
-rw-r--r--lib/ansible/utils/path.py3
-rw-r--r--lib/ansible/utils/plugin_docs.py13
-rw-r--r--lib/ansible/utils/py3compat.py72
-rw-r--r--lib/ansible/utils/sentinel.py4
-rw-r--r--lib/ansible/utils/shlex.py4
-rw-r--r--lib/ansible/utils/singleton.py4
-rw-r--r--lib/ansible/utils/ssh_functions.py4
-rw-r--r--lib/ansible/utils/unicode.py4
-rw-r--r--lib/ansible/utils/unsafe_proxy.py3
-rw-r--r--lib/ansible/utils/vars.py14
-rw-r--r--lib/ansible/utils/version.py4
30 files changed, 148 insertions, 270 deletions
diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py
index ae8ccff..64fee52 100644
--- a/lib/ansible/utils/__init__.py
+++ b/lib/ansible/utils/__init__.py
@@ -15,6 +15,4 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
diff --git a/lib/ansible/utils/cmd_functions.py b/lib/ansible/utils/cmd_functions.py
index 436d955..99de684 100644
--- a/lib/ansible/utils/cmd_functions.py
+++ b/lib/ansible/utils/cmd_functions.py
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import select
diff --git a/lib/ansible/utils/collection_loader/__init__.py b/lib/ansible/utils/collection_loader/__init__.py
index 83cc246..2ae2fe5 100644
--- a/lib/ansible/utils/collection_loader/__init__.py
+++ b/lib/ansible/utils/collection_loader/__init__.py
@@ -4,8 +4,7 @@
# CAUTION: This implementation of the collection loader is used by ansible-test.
# Because of this, it must be compatible with all Python versions supported on the controller or remote.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
# FIXME: decide what of this we want to actually be public/toplevel, put other stuff on a utility class?
from ._collection_config import AnsibleCollectionConfig
diff --git a/lib/ansible/utils/collection_loader/_collection_config.py b/lib/ansible/utils/collection_loader/_collection_config.py
index 4f73a1a..add20c6 100644
--- a/lib/ansible/utils/collection_loader/_collection_config.py
+++ b/lib/ansible/utils/collection_loader/_collection_config.py
@@ -4,8 +4,7 @@
# CAUTION: This implementation of the collection loader is used by ansible-test.
# Because of this, it must be compatible with all Python versions supported on the controller or remote.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.six import add_metaclass
diff --git a/lib/ansible/utils/collection_loader/_collection_finder.py b/lib/ansible/utils/collection_loader/_collection_finder.py
index 16d0bcc..85660b4 100644
--- a/lib/ansible/utils/collection_loader/_collection_finder.py
+++ b/lib/ansible/utils/collection_loader/_collection_finder.py
@@ -4,8 +4,7 @@
# CAUTION: This implementation of the collection loader is used by ansible-test.
# Because of this, it must be compatible with all Python versions supported on the controller or remote.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import itertools
import os
diff --git a/lib/ansible/utils/collection_loader/_collection_meta.py b/lib/ansible/utils/collection_loader/_collection_meta.py
index deaac8e..3b0333f 100644
--- a/lib/ansible/utils/collection_loader/_collection_meta.py
+++ b/lib/ansible/utils/collection_loader/_collection_meta.py
@@ -4,8 +4,7 @@
# CAUTION: This implementation of the collection loader is used by ansible-test.
# Because of this, it must be compatible with all Python versions supported on the controller or remote.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
try:
from collections.abc import Mapping
diff --git a/lib/ansible/utils/color.py b/lib/ansible/utils/color.py
index be8fb00..0e00635 100644
--- a/lib/ansible/utils/color.py
+++ b/lib/ansible/utils/color.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import re
import sys
diff --git a/lib/ansible/utils/context_objects.py b/lib/ansible/utils/context_objects.py
index efe15fe..02db666 100644
--- a/lib/ansible/utils/context_objects.py
+++ b/lib/ansible/utils/context_objects.py
@@ -1,13 +1,10 @@
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
"""
Hold command line arguments for use in other modules
"""
+from __future__ import annotations
from abc import ABCMeta
from collections.abc import Container, Mapping, Sequence, Set
diff --git a/lib/ansible/utils/display.py b/lib/ansible/utils/display.py
index 3f331ad..9616f18 100644
--- a/lib/ansible/utils/display.py
+++ b/lib/ansible/utils/display.py
@@ -59,6 +59,8 @@ if t.TYPE_CHECKING:
# avoid circular import at runtime
from ansible.executor.task_queue_manager import FinalQueue
+P = t.ParamSpec('P')
+
_LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
# Set argtypes, to avoid segfault if the wrong type is provided,
# restype is assumed to be c_int
@@ -122,20 +124,6 @@ def get_text_width(text: str) -> int:
return width if width >= 0 else 0
-def proxy_display(method):
-
- def proxyit(self, *args, **kwargs):
- if self._final_q:
- # If _final_q is set, that means we are in a WorkerProcess
- # and instead of displaying messages directly from the fork
- # we will proxy them through the queue
- return self._final_q.send_display(method.__name__, *args, **kwargs)
- else:
- return method(self, *args, **kwargs)
-
- return proxyit
-
-
class FilterBlackList(logging.Filter):
def __init__(self, blacklist):
self.blacklist = [logging.Filter(name) for name in blacklist]
@@ -283,6 +271,11 @@ class Display(metaclass=Singleton):
self.columns = None
self.verbosity = verbosity
+ if C.LOG_VERBOSITY is None:
+ self.log_verbosity = verbosity
+ else:
+ self.log_verbosity = max(verbosity, C.LOG_VERBOSITY)
+
# list of all deprecation messages to prevent duplicate display
self._deprecations: dict[str, int] = {}
self._warns: dict[str, int] = {}
@@ -354,7 +347,49 @@ class Display(metaclass=Singleton):
if os.path.exists(b_cow_path):
self.b_cowsay = b_cow_path
- @proxy_display
+ @staticmethod
+ def _proxy(
+ func: c.Callable[t.Concatenate[Display, P], None]
+ ) -> c.Callable[..., None]:
+ @wraps(func)
+ def wrapper(self, *args: P.args, **kwargs: P.kwargs) -> None:
+ if self._final_q:
+ # If _final_q is set, that means we are in a WorkerProcess
+ # and instead of displaying messages directly from the fork
+ # we will proxy them through the queue
+ return self._final_q.send_display(func.__name__, *args, **kwargs)
+ return func(self, *args, **kwargs)
+ return wrapper
+
+ @staticmethod
+ def _meets_debug(
+ func: c.Callable[..., None]
+ ) -> c.Callable[..., None]:
+ """This method ensures that debug is enabled before delegating to the proxy
+ """
+ @wraps(func)
+ def wrapper(self, msg: str, host: str | None = None) -> None:
+ if not C.DEFAULT_DEBUG:
+ return
+ return func(self, msg, host=host)
+ return wrapper
+
+ @staticmethod
+ def _meets_verbosity(
+ func: c.Callable[..., None]
+ ) -> c.Callable[..., None]:
+ """This method ensures the verbosity has been met before delegating to the proxy
+
+ Currently this method is unused, and the logic is handled directly in ``verbose``
+ """
+ @wraps(func)
+ def wrapper(self, msg: str, host: str | None = None, caplevel: int = None) -> None:
+ if self.verbosity > caplevel:
+ return func(self, msg, host=host, caplevel=caplevel)
+ return
+ return wrapper
+
+ @_proxy
def display(
self,
msg: str,
@@ -412,7 +447,12 @@ class Display(metaclass=Singleton):
# raise
if logger and not screen_only:
- msg2 = nocolor.lstrip('\n')
+ self._log(nocolor, color)
+
+ def _log(self, msg: str, color: str | None = None, caplevel: int | None = None):
+
+ if logger and (caplevel is None or self.log_verbosity > caplevel):
+ msg2 = msg.lstrip('\n')
lvl = logging.INFO
if color:
@@ -422,6 +462,7 @@ class Display(metaclass=Singleton):
except KeyError:
# this should not happen, but JIC
raise AnsibleAssertionError('Invalid color supplied to display: %s' % color)
+
# actually log
logger.log(lvl, msg2)
@@ -443,21 +484,35 @@ class Display(metaclass=Singleton):
def vvvvvv(self, msg: str, host: str | None = None) -> None:
return self.verbose(msg, host=host, caplevel=5)
- def debug(self, msg: str, host: str | None = None) -> None:
- if C.DEFAULT_DEBUG:
- if host is None:
- self.display("%6d %0.5f: %s" % (os.getpid(), time.time(), msg), color=C.COLOR_DEBUG)
- else:
- self.display("%6d %0.5f [%s]: %s" % (os.getpid(), time.time(), host, msg), color=C.COLOR_DEBUG)
-
def verbose(self, msg: str, host: str | None = None, caplevel: int = 2) -> None:
+ if self.verbosity > caplevel:
+ self._verbose_display(msg, host=host, caplevel=caplevel)
+ if self.log_verbosity > self.verbosity and self.log_verbosity > caplevel:
+ self._verbose_log(msg, host=host, caplevel=caplevel)
+
+ @_proxy
+ def _verbose_display(self, msg: str, host: str | None = None, caplevel: int = 2) -> None:
to_stderr = C.VERBOSE_TO_STDERR
- if self.verbosity > caplevel:
- if host is None:
- self.display(msg, color=C.COLOR_VERBOSE, stderr=to_stderr)
- else:
- self.display("<%s> %s" % (host, msg), color=C.COLOR_VERBOSE, stderr=to_stderr)
+ if host is None:
+ self.display(msg, color=C.COLOR_VERBOSE, stderr=to_stderr)
+ else:
+ self.display("<%s> %s" % (host, msg), color=C.COLOR_VERBOSE, stderr=to_stderr)
+
+ @_proxy
+ def _verbose_log(self, msg: str, host: str | None = None, caplevel: int = 2) -> None:
+ # we send to log if log was configured with higher verbosity
+ if host is not None:
+ msg = "<%s> %s" % (host, msg)
+ self._log(msg, C.COLOR_VERBOSE, caplevel)
+
+ @_meets_debug
+ @_proxy
+ def debug(self, msg: str, host: str | None = None) -> None:
+ if host is None:
+ self.display("%6d %0.5f: %s" % (os.getpid(), time.time(), msg), color=C.COLOR_DEBUG)
+ else:
+ self.display("%6d %0.5f [%s]: %s" % (os.getpid(), time.time(), host, msg), color=C.COLOR_DEBUG)
def get_deprecation_message(
self,
@@ -501,7 +556,7 @@ class Display(metaclass=Singleton):
return message_text
- @proxy_display
+ @_proxy
def deprecated(
self,
msg: str,
@@ -525,7 +580,7 @@ class Display(metaclass=Singleton):
self.display(message_text.strip(), color=C.COLOR_DEPRECATE, stderr=True)
self._deprecations[message_text] = 1
- @proxy_display
+ @_proxy
def warning(self, msg: str, formatted: bool = False) -> None:
if not formatted:
@@ -539,10 +594,12 @@ class Display(metaclass=Singleton):
self.display(new_msg, color=C.COLOR_WARN, stderr=True)
self._warns[new_msg] = 1
+ @_proxy
def system_warning(self, msg: str) -> None:
if C.SYSTEM_WARNINGS:
self.warning(msg)
+ @_proxy
def banner(self, msg: str, color: str | None = None, cows: bool = True) -> None:
'''
Prints a header-looking line with cowsay or stars with length depending on terminal width (3 minimum)
@@ -566,6 +623,7 @@ class Display(metaclass=Singleton):
stars = u"*" * star_len
self.display(u"\n%s %s" % (msg, stars), color=color)
+ @_proxy
def banner_cowsay(self, msg: str, color: str | None = None) -> None:
if u": [" in msg:
msg = msg.replace(u"[", u"")
@@ -583,6 +641,7 @@ class Display(metaclass=Singleton):
(out, err) = cmd.communicate()
self.display(u"%s\n" % to_text(out), color=color)
+ @_proxy
def error(self, msg: str, wrap_text: bool = True) -> None:
if wrap_text:
new_msg = u"\n[ERROR]: %s" % msg
diff --git a/lib/ansible/utils/encrypt.py b/lib/ansible/utils/encrypt.py
index 541c5c8..3a279b7 100644
--- a/lib/ansible/utils/encrypt.py
+++ b/lib/ansible/utils/encrypt.py
@@ -1,13 +1,10 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import random
-import re
import string
-import sys
from collections import namedtuple
@@ -17,8 +14,8 @@ from ansible.module_utils.six import text_type
from ansible.module_utils.common.text.converters import to_text, to_bytes
from ansible.utils.display import Display
-PASSLIB_E = CRYPT_E = None
-HAS_CRYPT = PASSLIB_AVAILABLE = False
+PASSLIB_E = None
+PASSLIB_AVAILABLE = False
try:
import passlib
import passlib.hash
@@ -31,12 +28,6 @@ try:
except Exception as e:
PASSLIB_E = e
-try:
- import crypt
- HAS_CRYPT = True
-except Exception as e:
- CRYPT_E = e
-
display = Display()
@@ -84,96 +75,6 @@ class BaseHash(object):
self.algorithm = algorithm
-class CryptHash(BaseHash):
- def __init__(self, algorithm):
- super(CryptHash, self).__init__(algorithm)
-
- if not HAS_CRYPT:
- raise AnsibleError("crypt.crypt cannot be used as the 'crypt' python library is not installed or is unusable.", orig_exc=CRYPT_E)
-
- if sys.platform.startswith('darwin'):
- raise AnsibleError("crypt.crypt not supported on Mac OS X/Darwin, install passlib python module")
-
- if algorithm not in self.algorithms:
- raise AnsibleError("crypt.crypt does not support '%s' algorithm" % self.algorithm)
-
- display.deprecated(
- "Encryption using the Python crypt module is deprecated. The "
- "Python crypt module is deprecated and will be removed from "
- "Python 3.13. Install the passlib library for continued "
- "encryption functionality.",
- version="2.17",
- )
-
- self.algo_data = self.algorithms[algorithm]
-
- def hash(self, secret, salt=None, salt_size=None, rounds=None, ident=None):
- salt = self._salt(salt, salt_size)
- rounds = self._rounds(rounds)
- ident = self._ident(ident)
- return self._hash(secret, salt, rounds, ident)
-
- def _salt(self, salt, salt_size):
- salt_size = salt_size or self.algo_data.salt_size
- ret = salt or random_salt(salt_size)
- if re.search(r'[^./0-9A-Za-z]', ret):
- raise AnsibleError("invalid characters in salt")
- if self.algo_data.salt_exact and len(ret) != self.algo_data.salt_size:
- raise AnsibleError("invalid salt size")
- elif not self.algo_data.salt_exact and len(ret) > self.algo_data.salt_size:
- raise AnsibleError("invalid salt size")
- return ret
-
- def _rounds(self, rounds):
- if self.algorithm == 'bcrypt':
- # crypt requires 2 digits for rounds
- return rounds or self.algo_data.implicit_rounds
- elif rounds == self.algo_data.implicit_rounds:
- # Passlib does not include the rounds if it is the same as implicit_rounds.
- # Make crypt lib behave the same, by not explicitly specifying the rounds in that case.
- return None
- else:
- return rounds
-
- def _ident(self, ident):
- if not ident:
- return self.algo_data.crypt_id
- if self.algorithm == 'bcrypt':
- return ident
- return None
-
- def _hash(self, secret, salt, rounds, ident):
- saltstring = ""
- if ident:
- saltstring = "$%s" % ident
-
- if rounds:
- if self.algorithm == 'bcrypt':
- saltstring += "$%d" % rounds
- else:
- saltstring += "$rounds=%d" % rounds
-
- saltstring += "$%s" % salt
-
- # crypt.crypt throws OSError on Python >= 3.9 if it cannot parse saltstring.
- try:
- result = crypt.crypt(secret, saltstring)
- orig_exc = None
- except OSError as e:
- result = None
- orig_exc = e
-
- # None as result would be interpreted by some modules (user module)
- # as no password at all.
- if not result:
- raise AnsibleError(
- "crypt.crypt does not support '%s' algorithm" % self.algorithm,
- orig_exc=orig_exc,
- )
-
- return result
-
-
class PasslibHash(BaseHash):
def __init__(self, algorithm):
super(PasslibHash, self).__init__(algorithm)
@@ -274,6 +175,4 @@ def passlib_or_crypt(secret, algorithm, salt=None, salt_size=None, rounds=None,
def do_encrypt(result, encrypt, salt_size=None, salt=None, ident=None, rounds=None):
if PASSLIB_AVAILABLE:
return PasslibHash(encrypt).hash(result, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident)
- if HAS_CRYPT:
- return CryptHash(encrypt).hash(result, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident)
- raise AnsibleError("Unable to encrypt nor hash, either crypt or passlib must be installed.", orig_exc=CRYPT_E)
+ raise AnsibleError("Unable to encrypt nor hash, passlib must be installed", orig_exc=PASSLIB_E)
diff --git a/lib/ansible/utils/fqcn.py b/lib/ansible/utils/fqcn.py
index a492be1..043d8a0 100644
--- a/lib/ansible/utils/fqcn.py
+++ b/lib/ansible/utils/fqcn.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
def add_internal_fqcns(names):
diff --git a/lib/ansible/utils/galaxy.py b/lib/ansible/utils/galaxy.py
index bbb26fb..977ae2c 100644
--- a/lib/ansible/utils/galaxy.py
+++ b/lib/ansible/utils/galaxy.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import tempfile
diff --git a/lib/ansible/utils/hashing.py b/lib/ansible/utils/hashing.py
index 97ea1dc..e8faf25 100644
--- a/lib/ansible/utils/hashing.py
+++ b/lib/ansible/utils/hashing.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
diff --git a/lib/ansible/utils/helpers.py b/lib/ansible/utils/helpers.py
index 658ad99..c9b5f16 100644
--- a/lib/ansible/utils/helpers.py
+++ b/lib/ansible/utils/helpers.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.module_utils.six import string_types
diff --git a/lib/ansible/utils/jsonrpc.py b/lib/ansible/utils/jsonrpc.py
index 2af8bd3..37b286a 100644
--- a/lib/ansible/utils/jsonrpc.py
+++ b/lib/ansible/utils/jsonrpc.py
@@ -1,8 +1,7 @@
# (c) 2017, Peter Sprygada <psprygad@redhat.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import json
import pickle
diff --git a/lib/ansible/utils/listify.py b/lib/ansible/utils/listify.py
index 0e6a872..362a50b 100644
--- a/lib/ansible/utils/listify.py
+++ b/lib/ansible/utils/listify.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from collections.abc import Iterable
diff --git a/lib/ansible/utils/lock.py b/lib/ansible/utils/lock.py
index 34387dc..9f834da 100644
--- a/lib/ansible/utils/lock.py
+++ b/lib/ansible/utils/lock.py
@@ -1,9 +1,7 @@
# Copyright (c) 2020 Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from functools import wraps
diff --git a/lib/ansible/utils/multiprocessing.py b/lib/ansible/utils/multiprocessing.py
index 2912f71..c573c72 100644
--- a/lib/ansible/utils/multiprocessing.py
+++ b/lib/ansible/utils/multiprocessing.py
@@ -1,9 +1,7 @@
# Copyright (c) 2019 Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import multiprocessing
diff --git a/lib/ansible/utils/native_jinja.py b/lib/ansible/utils/native_jinja.py
index 53ef140..15d1624 100644
--- a/lib/ansible/utils/native_jinja.py
+++ b/lib/ansible/utils/native_jinja.py
@@ -1,9 +1,7 @@
# Copyright: (c) 2020, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.module_utils.six import text_type
diff --git a/lib/ansible/utils/path.py b/lib/ansible/utils/path.py
index e4e00ce..ac0b450 100644
--- a/lib/ansible/utils/path.py
+++ b/lib/ansible/utils/path.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import shutil
diff --git a/lib/ansible/utils/plugin_docs.py b/lib/ansible/utils/plugin_docs.py
index 91b3722..c5089aa 100644
--- a/lib/ansible/utils/plugin_docs.py
+++ b/lib/ansible/utils/plugin_docs.py
@@ -1,8 +1,7 @@
# Copyright: (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from collections.abc import MutableMapping, MutableSet, MutableSequence
from pathlib import Path
@@ -128,7 +127,7 @@ def add_fragments(doc, filename, fragment_loader, is_module=False):
fragments = doc.pop('extends_documentation_fragment', [])
if isinstance(fragments, string_types):
- fragments = [fragments]
+ fragments = fragments.split(',')
unknown_fragments = []
@@ -138,7 +137,7 @@ def add_fragments(doc, filename, fragment_loader, is_module=False):
# as-specified. If failure, assume the right-most component is a var, split it off,
# and retry the load.
for fragment_slug in fragments:
- fragment_name = fragment_slug
+ fragment_name = fragment_slug.strip()
fragment_var = 'DOCUMENTATION'
fragment_class = fragment_loader.get(fragment_name)
@@ -314,7 +313,7 @@ def find_plugin_docfile(plugin, plugin_type, loader):
if filename is None:
raise AnsibleError('%s cannot contain DOCUMENTATION nor does it have a companion documentation file' % (plugin))
- return filename, context.plugin_resolved_collection
+ return filename, context
def get_plugin_docs(plugin, plugin_type, loader, fragment_loader, verbose):
@@ -323,7 +322,8 @@ def get_plugin_docs(plugin, plugin_type, loader, fragment_loader, verbose):
# find plugin doc file, if it doesn't exist this will throw error, we let it through
# can raise exception and short circuit when 'not found'
- filename, collection_name = find_plugin_docfile(plugin, plugin_type, loader)
+ filename, context = find_plugin_docfile(plugin, plugin_type, loader)
+ collection_name = context.plugin_resolved_collection
try:
docs = get_docstring(filename, fragment_loader, verbose=verbose, collection_name=collection_name, plugin_type=plugin_type)
@@ -347,5 +347,6 @@ def get_plugin_docs(plugin, plugin_type, loader, fragment_loader, verbose):
else:
docs[0]['filename'] = filename
docs[0]['collection'] = collection_name
+ docs[0]['plugin_name'] = context.resolved_fqcn
return docs
diff --git a/lib/ansible/utils/py3compat.py b/lib/ansible/utils/py3compat.py
index 5201132..53f06ff 100644
--- a/lib/ansible/utils/py3compat.py
+++ b/lib/ansible/utils/py3compat.py
@@ -1,70 +1,32 @@
# -*- coding: utf-8 -*-
#
# (c) 2018, Toshio Kuratomi <a.badger@gmail.com>
+# Copyright: Contributors to the Ansible project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-# Note that the original author of this, Toshio Kuratomi, is trying to submit this to six. If
-# successful, the code in six will be available under six's more liberal license:
-# https://mail.python.org/pipermail/python-porting/2018-July/000539.html
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
+import inspect
import os
-import sys
-
-from collections.abc import MutableMapping
-
-from ansible.module_utils.six import PY3
-from ansible.module_utils.common.text.converters import to_bytes, to_text
-
-__all__ = ('environ',)
-
-
-class _TextEnviron(MutableMapping):
- """
- Utility class to return text strings from the environment instead of byte strings
- Mimics the behaviour of os.environ on Python3
- """
- def __init__(self, env=None, encoding=None):
- if env is None:
- env = os.environ
- self._raw_environ = env
- self._value_cache = {}
- # Since we're trying to mimic Python3's os.environ, use sys.getfilesystemencoding()
- # instead of utf-8
- if encoding is None:
- # Since we're trying to mimic Python3's os.environ, use sys.getfilesystemencoding()
- # instead of utf-8
- self.encoding = sys.getfilesystemencoding()
- else:
- self.encoding = encoding
+from ansible.utils.display import Display
- def __delitem__(self, key):
- del self._raw_environ[key]
- def __getitem__(self, key):
- value = self._raw_environ[key]
- if PY3:
- return value
- # Cache keys off of the undecoded values to handle any environment variables which change
- # during a run
- if value not in self._value_cache:
- self._value_cache[value] = to_text(value, encoding=self.encoding,
- nonstring='passthru', errors='surrogate_or_strict')
- return self._value_cache[value]
+display = Display()
- def __setitem__(self, key, value):
- self._raw_environ[key] = to_bytes(value, encoding=self.encoding, nonstring='strict',
- errors='surrogate_or_strict')
- def __iter__(self):
- return self._raw_environ.__iter__()
+def __getattr__(name):
+ if name != 'environ':
+ raise AttributeError(name)
- def __len__(self):
- return len(self._raw_environ)
+ caller = inspect.stack()[1]
+ display.deprecated(
+ (
+ 'ansible.utils.py3compat.environ is deprecated in favor of os.environ. '
+ f'Accessed by {caller.filename} line number {caller.lineno}'
+ ),
+ version='2.20',
+ )
-environ = _TextEnviron(encoding='utf-8')
+ return os.environ
diff --git a/lib/ansible/utils/sentinel.py b/lib/ansible/utils/sentinel.py
index ca4f827..0fdbf4c 100644
--- a/lib/ansible/utils/sentinel.py
+++ b/lib/ansible/utils/sentinel.py
@@ -1,9 +1,7 @@
# Copyright (c) 2019 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class Sentinel:
diff --git a/lib/ansible/utils/shlex.py b/lib/ansible/utils/shlex.py
index 8f50ffd..470270d 100644
--- a/lib/ansible/utils/shlex.py
+++ b/lib/ansible/utils/shlex.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# alongwith Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import shlex
diff --git a/lib/ansible/utils/singleton.py b/lib/ansible/utils/singleton.py
index 4299403..0b68423 100644
--- a/lib/ansible/utils/singleton.py
+++ b/lib/ansible/utils/singleton.py
@@ -1,9 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from threading import RLock
diff --git a/lib/ansible/utils/ssh_functions.py b/lib/ansible/utils/ssh_functions.py
index 594dbc0..a96249e 100644
--- a/lib/ansible/utils/ssh_functions.py
+++ b/lib/ansible/utils/ssh_functions.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import subprocess
diff --git a/lib/ansible/utils/unicode.py b/lib/ansible/utils/unicode.py
index b5304ba..2ea456c 100644
--- a/lib/ansible/utils/unicode.py
+++ b/lib/ansible/utils/unicode.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.module_utils.common.text.converters import to_text
diff --git a/lib/ansible/utils/unsafe_proxy.py b/lib/ansible/utils/unsafe_proxy.py
index b3e7383..378725c 100644
--- a/lib/ansible/utils/unsafe_proxy.py
+++ b/lib/ansible/utils/unsafe_proxy.py
@@ -50,8 +50,7 @@
# http://code.activestate.com/recipes/496741-object-proxying/
# Author: Tomer Filiba
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import sys
import types
diff --git a/lib/ansible/utils/vars.py b/lib/ansible/utils/vars.py
index 5e21cb3..373fc70 100644
--- a/lib/ansible/utils/vars.py
+++ b/lib/ansible/utils/vars.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import keyword
import random
@@ -85,11 +83,11 @@ def combine_vars(a, b, merge=None):
if merge or merge is None and C.DEFAULT_HASH_BEHAVIOUR == "merge":
return merge_hash(a, b)
- else:
- # HASH_BEHAVIOUR == 'replace'
- _validate_mutable_mappings(a, b)
- result = a | b
- return result
+
+ # HASH_BEHAVIOUR == 'replace'
+ _validate_mutable_mappings(a, b)
+ result = a | b
+ return result
def merge_hash(x, y, recursive=True, list_merge='replace'):
diff --git a/lib/ansible/utils/version.py b/lib/ansible/utils/version.py
index e7da9fd..77c8228 100644
--- a/lib/ansible/utils/version.py
+++ b/lib/ansible/utils/version.py
@@ -1,9 +1,7 @@
# Copyright (c) 2020 Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import re