diff --git a/lib/ansible/compat/selectors/__init__.py b/lib/ansible/compat/selectors.py
similarity index 69%
rename from lib/ansible/compat/selectors/__init__.py
rename to lib/ansible/compat/selectors.py
index 6998c2ead40..0117f362ad7 100644
--- a/lib/ansible/compat/selectors/__init__.py
+++ b/lib/ansible/compat/selectors.py
@@ -15,16 +15,18 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
-# NOT_BUNDLED
-
-'''
-Compat selectors library. Python-3.5 has this builtin. The selectors2
-package exists on pypi to backport the functionality as far as python-2.6.
-Implementation previously resided here - maintaining this file after the
-move to ansible.module_utils for code backwards compatibility.
-'''
from __future__ import annotations
import sys
-from ansible.module_utils.compat import selectors
+import selectors
+
+from ansible.module_utils.common.warnings import deprecate
+
+
sys.modules['ansible.compat.selectors'] = selectors
+
+
+deprecate(
+ msg='The `ansible.module_utils.compat.selectors` module is deprecated.',
+ version='2.19',
+)
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index a6dedcd21b7..604b69b35ec 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -34,6 +34,7 @@ import pwd
import platform
import re
import select
+import selectors
import shlex
import shutil
import signal
@@ -72,8 +73,6 @@ except ImportError:
# Python2 & 3 way to get NoneType
NoneType = type(None)
-from ansible.module_utils.compat import selectors
-
from ._text import to_native, to_bytes, to_text
from ansible.module_utils.common.text.converters import (
jsonify,
diff --git a/lib/ansible/module_utils/compat/_selectors2.py b/lib/ansible/module_utils/compat/_selectors2.py
deleted file mode 100644
index 7caf4c31d7f..00000000000
--- a/lib/ansible/module_utils/compat/_selectors2.py
+++ /dev/null
@@ -1,656 +0,0 @@
-# This file is from the selectors2.py package. It backports the PSF Licensed
-# selectors module from the Python-3.5 stdlib to older versions of Python.
-# The author, Seth Michael Larson, dual licenses his modifications under the
-# PSF License and MIT License:
-# https://github.com/SethMichaelLarson/selectors2#license
-#
-# Copyright (c) 2016 Seth Michael Larson
-#
-# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
-# MIT License (see licenses/MIT-license.txt or https://opensource.org/licenses/MIT)
-#
-
-
-# Backport of selectors.py from Python 3.5+ to support Python < 3.4
-# Also has the behavior specified in PEP 475 which is to retry syscalls
-# in the case of an EINTR error. This module is required because selectors34
-# does not follow this behavior and instead returns that no file descriptor
-# events have occurred rather than retry the syscall. The decision to drop
-# support for select.devpoll is made to maintain 100% test coverage.
-from __future__ import annotations
-
-import errno
-import math
-import select
-import socket
-import sys
-import time
-from collections import namedtuple
-from ansible.module_utils.six.moves.collections_abc import Mapping
-
-try:
- monotonic = time.monotonic
-except (AttributeError, ImportError): # Python 3.3<
- monotonic = time.time
-
-__author__ = 'Seth Michael Larson'
-__email__ = 'sethmichaellarson@protonmail.com'
-__version__ = '1.1.1'
-__license__ = 'MIT'
-
-__all__ = [
- 'EVENT_READ',
- 'EVENT_WRITE',
- 'SelectorError',
- 'SelectorKey',
- 'DefaultSelector'
-]
-
-EVENT_READ = (1 << 0)
-EVENT_WRITE = (1 << 1)
-
-HAS_SELECT = True # Variable that shows whether the platform has a selector.
-_SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None.
-
-
-class SelectorError(Exception):
- def __init__(self, errcode):
- super(SelectorError, self).__init__()
- self.errno = errcode
-
- def __repr__(self):
- return "".format(self.errno)
-
- def __str__(self):
- return self.__repr__()
-
-
-def _fileobj_to_fd(fileobj):
- """ Return a file descriptor from a file object. If
- given an integer will simply return that integer back. """
- if isinstance(fileobj, int):
- fd = fileobj
- else:
- try:
- fd = int(fileobj.fileno())
- except (AttributeError, TypeError, ValueError):
- raise ValueError("Invalid file object: {0!r}".format(fileobj))
- if fd < 0:
- raise ValueError("Invalid file descriptor: {0}".format(fd))
- return fd
-
-
-# Python 3.5 uses a more direct route to wrap system calls to increase speed.
-if sys.version_info >= (3, 5):
- def _syscall_wrapper(func, dummy, *args, **kwargs):
- """ This is the short-circuit version of the below logic
- because in Python 3.5+ all selectors restart system calls. """
- try:
- return func(*args, **kwargs)
- except (OSError, IOError, select.error) as e:
- errcode = None
- if hasattr(e, "errno"):
- errcode = e.errno
- elif hasattr(e, "args"):
- errcode = e.args[0]
- raise SelectorError(errcode)
-else:
- def _syscall_wrapper(func, recalc_timeout, *args, **kwargs):
- """ Wrapper function for syscalls that could fail due to EINTR.
- All functions should be retried if there is time left in the timeout
- in accordance with PEP 475. """
- timeout = kwargs.get("timeout", None)
- if timeout is None:
- expires = None
- recalc_timeout = False
- else:
- timeout = float(timeout)
- if timeout < 0.0: # Timeout less than 0 treated as no timeout.
- expires = None
- else:
- expires = monotonic() + timeout
-
- args = list(args)
- if recalc_timeout and "timeout" not in kwargs:
- raise ValueError(
- "Timeout must be in args or kwargs to be recalculated")
-
- result = _SYSCALL_SENTINEL
- while result is _SYSCALL_SENTINEL:
- try:
- result = func(*args, **kwargs)
- # OSError is thrown by select.select
- # IOError is thrown by select.epoll.poll
- # select.error is thrown by select.poll.poll
- # Aren't we thankful for Python 3.x rework for exceptions?
- except (OSError, IOError, select.error) as e:
- # select.error wasn't a subclass of OSError in the past.
- errcode = None
- if hasattr(e, "errno"):
- errcode = e.errno
- elif hasattr(e, "args"):
- errcode = e.args[0]
-
- # Also test for the Windows equivalent of EINTR.
- is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and
- errcode == errno.WSAEINTR))
-
- if is_interrupt:
- if expires is not None:
- current_time = monotonic()
- if current_time > expires:
- raise OSError(errno.ETIMEDOUT)
- if recalc_timeout:
- if "timeout" in kwargs:
- kwargs["timeout"] = expires - current_time
- continue
- if errcode:
- raise SelectorError(errcode)
- else:
- raise
- return result
-
-
-SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
-
-
-class _SelectorMapping(Mapping):
- """ Mapping of file objects to selector keys """
-
- def __init__(self, selector):
- self._selector = selector
-
- def __len__(self):
- return len(self._selector._fd_to_key)
-
- def __getitem__(self, fileobj):
- try:
- fd = self._selector._fileobj_lookup(fileobj)
- return self._selector._fd_to_key[fd]
- except KeyError:
- raise KeyError("{0!r} is not registered.".format(fileobj))
-
- def __iter__(self):
- return iter(self._selector._fd_to_key)
-
-
-class BaseSelector(object):
- """ Abstract Selector class
-
- A selector supports registering file objects to be monitored
- for specific I/O events.
-
- A file object is a file descriptor or any object with a
- `fileno()` method. An arbitrary object can be attached to the
- file object which can be used for example to store context info,
- a callback, etc.
-
- A selector can use various implementations (select(), poll(), epoll(),
- and kqueue()) depending on the platform. The 'DefaultSelector' class uses
- the most efficient implementation for the current platform.
- """
- def __init__(self):
- # Maps file descriptors to keys.
- self._fd_to_key = {}
-
- # Read-only mapping returned by get_map()
- self._map = _SelectorMapping(self)
-
- def _fileobj_lookup(self, fileobj):
- """ Return a file descriptor from a file object.
- This wraps _fileobj_to_fd() to do an exhaustive
- search in case the object is invalid but we still
- have it in our map. Used by unregister() so we can
- unregister an object that was previously registered
- even if it is closed. It is also used by _SelectorMapping
- """
- try:
- return _fileobj_to_fd(fileobj)
- except ValueError:
-
- # Search through all our mapped keys.
- for key in self._fd_to_key.values():
- if key.fileobj is fileobj:
- return key.fd
-
- # Raise ValueError after all.
- raise
-
- def register(self, fileobj, events, data=None):
- """ Register a file object for a set of events to monitor. """
- if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
- raise ValueError("Invalid events: {0!r}".format(events))
-
- key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
-
- if key.fd in self._fd_to_key:
- raise KeyError("{0!r} (FD {1}) is already registered"
- .format(fileobj, key.fd))
-
- self._fd_to_key[key.fd] = key
- return key
-
- def unregister(self, fileobj):
- """ Unregister a file object from being monitored. """
- try:
- key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
- except KeyError:
- raise KeyError("{0!r} is not registered".format(fileobj))
-
- # Getting the fileno of a closed socket on Windows errors with EBADF.
- except socket.error as err:
- if err.errno != errno.EBADF:
- raise
- else:
- for key in self._fd_to_key.values():
- if key.fileobj is fileobj:
- self._fd_to_key.pop(key.fd)
- break
- else:
- raise KeyError("{0!r} is not registered".format(fileobj))
- return key
-
- def modify(self, fileobj, events, data=None):
- """ Change a registered file object monitored events and data. """
- # NOTE: Some subclasses optimize this operation even further.
- try:
- key = self._fd_to_key[self._fileobj_lookup(fileobj)]
- except KeyError:
- raise KeyError("{0!r} is not registered".format(fileobj))
-
- if events != key.events:
- self.unregister(fileobj)
- key = self.register(fileobj, events, data)
-
- elif data != key.data:
- # Use a shortcut to update the data.
- key = key._replace(data=data)
- self._fd_to_key[key.fd] = key
-
- return key
-
- def select(self, timeout=None):
- """ Perform the actual selection until some monitored file objects
- are ready or the timeout expires. """
- raise NotImplementedError()
-
- def close(self):
- """ Close the selector. This must be called to ensure that all
- underlying resources are freed. """
- self._fd_to_key.clear()
- self._map = None
-
- def get_key(self, fileobj):
- """ Return the key associated with a registered file object. """
- mapping = self.get_map()
- if mapping is None:
- raise RuntimeError("Selector is closed")
- try:
- return mapping[fileobj]
- except KeyError:
- raise KeyError("{0!r} is not registered".format(fileobj))
-
- def get_map(self):
- """ Return a mapping of file objects to selector keys """
- return self._map
-
- def _key_from_fd(self, fd):
- """ Return the key associated to a given file descriptor
- Return None if it is not found. """
- try:
- return self._fd_to_key[fd]
- except KeyError:
- return None
-
- def __enter__(self):
- return self
-
- def __exit__(self, *args):
- self.close()
-
-
-# Almost all platforms have select.select()
-if hasattr(select, "select"):
- class SelectSelector(BaseSelector):
- """ Select-based selector. """
- def __init__(self):
- super(SelectSelector, self).__init__()
- self._readers = set()
- self._writers = set()
-
- def register(self, fileobj, events, data=None):
- key = super(SelectSelector, self).register(fileobj, events, data)
- if events & EVENT_READ:
- self._readers.add(key.fd)
- if events & EVENT_WRITE:
- self._writers.add(key.fd)
- return key
-
- def unregister(self, fileobj):
- key = super(SelectSelector, self).unregister(fileobj)
- self._readers.discard(key.fd)
- self._writers.discard(key.fd)
- return key
-
- def _select(self, r, w, timeout=None):
- """ Wrapper for select.select because timeout is a positional arg """
- return select.select(r, w, [], timeout)
-
- def select(self, timeout=None):
- # Selecting on empty lists on Windows errors out.
- if not len(self._readers) and not len(self._writers):
- return []
-
- timeout = None if timeout is None else max(timeout, 0.0)
- ready = []
- r, w, dummy = _syscall_wrapper(self._select, True, self._readers,
- self._writers, timeout=timeout)
- r = set(r)
- w = set(w)
- for fd in r | w:
- events = 0
- if fd in r:
- events |= EVENT_READ
- if fd in w:
- events |= EVENT_WRITE
-
- key = self._key_from_fd(fd)
- if key:
- ready.append((key, events & key.events))
- return ready
-
- __all__.append('SelectSelector')
-
-
-if hasattr(select, "poll"):
- class PollSelector(BaseSelector):
- """ Poll-based selector """
- def __init__(self):
- super(PollSelector, self).__init__()
- self._poll = select.poll()
-
- def register(self, fileobj, events, data=None):
- key = super(PollSelector, self).register(fileobj, events, data)
- event_mask = 0
- if events & EVENT_READ:
- event_mask |= select.POLLIN
- if events & EVENT_WRITE:
- event_mask |= select.POLLOUT
- self._poll.register(key.fd, event_mask)
- return key
-
- def unregister(self, fileobj):
- key = super(PollSelector, self).unregister(fileobj)
- self._poll.unregister(key.fd)
- return key
-
- def _wrap_poll(self, timeout=None):
- """ Wrapper function for select.poll.poll() so that
- _syscall_wrapper can work with only seconds. """
- if timeout is not None:
- if timeout <= 0:
- timeout = 0
- else:
- # select.poll.poll() has a resolution of 1 millisecond,
- # round away from zero to wait *at least* timeout seconds.
- timeout = math.ceil(timeout * 1e3)
-
- result = self._poll.poll(timeout)
- return result
-
- def select(self, timeout=None):
- ready = []
- fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
- for fd, event_mask in fd_events:
- events = 0
- if event_mask & ~select.POLLIN:
- events |= EVENT_WRITE
- if event_mask & ~select.POLLOUT:
- events |= EVENT_READ
-
- key = self._key_from_fd(fd)
- if key:
- ready.append((key, events & key.events))
-
- return ready
-
- __all__.append('PollSelector')
-
-if hasattr(select, "epoll"):
- class EpollSelector(BaseSelector):
- """ Epoll-based selector """
- def __init__(self):
- super(EpollSelector, self).__init__()
- self._epoll = select.epoll()
-
- def fileno(self):
- return self._epoll.fileno()
-
- def register(self, fileobj, events, data=None):
- key = super(EpollSelector, self).register(fileobj, events, data)
- events_mask = 0
- if events & EVENT_READ:
- events_mask |= select.EPOLLIN
- if events & EVENT_WRITE:
- events_mask |= select.EPOLLOUT
- _syscall_wrapper(self._epoll.register, False, key.fd, events_mask)
- return key
-
- def unregister(self, fileobj):
- key = super(EpollSelector, self).unregister(fileobj)
- try:
- _syscall_wrapper(self._epoll.unregister, False, key.fd)
- except SelectorError:
- # This can occur when the fd was closed since registry.
- pass
- return key
-
- def select(self, timeout=None):
- if timeout is not None:
- if timeout <= 0:
- timeout = 0.0
- else:
- # select.epoll.poll() has a resolution of 1 millisecond
- # but luckily takes seconds so we don't need a wrapper
- # like PollSelector. Just for better rounding.
- timeout = math.ceil(timeout * 1e3) * 1e-3
- timeout = float(timeout)
- else:
- timeout = -1.0 # epoll.poll() must have a float.
-
- # We always want at least 1 to ensure that select can be called
- # with no file descriptors registered. Otherwise will fail.
- max_events = max(len(self._fd_to_key), 1)
-
- ready = []
- fd_events = _syscall_wrapper(self._epoll.poll, True,
- timeout=timeout,
- maxevents=max_events)
- for fd, event_mask in fd_events:
- events = 0
- if event_mask & ~select.EPOLLIN:
- events |= EVENT_WRITE
- if event_mask & ~select.EPOLLOUT:
- events |= EVENT_READ
-
- key = self._key_from_fd(fd)
- if key:
- ready.append((key, events & key.events))
- return ready
-
- def close(self):
- self._epoll.close()
- super(EpollSelector, self).close()
-
- __all__.append('EpollSelector')
-
-
-if hasattr(select, "devpoll"):
- class DevpollSelector(BaseSelector):
- """Solaris /dev/poll selector."""
-
- def __init__(self):
- super(DevpollSelector, self).__init__()
- self._devpoll = select.devpoll()
-
- def fileno(self):
- return self._devpoll.fileno()
-
- def register(self, fileobj, events, data=None):
- key = super(DevpollSelector, self).register(fileobj, events, data)
- poll_events = 0
- if events & EVENT_READ:
- poll_events |= select.POLLIN
- if events & EVENT_WRITE:
- poll_events |= select.POLLOUT
- self._devpoll.register(key.fd, poll_events)
- return key
-
- def unregister(self, fileobj):
- key = super(DevpollSelector, self).unregister(fileobj)
- self._devpoll.unregister(key.fd)
- return key
-
- def _wrap_poll(self, timeout=None):
- """ Wrapper function for select.poll.poll() so that
- _syscall_wrapper can work with only seconds. """
- if timeout is not None:
- if timeout <= 0:
- timeout = 0
- else:
- # select.devpoll.poll() has a resolution of 1 millisecond,
- # round away from zero to wait *at least* timeout seconds.
- timeout = math.ceil(timeout * 1e3)
-
- result = self._devpoll.poll(timeout)
- return result
-
- def select(self, timeout=None):
- ready = []
- fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
- for fd, event_mask in fd_events:
- events = 0
- if event_mask & ~select.POLLIN:
- events |= EVENT_WRITE
- if event_mask & ~select.POLLOUT:
- events |= EVENT_READ
-
- key = self._key_from_fd(fd)
- if key:
- ready.append((key, events & key.events))
-
- return ready
-
- def close(self):
- self._devpoll.close()
- super(DevpollSelector, self).close()
-
- __all__.append('DevpollSelector')
-
-
-if hasattr(select, "kqueue"):
- class KqueueSelector(BaseSelector):
- """ Kqueue / Kevent-based selector """
- def __init__(self):
- super(KqueueSelector, self).__init__()
- self._kqueue = select.kqueue()
-
- def fileno(self):
- return self._kqueue.fileno()
-
- def register(self, fileobj, events, data=None):
- key = super(KqueueSelector, self).register(fileobj, events, data)
- if events & EVENT_READ:
- kevent = select.kevent(key.fd,
- select.KQ_FILTER_READ,
- select.KQ_EV_ADD)
-
- _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
-
- if events & EVENT_WRITE:
- kevent = select.kevent(key.fd,
- select.KQ_FILTER_WRITE,
- select.KQ_EV_ADD)
-
- _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
-
- return key
-
- def unregister(self, fileobj):
- key = super(KqueueSelector, self).unregister(fileobj)
- if key.events & EVENT_READ:
- kevent = select.kevent(key.fd,
- select.KQ_FILTER_READ,
- select.KQ_EV_DELETE)
- try:
- _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
- except SelectorError:
- pass
- if key.events & EVENT_WRITE:
- kevent = select.kevent(key.fd,
- select.KQ_FILTER_WRITE,
- select.KQ_EV_DELETE)
- try:
- _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
- except SelectorError:
- pass
-
- return key
-
- def select(self, timeout=None):
- if timeout is not None:
- timeout = max(timeout, 0)
-
- max_events = len(self._fd_to_key) * 2
- ready_fds = {}
-
- kevent_list = _syscall_wrapper(self._wrap_control, True,
- None, max_events, timeout=timeout)
-
- for kevent in kevent_list:
- fd = kevent.ident
- event_mask = kevent.filter
- events = 0
- if event_mask == select.KQ_FILTER_READ:
- events |= EVENT_READ
- if event_mask == select.KQ_FILTER_WRITE:
- events |= EVENT_WRITE
-
- key = self._key_from_fd(fd)
- if key:
- if key.fd not in ready_fds:
- ready_fds[key.fd] = (key, events & key.events)
- else:
- old_events = ready_fds[key.fd][1]
- ready_fds[key.fd] = (key, (events | old_events) & key.events)
-
- return list(ready_fds.values())
-
- def close(self):
- self._kqueue.close()
- super(KqueueSelector, self).close()
-
- def _wrap_control(self, changelist, max_events, timeout):
- return self._kqueue.control(changelist, max_events, timeout)
-
- __all__.append('KqueueSelector')
-
-
-# Choose the best implementation, roughly:
-# kqueue == epoll == devpoll > poll > select.
-# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
-if 'KqueueSelector' in globals(): # Platform-specific: Mac OS and BSD
- DefaultSelector = KqueueSelector
-elif 'DevpollSelector' in globals():
- DefaultSelector = DevpollSelector
-elif 'EpollSelector' in globals(): # Platform-specific: Linux
- DefaultSelector = EpollSelector
-elif 'PollSelector' in globals(): # Platform-specific: Linux
- DefaultSelector = PollSelector
-elif 'SelectSelector' in globals(): # Platform-specific: Windows
- DefaultSelector = SelectSelector
-else: # Platform-specific: AppEngine
- def no_selector(dummy):
- raise ValueError("Platform does not have a selector")
- DefaultSelector = no_selector
- HAS_SELECT = False
diff --git a/lib/ansible/module_utils/compat/selectors.py b/lib/ansible/module_utils/compat/selectors.py
index 1f30929fb1b..81082f3fe36 100644
--- a/lib/ansible/module_utils/compat/selectors.py
+++ b/lib/ansible/module_utils/compat/selectors.py
@@ -15,41 +15,18 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
-
-'''
-Compat selectors library. Python-3.5 has this builtin. The selectors2
-package exists on pypi to backport the functionality as far as python-2.6.
-'''
from __future__ import annotations
-# The following makes it easier for us to script updates of the bundled code
-_BUNDLED_METADATA = {"pypi_name": "selectors2", "version": "1.1.1", "version_constraints": ">1.0,<2.0"}
-
-# Added these bugfix commits from 2.1.0:
-# * https://github.com/SethMichaelLarson/selectors2/commit/3bd74f2033363b606e1e849528ccaa76f5067590
-# Wrap kqueue.control so that timeout is a keyword arg
-# * https://github.com/SethMichaelLarson/selectors2/commit/6f6a26f42086d8aab273b30be492beecb373646b
-# Fix formatting of the kqueue.control patch for pylint
-# * https://github.com/SethMichaelLarson/selectors2/commit/f0c2c6c66cfa7662bc52beaf4e2d65adfa25e189
-# Fix use of OSError exception for py3 and use the wrapper of kqueue.control so retries of
-# interrupted syscalls work with kqueue
-
+import selectors
import sys
-import types # pylint: disable=unused-import
-try:
- # Python 3.4+
- import selectors as _system_selectors
-except ImportError:
- try:
- # backport package installed in the system
- import selectors2 as _system_selectors # type: ignore[no-redef]
- except ImportError:
- _system_selectors = None # type: types.ModuleType | None # type: ignore[no-redef]
+from ansible.module_utils.common.warnings import deprecate
+
-if _system_selectors:
- selectors = _system_selectors
-else:
- # Our bundled copy
- from ansible.module_utils.compat import _selectors2 as selectors # type: ignore[no-redef]
sys.modules['ansible.module_utils.compat.selectors'] = selectors
+
+
+deprecate(
+ msg='The `ansible.module_utils.compat.selectors` module is deprecated.',
+ version='2.19',
+)
diff --git a/lib/ansible/plugins/connection/local.py b/lib/ansible/plugins/connection/local.py
index 44af5829b30..2ba3e692869 100644
--- a/lib/ansible/plugins/connection/local.py
+++ b/lib/ansible/plugins/connection/local.py
@@ -21,13 +21,13 @@ import fcntl
import getpass
import os
import pty
+import selectors
import shutil
import subprocess
import typing as t
import ansible.constants as C
from ansible.errors import AnsibleError, AnsibleFileNotFound
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import text_type, binary_type
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 7d6513aac9b..dc8ffbff142 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -388,6 +388,7 @@ import io
import os
import pty
import re
+import selectors
import shlex
import subprocess
import time
@@ -401,7 +402,6 @@ from ansible.errors import (
AnsibleFileNotFound,
)
from ansible.errors import AnsibleOptionsError
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import PY3, text_type, binary_type
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import BOOLEANS, boolean
diff --git a/test/lib/ansible_test/_internal/commands/sanity/mypy.py b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
index 0296177772c..4d580e933e3 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/mypy.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/mypy.py
@@ -64,7 +64,6 @@ class MypyTest(SanityMultipleVersion):
vendored_paths = (
'lib/ansible/module_utils/six/__init__.py',
'lib/ansible/module_utils/distro/_distro.py',
- 'lib/ansible/module_utils/compat/_selectors2.py',
)
def filter_targets(self, targets: list[TestTarget]) -> list[TestTarget]:
diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
index 41d824b26c0..0c10e68ae0b 100644
--- a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
+++ b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini
@@ -85,9 +85,6 @@ ignore_missing_imports = True
[mypy-distro.*]
ignore_missing_imports = True
-[mypy-selectors2.*]
-ignore_missing_imports = True
-
[mypy-resolvelib.*]
ignore_missing_imports = True
diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
index d6a608f6c7e..fb3da5c26d4 100644
--- a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
+++ b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini
@@ -52,9 +52,6 @@ ignore_missing_imports = True
[mypy-distro.*]
ignore_missing_imports = True
-[mypy-selectors2.*]
-ignore_missing_imports = True
-
[mypy-selinux.*]
ignore_missing_imports = True
diff --git a/test/sanity/code-smell/update-bundled.py b/test/sanity/code-smell/update-bundled.py
index 4bad77a6672..61c837c81ab 100644
--- a/test/sanity/code-smell/update-bundled.py
+++ b/test/sanity/code-smell/update-bundled.py
@@ -49,7 +49,6 @@ def get_bundled_libs(paths):
for filename in fnmatch.filter(paths, 'lib/ansible/compat/*/__init__.py'):
bundled_libs.add(filename)
- bundled_libs.add('lib/ansible/module_utils/compat/selectors.py')
bundled_libs.add('lib/ansible/module_utils/distro/__init__.py')
bundled_libs.add('lib/ansible/module_utils/six/__init__.py')
# backports.ssl_match_hostname should be moved to its own file in the future
diff --git a/test/units/executor/module_common/test_recursive_finder.py b/test/units/executor/module_common/test_recursive_finder.py
index 5058d6da2ff..92d7c206e0b 100644
--- a/test/units/executor/module_common/test_recursive_finder.py
+++ b/test/units/executor/module_common/test_recursive_finder.py
@@ -56,8 +56,6 @@ MODULE_UTILS_BASIC_FILES = frozenset(('ansible/__init__.py',
'ansible/module_utils/common/_utils.py',
'ansible/module_utils/common/arg_spec.py',
'ansible/module_utils/compat/__init__.py',
- 'ansible/module_utils/compat/_selectors2.py',
- 'ansible/module_utils/compat/selectors.py',
'ansible/module_utils/compat/selinux.py',
'ansible/module_utils/distro/__init__.py',
'ansible/module_utils/distro/_distro.py',
diff --git a/test/units/module_utils/basic/test_run_command.py b/test/units/module_utils/basic/test_run_command.py
index ecb68659520..707d5c95e22 100644
--- a/test/units/module_utils/basic/test_run_command.py
+++ b/test/units/module_utils/basic/test_run_command.py
@@ -5,6 +5,7 @@
from __future__ import annotations
import errno
+import selectors
from itertools import product
from io import BytesIO
@@ -12,7 +13,6 @@ import pytest
from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.six import PY2
-from ansible.module_utils.compat import selectors
class OpenBytesIO(BytesIO):
diff --git a/test/units/plugins/connection/test_ssh.py b/test/units/plugins/connection/test_ssh.py
index ff1f128bafe..62d72713e21 100644
--- a/test/units/plugins/connection/test_ssh.py
+++ b/test/units/plugins/connection/test_ssh.py
@@ -19,6 +19,7 @@
from __future__ import annotations
from io import StringIO
+from selectors import SelectorKey, EVENT_READ
import pytest
@@ -26,7 +27,6 @@ from ansible.errors import AnsibleAuthenticationFailure
from units.compat import unittest
from unittest.mock import patch, MagicMock, PropertyMock
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
-from ansible.module_utils.compat.selectors import SelectorKey, EVENT_READ
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.common.text.converters import to_bytes
from ansible.playbook.play_context import PlayContext
@@ -389,7 +389,7 @@ def mock_run_env(request, mocker):
request.cls.mock_popen = mock_popen
request.cls.mock_selector = MockSelector()
- mocker.patch('ansible.module_utils.compat.selectors.DefaultSelector', lambda: request.cls.mock_selector)
+ mocker.patch('selectors.DefaultSelector', lambda: request.cls.mock_selector)
request.cls.mock_openpty = mocker.patch('pty.openpty')