Merge pull request #932 from moreati/release-0.3.3

Release 0.3.3
pull/1003/head v0.3.3
Alex Willmer 2 years ago committed by GitHub
commit 660d3e0885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,7 +7,6 @@ import signal
import sys import sys
import ci_lib import ci_lib
from ci_lib import run
TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible') TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible')
@ -40,10 +39,10 @@ with ci_lib.Fold('job_setup'):
os.chdir(TESTS_DIR) os.chdir(TESTS_DIR)
os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7)) os.chmod('../data/docker/mitogen__has_sudo_pubkey.key', int('0600', 7))
run("mkdir %s", HOSTS_DIR) ci_lib.run("mkdir %s", HOSTS_DIR)
for path in glob.glob(TESTS_DIR + '/hosts/*'): for path in glob.glob(TESTS_DIR + '/hosts/*'):
if not path.endswith('default.hosts'): if not path.endswith('default.hosts'):
run("ln -s %s %s", path, HOSTS_DIR) ci_lib.run("ln -s %s %s", path, HOSTS_DIR)
inventory_path = os.path.join(HOSTS_DIR, 'target') inventory_path = os.path.join(HOSTS_DIR, 'target')
with open(inventory_path, 'w') as fp: with open(inventory_path, 'w') as fp:
@ -63,14 +62,14 @@ with ci_lib.Fold('job_setup'):
ci_lib.dump_file(inventory_path) ci_lib.dump_file(inventory_path)
if not ci_lib.exists_in_path('sshpass'): if not ci_lib.exists_in_path('sshpass'):
run("sudo apt-get update") ci_lib.run("sudo apt-get update")
run("sudo apt-get install -y sshpass") ci_lib.run("sudo apt-get install -y sshpass")
with ci_lib.Fold('ansible'): with ci_lib.Fold('ansible'):
playbook = os.environ.get('PLAYBOOK', 'all.yml') playbook = os.environ.get('PLAYBOOK', 'all.yml')
try: try:
run('./run_ansible_playbook.py %s -i "%s" %s', ci_lib.run('./run_ansible_playbook.py %s -i "%s" %s',
playbook, HOSTS_DIR, ' '.join(sys.argv[1:])) playbook, HOSTS_DIR, ' '.join(sys.argv[1:]))
except: except:
pause_if_interactive() pause_if_interactive()

@ -25,9 +25,9 @@ jobs:
Mito_36: Mito_36:
python.version: '3.6' python.version: '3.6'
tox.env: py36-mode_mitogen tox.env: py36-mode_mitogen
Mito_39: Mito_310:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen tox.env: py310-mode_mitogen
# TODO: test python3, python3 tests are broken # TODO: test python3, python3 tests are broken
Loc_27_210: Loc_27_210:
@ -72,9 +72,9 @@ jobs:
Mito_37: Mito_37:
python.version: '3.7' python.version: '3.7'
tox.env: py37-mode_mitogen tox.env: py37-mode_mitogen
Mito_39: Mito_310:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen tox.env: py310-mode_mitogen
# TODO: test python3, python3 tests are broken # TODO: test python3, python3 tests are broken
Loc_27_210: Loc_27_210:
@ -162,33 +162,33 @@ jobs:
python.version: '3.6' python.version: '3.6'
tox.env: py36-mode_mitogen-distro_ubuntu2004 tox.env: py36-mode_mitogen-distro_ubuntu2004
Mito_39_centos6: Mito_310_centos6:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_centos6 tox.env: py310-mode_mitogen-distro_centos6
Mito_39_centos7: Mito_310_centos7:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_centos7 tox.env: py310-mode_mitogen-distro_centos7
Mito_39_centos8: Mito_310_centos8:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_centos8 tox.env: py310-mode_mitogen-distro_centos8
Mito_39_debian9: Mito_310_debian9:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_debian9 tox.env: py310-mode_mitogen-distro_debian9
Mito_39_debian10: Mito_310_debian10:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_debian10 tox.env: py310-mode_mitogen-distro_debian10
Mito_39_debian11: Mito_310_debian11:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_debian11 tox.env: py310-mode_mitogen-distro_debian11
Mito_39_ubuntu1604: Mito_310_ubuntu1604:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_ubuntu1604 tox.env: py310-mode_mitogen-distro_ubuntu1604
Mito_39_ubuntu1804: Mito_310_ubuntu1804:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_ubuntu1804 tox.env: py310-mode_mitogen-distro_ubuntu1804
Mito_39_ubuntu2004: Mito_310_ubuntu2004:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_mitogen-distro_ubuntu2004 tox.env: py310-mode_mitogen-distro_ubuntu2004
#DebOps_2460_27_27: #DebOps_2460_27_27:
#python.version: '2.7' #python.version: '2.7'
@ -247,15 +247,15 @@ jobs:
python.version: '3.6' python.version: '3.6'
tox.env: py36-mode_ansible-ansible4 tox.env: py36-mode_ansible-ansible4
Ans_39_210: Ans_310_210:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_ansible-ansible2.10 tox.env: py310-mode_ansible-ansible2.10
Ans_39_3: Ans_310_3:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_ansible-ansible3 tox.env: py310-mode_ansible-ansible3
Ans_39_4: Ans_310_4:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_ansible-ansible4 tox.env: py310-mode_ansible-ansible4
Ans_39_5: Ans_310_5:
python.version: '3.9' python.version: '3.10'
tox.env: py39-mode_ansible-ansible5 tox.env: py310-mode_ansible-ansible5

@ -1,4 +1,3 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
@ -193,8 +192,6 @@ class Fold(object):
def __exit__(self, _1, _2, _3): pass def __exit__(self, _1, _2, _3): pass
os.environ.setdefault('ANSIBLE_STRATEGY',
os.environ.get('STRATEGY', 'mitogen_linear'))
GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) GIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
# Used only when MODE=mitogen # Used only when MODE=mitogen
DISTRO = os.environ.get('DISTRO', 'debian9') DISTRO = os.environ.get('DISTRO', 'debian9')

@ -7,7 +7,7 @@ ci_lib.DISTROS = ['debian']
ci_lib.run_batches([ ci_lib.run_batches([
[ [
'pip install -qqq "debops[ansible]==2.1.2"', 'python -m pip --no-python-version-warning --disable-pip-version-check "debops[ansible]==2.1.2"',
], ],
[ [
'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws', 'aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws',

@ -2,10 +2,10 @@
# Run tests/ansible/all.yml under Ansible and Ansible-Mitogen # Run tests/ansible/all.yml under Ansible and Ansible-Mitogen
import os import os
import subprocess
import sys import sys
import ci_lib import ci_lib
from ci_lib import run
TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible') TESTS_DIR = os.path.join(ci_lib.GIT_ROOT, 'tests/ansible')
@ -24,37 +24,38 @@ with ci_lib.Fold('job_setup'):
# NOTE: sshpass v1.06 causes errors so pegging to 1.05 -> "msg": "Error when changing password","out": "passwd: DS error: eDSAuthFailed\n", # NOTE: sshpass v1.06 causes errors so pegging to 1.05 -> "msg": "Error when changing password","out": "passwd: DS error: eDSAuthFailed\n",
# there's a checksum error with "brew install http://git.io/sshpass.rb" though, so installing manually # there's a checksum error with "brew install http://git.io/sshpass.rb" though, so installing manually
if not ci_lib.exists_in_path('sshpass'): if not ci_lib.exists_in_path('sshpass'):
os.system("curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz && \ subprocess.check_call(
"curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.05/sshpass-1.05.tar.gz && \
tar xvf sshpass-1.05.tar.gz && \ tar xvf sshpass-1.05.tar.gz && \
cd sshpass-1.05 && \ cd sshpass-1.05 && \
./configure && \ ./configure && \
sudo make install") sudo make install",
shell=True,
)
with ci_lib.Fold('machine_prep'): with ci_lib.Fold('machine_prep'):
# generate a new ssh key for localhost ssh # generate a new ssh key for localhost ssh
if not os.path.exists(os.path.expanduser("~/.ssh/id_rsa")): if not os.path.exists(os.path.expanduser("~/.ssh/id_rsa")):
os.system("ssh-keygen -P '' -m pem -f ~/.ssh/id_rsa") subprocess.check_call("ssh-keygen -P '' -m pem -f ~/.ssh/id_rsa", shell=True)
os.system("cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys") subprocess.check_call("cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys", shell=True)
# also generate it for the sudo user
if os.system("sudo [ -f /var/root/.ssh/id_rsa ]") != 0:
os.system("sudo ssh-keygen -P '' -m pem -f /var/root/.ssh/id_rsa")
os.system("sudo cat /var/root/.ssh/id_rsa.pub | sudo tee -a /var/root/.ssh/authorized_keys")
os.chmod(os.path.expanduser('~/.ssh'), int('0700', 8)) os.chmod(os.path.expanduser('~/.ssh'), int('0700', 8))
os.chmod(os.path.expanduser('~/.ssh/authorized_keys'), int('0600', 8)) os.chmod(os.path.expanduser('~/.ssh/authorized_keys'), int('0600', 8))
# run chmod through sudo since it's owned by root
os.system('sudo chmod 700 /var/root/.ssh') # also generate it for the sudo user
os.system('sudo chmod 600 /var/root/.ssh/authorized_keys') if os.system("sudo [ -f ~root/.ssh/id_rsa ]") != 0:
subprocess.check_call("sudo ssh-keygen -P '' -m pem -f ~root/.ssh/id_rsa", shell=True)
subprocess.check_call("sudo cat ~root/.ssh/id_rsa.pub | sudo tee -a ~root/.ssh/authorized_keys", shell=True)
subprocess.check_call('sudo chmod 700 ~root/.ssh', shell=True)
subprocess.check_call('sudo chmod 600 ~root/.ssh/authorized_keys', shell=True)
if os.path.expanduser('~mitogen__user1') == '~mitogen__user1': if os.path.expanduser('~mitogen__user1') == '~mitogen__user1':
os.chdir(IMAGE_PREP_DIR) os.chdir(IMAGE_PREP_DIR)
run("ansible-playbook -c local -i localhost, _user_accounts.yml") ci_lib.run("ansible-playbook -c local -i localhost, _user_accounts.yml")
with ci_lib.Fold('ansible'): with ci_lib.Fold('ansible'):
os.chdir(TESTS_DIR) os.chdir(TESTS_DIR)
playbook = os.environ.get('PLAYBOOK', 'all.yml') playbook = os.environ.get('PLAYBOOK', 'all.yml')
run('./run_ansible_playbook.py %s -l target %s', ci_lib.run('./run_ansible_playbook.py %s -l target %s',
playbook, ' '.join(sys.argv[1:])) playbook, ' '.join(sys.argv[1:]))

@ -73,7 +73,9 @@ necessarily involves preventing the scheduler from making load balancing
decisions. decisions.
""" """
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import ctypes import ctypes
import logging import logging
import mmap import mmap

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals from __future__ import unicode_literals
__metaclass__ = type
import errno import errno
import logging import logging
@ -40,10 +41,8 @@ import time
import ansible.constants as C import ansible.constants as C
import ansible.errors import ansible.errors
import ansible.plugins.connection import ansible.plugins.connection
import ansible.utils.shlex
import mitogen.core import mitogen.core
import mitogen.fork
import mitogen.utils import mitogen.utils
import ansible_mitogen.mixins import ansible_mitogen.mixins
@ -262,6 +261,21 @@ def _connect_machinectl(spec):
return _connect_setns(spec, kind='machinectl') return _connect_setns(spec, kind='machinectl')
def _connect_podman(spec):
"""
Return ContextService arguments for a Docker connection.
"""
return {
'method': 'podman',
'kwargs': {
'username': spec.remote_user(),
'container': spec.remote_addr(),
'python_path': spec.python_path(rediscover_python=True),
'connect_timeout': spec.ansible_ssh_timeout() or spec.timeout(),
'remote_name': get_remote_name(spec),
}
}
def _connect_setns(spec, kind=None): def _connect_setns(spec, kind=None):
""" """
Return ContextService arguments for a mitogen_setns connection. Return ContextService arguments for a mitogen_setns connection.
@ -400,6 +414,7 @@ CONNECTION_METHOD = {
'lxc': _connect_lxc, 'lxc': _connect_lxc,
'lxd': _connect_lxd, 'lxd': _connect_lxd,
'machinectl': _connect_machinectl, 'machinectl': _connect_machinectl,
'podman': _connect_podman,
'setns': _connect_setns, 'setns': _connect_setns,
'ssh': _connect_ssh, 'ssh': _connect_ssh,
'smart': _connect_ssh, # issue #548. 'smart': _connect_ssh, # issue #548.
@ -1081,7 +1096,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
s = fp.read(self.SMALL_FILE_LIMIT + 1) s = fp.read(self.SMALL_FILE_LIMIT + 1)
finally: finally:
fp.close() fp.close()
except OSError: except OSError as e:
self._throw_io_error(e, in_path) self._throw_io_error(e, in_path)
raise raise

@ -30,7 +30,10 @@
Stable names for PluginLoader instances across Ansible versions. Stable names for PluginLoader instances across Ansible versions.
""" """
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import ansible.errors
import ansible_mitogen.utils import ansible_mitogen.utils

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import logging import logging
import os import os
@ -36,8 +38,8 @@ import mitogen.utils
try: try:
from __main__ import display from __main__ import display
except ImportError: except ImportError:
from ansible.utils.display import Display import ansible.utils.display
display = Display() display = ansible.utils.display.Display()
#: The process name set via :func:`set_process_name`. #: The process name set via :func:`set_process_name`.

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import logging import logging
import os import os
import pwd import pwd

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals from __future__ import unicode_literals
__metaclass__ = type
import collections import collections
import imp import imp

@ -26,8 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals from __future__ import unicode_literals
__metaclass__ = type
import mitogen.core import mitogen.core

@ -34,8 +34,9 @@ files/modules known missing.
[0] "Ansible Module Architecture", developing_program_flow_modules.html [0] "Ansible Module Architecture", developing_program_flow_modules.html
""" """
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals from __future__ import unicode_literals
__metaclass__ = type
import json import json
import logging import logging
@ -43,11 +44,10 @@ import os
import random import random
import re import re
from ansible.executor import module_common import ansible.collections.list
from ansible.collections.list import list_collection_dirs
import ansible.errors import ansible.errors
import ansible.module_utils import ansible.executor.module_common
import ansible.release
import mitogen.core import mitogen.core
import mitogen.select import mitogen.select
@ -192,7 +192,7 @@ class BinaryPlanner(Planner):
@classmethod @classmethod
def detect(cls, path, source): def detect(cls, path, source):
return module_common._is_binary(source) return ansible.executor.module_common._is_binary(source)
def get_push_files(self): def get_push_files(self):
return [mitogen.core.to_text(self._inv.module_path)] return [mitogen.core.to_text(self._inv.module_path)]
@ -269,7 +269,7 @@ class JsonArgsPlanner(ScriptPlanner):
@classmethod @classmethod
def detect(cls, path, source): def detect(cls, path, source):
return module_common.REPLACER_JSONARGS in source return ansible.executor.module_common.REPLACER_JSONARGS in source
class WantJsonPlanner(ScriptPlanner): class WantJsonPlanner(ScriptPlanner):
@ -298,11 +298,11 @@ class NewStylePlanner(ScriptPlanner):
preprocessing the module. preprocessing the module.
""" """
runner_name = 'NewStyleRunner' runner_name = 'NewStyleRunner'
MARKER = re.compile(b'from ansible(?:_collections|\.module_utils)\.') MARKER = re.compile(br'from ansible(?:_collections|\.module_utils)\.')
@classmethod @classmethod
def detect(cls, path, source): def detect(cls, path, source):
return cls.MARKER.search(source) != None return cls.MARKER.search(source) is not None
def _get_interpreter(self): def _get_interpreter(self):
return None, None return None, None
@ -362,7 +362,7 @@ class NewStylePlanner(ScriptPlanner):
module_name='ansible_module_%s' % (self._inv.module_name,), module_name='ansible_module_%s' % (self._inv.module_name,),
module_path=self._inv.module_path, module_path=self._inv.module_path,
search_path=self.get_search_path(), search_path=self.get_search_path(),
builtin_path=module_common._MODULE_UTILS_PATH, builtin_path=ansible.executor.module_common._MODULE_UTILS_PATH,
context=self._inv.connection.context, context=self._inv.connection.context,
) )
return self._module_map return self._module_map
@ -405,7 +405,7 @@ class ReplacerPlanner(NewStylePlanner):
@classmethod @classmethod
def detect(cls, path, source): def detect(cls, path, source):
return module_common.REPLACER in source return ansible.executor.module_common.REPLACER in source
class OldStylePlanner(ScriptPlanner): class OldStylePlanner(ScriptPlanner):
@ -427,12 +427,6 @@ _planners = [
] ]
try:
_get_ansible_module_fqn = module_common._get_ansible_module_fqn
except AttributeError:
_get_ansible_module_fqn = None
def py_modname_from_path(name, path): def py_modname_from_path(name, path):
""" """
Fetch the logical name of a new-style module as it might appear in Fetch the logical name of a new-style module as it might appear in
@ -442,9 +436,10 @@ def py_modname_from_path(name, path):
package hierarchy approximated on the target, enabling relative imports package hierarchy approximated on the target, enabling relative imports
to function correctly. For example, "ansible.modules.system.setup". to function correctly. For example, "ansible.modules.system.setup".
""" """
if _get_ansible_module_fqn:
try: try:
return _get_ansible_module_fqn(path) return ansible.executor.module_common._get_ansible_module_fqn(path)
except AttributeError:
pass
except ValueError: except ValueError:
pass pass
@ -528,12 +523,15 @@ def _invoke_isolated_task(invocation, planner):
context.shutdown() context.shutdown()
def _get_planner(name, path, source): def _get_planner(invocation, source):
for klass in _planners: for klass in _planners:
if klass.detect(path, source): if klass.detect(invocation.module_path, source):
LOG.debug('%r accepted %r (filename %r)', klass, name, path) LOG.debug(
'%r accepted %r (filename %r)',
klass, invocation.module_name, invocation.module_path,
)
return klass return klass
LOG.debug('%r rejected %r', klass, name) LOG.debug('%r rejected %r', klass, invocation.module_name)
raise ansible.errors.AnsibleError(NO_METHOD_MSG + repr(invocation)) raise ansible.errors.AnsibleError(NO_METHOD_MSG + repr(invocation))
@ -564,7 +562,7 @@ def _load_collections(invocation):
Goes through all collection path possibilities and stores paths to installed collections Goes through all collection path possibilities and stores paths to installed collections
Stores them on the current invocation to later be passed to the master service Stores them on the current invocation to later be passed to the master service
""" """
for collection_path in list_collection_dirs(): for collection_path in ansible.collections.list.list_collection_dirs():
invocation._extra_sys_paths.add(collection_path.decode('utf-8')) invocation._extra_sys_paths.add(collection_path.decode('utf-8'))
@ -596,8 +594,7 @@ def invoke(invocation):
module_source = invocation.get_module_source() module_source = invocation.get_module_source()
_fix_py35(invocation, module_source) _fix_py35(invocation, module_source)
_planner_by_path[invocation.module_path] = _get_planner( _planner_by_path[invocation.module_path] = _get_planner(
invocation.module_name, invocation,
invocation.module_path,
module_source module_source
) )

@ -18,23 +18,17 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import os import os
import base64
from ansible.module_utils._text import to_bytes from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils.parsing.convert_bool import boolean from ansible.module_utils.parsing.convert_bool import boolean
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.utils.hashing import checksum, md5, secure_hash from ansible.utils.display import Display
from ansible.utils.path import makedirs_safe from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash
from ansible.utils.path import makedirs_safe, is_subpath
REMOTE_CHECKSUM_ERRORS = { display = Display()
'0': "unable to calculate the checksum of the remote file",
'1': "the remote file does not exist",
'2': "no read permission on remote file",
'3': "remote file is a directory, fetch cannot work on directories",
'4': "python isn't present on the system. Unable to compute checksum",
'5': "stdlib json was not found on the remote machine. Only the raw module can work without those installed",
}
class ActionModule(ActionBase): class ActionModule(ActionBase):
@ -45,36 +39,94 @@ class ActionModule(ActionBase):
task_vars = dict() task_vars = dict()
result = super(ActionModule, self).run(tmp, task_vars) result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect
try: try:
if self._play_context.check_mode: if self._play_context.check_mode:
result['skipped'] = True raise AnsibleActionSkip('check mode not (yet) supported for this module')
result['msg'] = 'check mode not (yet) supported for this module'
return result
source = self._task.args.get('src', None)
original_dest = dest = self._task.args.get('dest', None)
flat = boolean(self._task.args.get('flat'), strict=False) flat = boolean(self._task.args.get('flat'), strict=False)
fail_on_missing = boolean(self._task.args.get('fail_on_missing', True), strict=False) fail_on_missing = boolean(self._task.args.get('fail_on_missing', True), strict=False)
validate_checksum = boolean(self._task.args.get('validate_checksum', True), strict=False) validate_checksum = boolean(self._task.args.get('validate_checksum', True), strict=False)
msg = ''
# validate source and dest are strings FIXME: use basic.py and module specs # validate source and dest are strings FIXME: use basic.py and module specs
source = self._task.args.get('src')
if not isinstance(source, string_types): if not isinstance(source, string_types):
result['msg'] = "Invalid type supplied for source option, it must be a string" msg = "Invalid type supplied for source option, it must be a string"
dest = self._task.args.get('dest')
if not isinstance(dest, string_types): if not isinstance(dest, string_types):
result['msg'] = "Invalid type supplied for dest option, it must be a string" msg = "Invalid type supplied for dest option, it must be a string"
if result.get('msg'): if source is None or dest is None:
result['failed'] = True msg = "src and dest are required"
return result
if msg:
raise AnsibleActionFail(msg)
source = self._connection._shell.join_path(source) source = self._connection._shell.join_path(source)
source = self._remote_expand_user(source) source = self._remote_expand_user(source)
# calculate checksum for the remote file, don't bother if using remote_stat = {}
# become as slurp will be used Force remote_checksum to follow remote_checksum = None
# symlinks because fetch always follows symlinks if True:
remote_checksum = self._remote_checksum(source, all_vars=task_vars, follow=True) # Get checksum for the remote file even using become. Mitogen doesn't need slurp.
# Follow symlinks because fetch always follows symlinks
try:
remote_stat = self._execute_remote_stat(source, all_vars=task_vars, follow=True)
except AnsibleError as ae:
result['changed'] = False
result['file'] = source
if fail_on_missing:
result['failed'] = True
result['msg'] = to_text(ae)
else:
result['msg'] = "%s, ignored" % to_text(ae, errors='surrogate_or_replace')
return result
remote_checksum = remote_stat.get('checksum')
if remote_stat.get('exists'):
if remote_stat.get('isdir'):
result['failed'] = True
result['changed'] = False
result['msg'] = "remote file is a directory, fetch cannot work on directories"
# Historically, these don't fail because you may want to transfer
# a log file that possibly MAY exist but keep going to fetch other
# log files. Today, this is better achieved by adding
# ignore_errors or failed_when to the task. Control the behaviour
# via fail_when_missing
if not fail_on_missing:
result['msg'] += ", not transferring, ignored"
del result['changed']
del result['failed']
return result
# use slurp if permissions are lacking or privilege escalation is needed
remote_data = None
if remote_checksum in (None, '1', ''):
slurpres = self._execute_module(module_name='ansible.legacy.slurp', module_args=dict(src=source), task_vars=task_vars)
if slurpres.get('failed'):
if not fail_on_missing:
result['file'] = source
result['changed'] = False
else:
result.update(slurpres)
if 'not found' in slurpres.get('msg', ''):
result['msg'] = "the remote file does not exist, not transferring, ignored"
elif slurpres.get('msg', '').startswith('source is a directory'):
result['msg'] = "remote file is a directory, fetch cannot work on directories"
return result
else:
if slurpres['encoding'] == 'base64':
remote_data = base64.b64decode(slurpres['content'])
if remote_data is not None:
remote_checksum = checksum_s(remote_data)
# calculate the destination name # calculate the destination name
if os.path.sep not in self._connection._shell.join_path('a', ''): if os.path.sep not in self._connection._shell.join_path('a', ''):
@ -83,13 +135,14 @@ class ActionModule(ActionBase):
else: else:
source_local = source source_local = source
dest = os.path.expanduser(dest) # ensure we only use file name, avoid relative paths
if not is_subpath(dest, original_dest):
# TODO: ? dest = os.path.expanduser(dest.replace(('../','')))
raise AnsibleActionFail("Detected directory traversal, expected to be contained in '%s' but got '%s'" % (original_dest, dest))
if flat: if flat:
if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')) and not dest.endswith(os.sep): if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')) and not dest.endswith(os.sep):
result['msg'] = "dest is an existing directory, use a trailing slash if you want to fetch src into that directory" raise AnsibleActionFail("dest is an existing directory, use a trailing slash if you want to fetch src into that directory")
result['file'] = dest
result['failed'] = True
return result
if dest.endswith(os.sep): if dest.endswith(os.sep):
# if the path ends with "/", we'll use the source filename as the # if the path ends with "/", we'll use the source filename as the
# destination filename # destination filename
@ -106,23 +159,7 @@ class ActionModule(ActionBase):
target_name = self._play_context.remote_addr target_name = self._play_context.remote_addr
dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local)
dest = dest.replace("//", "/") dest = os.path.normpath(dest)
if remote_checksum in REMOTE_CHECKSUM_ERRORS:
result['changed'] = False
result['file'] = source
result['msg'] = REMOTE_CHECKSUM_ERRORS[remote_checksum]
# Historically, these don't fail because you may want to transfer
# a log file that possibly MAY exist but keep going to fetch other
# log files. Today, this is better achieved by adding
# ignore_errors or failed_when to the task. Control the behaviour
# via fail_when_missing
if fail_on_missing:
result['failed'] = True
del result['changed']
else:
result['msg'] += ", not transferring, ignored"
return result
# calculate checksum for the local file # calculate checksum for the local file
local_checksum = checksum(dest) local_checksum = checksum(dest)
@ -132,7 +169,15 @@ class ActionModule(ActionBase):
makedirs_safe(os.path.dirname(dest)) makedirs_safe(os.path.dirname(dest))
# fetch the file and check for changes # fetch the file and check for changes
if remote_data is None:
self._connection.fetch_file(source, dest) self._connection.fetch_file(source, dest)
else:
try:
f = open(to_bytes(dest, errors='surrogate_or_strict'), 'wb')
f.write(remote_data)
f.close()
except (IOError, OSError) as e:
raise AnsibleActionFail("Failed to fetch the file: %s" % e)
new_checksum = secure_hash(dest) new_checksum = secure_hash(dest)
# For backwards compatibility. We'll return None on FIPS enabled systems # For backwards compatibility. We'll return None on FIPS enabled systems
try: try:
@ -157,10 +202,6 @@ class ActionModule(ActionBase):
result.update(dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum)) result.update(dict(changed=False, md5sum=local_md5, file=source, dest=dest, checksum=local_checksum))
finally: finally:
try:
self._remove_tmp_path(self._connection._shell.tmpdir) self._remove_tmp_path(self._connection._shell.tmpdir)
except AttributeError:
# .tmpdir was added to ShellModule in v2.6.0, so old versions don't have it
pass
return result return result

@ -26,14 +26,15 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import unicode_literals
""" """
Fetch the connection configuration stack that would be used to connect to a Fetch the connection configuration stack that would be used to connect to a
target, without actually connecting to it. target, without actually connecting to it.
""" """
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import ansible_mitogen.connection import ansible_mitogen.connection
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -27,12 +27,13 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys
from ansible.errors import AnsibleConnectionFailure import ansible.errors
from ansible.module_utils.six import iteritems
try: try:
import ansible_mitogen import ansible_mitogen
@ -61,7 +62,7 @@ class Connection(ansible_mitogen.connection.Connection):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if not _get_result: if not _get_result:
raise AnsibleConnectionFailure(self.not_supported_msg) raise ansible.errors.AnsibleConnectionFailure(self.not_supported_msg)
super(Connection, self).__init__(*args, **kwargs) super(Connection, self).__init__(*args, **kwargs)
def get_extra_args(self): def get_extra_args(self):
@ -72,8 +73,10 @@ class Connection(ansible_mitogen.connection.Connection):
# Ansible >= 2.10, _get_result is a get_with_context_result # Ansible >= 2.10, _get_result is a get_with_context_result
connection_options = _get_result.object.connection_options connection_options = _get_result.object.connection_options
parameters = [] parameters = []
for key, option in iteritems(connection_options): for key in connection_options:
if self.get_task_var('ansible_' + key) is not None: task_var_name = 'ansible_%s' % key
parameters += [ option, self.get_task_var('ansible_' + key) ] task_var = self.get_task_var(task_var_name)
if task_var is not None:
parameters += [connection_options[key], task_var]
return parameters return parameters

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -0,0 +1,46 @@
# Copyright 2022, Mitogen contributers
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
import sys
try:
import ansible_mitogen
except ImportError:
base_dir = os.path.dirname(__file__)
sys.path.insert(0, os.path.abspath(os.path.join(base_dir, '../../..')))
del base_dir
import ansible_mitogen.connection
class Connection(ansible_mitogen.connection.Connection):
transport = 'podman'

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path import os.path
import sys import sys

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import atexit import atexit
import logging import logging
import multiprocessing import multiprocessing

@ -36,6 +36,9 @@ Each class in here has a corresponding Planner class in planners.py that knows
how to build arguments for it, preseed related data, etc. how to build arguments for it, preseed related data, etc.
""" """
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import atexit import atexit
import imp import imp
import os import os

@ -39,18 +39,18 @@ connections, grant access to files by children, and register for notification
when a child has completed a job. when a child has completed a job.
""" """
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals from __future__ import unicode_literals
__metaclass__ = type
import logging import logging
import os import os
import os.path
import sys import sys
import threading import threading
import ansible.constants import ansible.constants
import mitogen import mitogen.core
import mitogen.service import mitogen.service
import mitogen.utils import mitogen.utils
import ansible_mitogen.loaders import ansible_mitogen.loaders

@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os import os
import signal import signal
import threading import threading
@ -43,7 +45,7 @@ import ansible_mitogen.mixins
import ansible_mitogen.process import ansible_mitogen.process
import ansible.executor.process.worker import ansible.executor.process.worker
from ansible.utils.sentinel import Sentinel import ansible.utils.sentinel
def _patch_awx_callback(): def _patch_awx_callback():
@ -54,12 +56,11 @@ def _patch_awx_callback():
# AWX uses sitecustomize.py to force-load this package. If it exists, we're # AWX uses sitecustomize.py to force-load this package. If it exists, we're
# running under AWX. # running under AWX.
try: try:
from awx_display_callback.events import EventContext import awx_display_callback.events
from awx_display_callback.events import event_context
except ImportError: except ImportError:
return return
if hasattr(EventContext(), '_local'): if hasattr(awx_display_callback.events.EventContext(), '_local'):
# Patched version. # Patched version.
return return
@ -68,8 +69,8 @@ def _patch_awx_callback():
ctx = tls.setdefault('_ctx', {}) ctx = tls.setdefault('_ctx', {})
ctx.update(kwargs) ctx.update(kwargs)
EventContext._local = threading.local() awx_display_callback.events.EventContext._local = threading.local()
EventContext.add_local = patch_add_local awx_display_callback.events.EventContext.add_local = patch_add_local
_patch_awx_callback() _patch_awx_callback()
@ -107,6 +108,7 @@ REDIRECTED_CONNECTION_PLUGINS = (
'lxc', 'lxc',
'lxd', 'lxd',
'machinectl', 'machinectl',
'podman',
'setns', 'setns',
'ssh', 'ssh',
) )
@ -278,7 +280,7 @@ class StrategyMixin(object):
name=task.action, name=task.action,
class_only=True, class_only=True,
) )
if play_context.connection is not Sentinel: if play_context.connection is not ansible.utils.sentinel.Sentinel:
# 2.8 appears to defer computing this until inside the worker. # 2.8 appears to defer computing this until inside the worker.
# TODO: figure out where it has moved. # TODO: figure out where it has moved.
ansible_mitogen.loaders.connection_loader.get( ansible_mitogen.loaders.connection_loader.get(

@ -33,6 +33,9 @@ Helper functions intended to be executed on the target. These are entrypoints
for file transfer, module execution and sundry bits like changing file modes. for file transfer, module execution and sundry bits like changing file modes.
""" """
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import errno import errno
import grp import grp
import operator import operator
@ -51,7 +54,6 @@ import types
logging = __import__('logging') logging = __import__('logging')
import mitogen.core import mitogen.core
import mitogen.fork
import mitogen.parent import mitogen.parent
import mitogen.service import mitogen.service
from mitogen.core import b from mitogen.core import b
@ -652,7 +654,8 @@ def read_path(path):
""" """
Fetch the contents of a filesystem `path` as bytes. Fetch the contents of a filesystem `path` as bytes.
""" """
return open(path, 'rb').read() with open(path, 'rb') as f:
return f.read()
def set_file_owner(path, owner, group=None, fd=None): def set_file_owner(path, owner, group=None, fd=None):

@ -26,9 +26,6 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import unicode_literals
""" """
Mitogen extends Ansible's target configuration mechanism in several ways that Mitogen extends Ansible's target configuration mechanism in several ways that
require some care: require some care:
@ -60,6 +57,10 @@ information from PlayContext, and another that takes (almost) all information
from HostVars. from HostVars.
""" """
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__metaclass__ = type
import abc import abc
import os import os
import ansible.utils.shlex import ansible.utils.shlex
@ -354,6 +355,12 @@ class Spec(with_metaclass(abc.ABCMeta, object)):
The path to the "machinectl" program for the 'setns' transport. The path to the "machinectl" program for the 'setns' transport.
""" """
@abc.abstractmethod
def mitogen_podman_path(self):
"""
The path to the "podman" program for the 'podman' transport.
"""
@abc.abstractmethod @abc.abstractmethod
def mitogen_ssh_keepalive_interval(self): def mitogen_ssh_keepalive_interval(self):
""" """
@ -527,6 +534,9 @@ class PlayContextSpec(Spec):
def mitogen_lxc_info_path(self): def mitogen_lxc_info_path(self):
return self._connection.get_task_var('mitogen_lxc_info_path') return self._connection.get_task_var('mitogen_lxc_info_path')
def mitogen_podman_path(self):
return self._connection.get_task_var('mitogen_podman_path')
def mitogen_ssh_keepalive_interval(self): def mitogen_ssh_keepalive_interval(self):
return self._connection.get_task_var('mitogen_ssh_keepalive_interval') return self._connection.get_task_var('mitogen_ssh_keepalive_interval')
@ -747,6 +757,9 @@ class MitogenViaSpec(Spec):
def mitogen_lxc_info_path(self): def mitogen_lxc_info_path(self):
return self._host_vars.get('mitogen_lxc_info_path') return self._host_vars.get('mitogen_lxc_info_path')
def mitogen_podman_path(self):
return self._host_vars.get('mitogen_podman_path')
def mitogen_ssh_keepalive_interval(self): def mitogen_ssh_keepalive_interval(self):
return self._host_vars.get('mitogen_ssh_keepalive_interval') return self._host_vars.get('mitogen_ssh_keepalive_interval')

@ -1,4 +1,5 @@
from __future__ import absolute_import from __future__ import absolute_import, division, print_function
__metaclass__ = type
import distutils.version import distutils.version

@ -147,8 +147,8 @@ Noteworthy Differences
* Mitogen 0.2.x supports Ansible 2.3-2.9; with Python 2.6, 2.7, or 3.6. * Mitogen 0.2.x supports Ansible 2.3-2.9; with Python 2.6, 2.7, or 3.6.
Mitogen 0.3.1+ supports Mitogen 0.3.1+ supports
- Ansible 2.10, 3, and 4; with Python 2.7, or 3.6-3.9 - Ansible 2.10, 3, and 4; with Python 2.7, or 3.6-3.10
- Ansible 5; with Python 3.8-3.9 - Ansible 5; with Python 3.8-3.10
Verify your installation is running one of these versions by checking Verify your installation is running one of these versions by checking
``ansible --version`` output. ``ansible --version`` output.
@ -188,9 +188,9 @@ Noteworthy Differences
your_ssh_username = (ALL) NOPASSWD:/usr/bin/python -c* your_ssh_username = (ALL) NOPASSWD:/usr/bin/python -c*
* The :ans:conn:`~buildah`, :ans:conn:`~docker`, :ans:conn:`~jail`, * The :ans:conn:`~buildah`, :ans:conn:`~docker`, :ans:conn:`~jail`,
:ans:conn:`~kubectl`, :ans:conn:`~local`, :ans:conn:`~lxd`, and :ans:conn:`~kubectl`, :ans:conn:`~local`, :ans:conn:`~lxd`,
:ans:conn:`~ssh` built-in connection types are supported, along with :ans:conn:`~podman`, & :ans:conn:`~ssh` connection types are supported; also
Mitogen-specific :ref:`machinectl <machinectl>`, :ref:`mitogen_doas <doas>`, Mitogen-specific :ref:`mitogen_doas <doas>`, :ref:`machinectl <machinectl>`,
:ref:`mitogen_su <su>`, :ref:`mitogen_sudo <sudo>`, and :ref:`setns <setns>` :ref:`mitogen_su <su>`, :ref:`mitogen_sudo <sudo>`, and :ref:`setns <setns>`
types. File bugs to register interest in others. types. File bugs to register interest in others.
@ -819,6 +819,20 @@ Like the :ans:conn:`local` except connection delegation is supported.
* ``ansible_python_interpreter`` * ``ansible_python_interpreter``
Podman
~~~~~~
Like :ans:conn:`podman` except connection delegation is supported.
* ``ansible_host``: Name of container (default: inventory hostname).
* ``ansible_user``: Name of user within the container to execute as.
* ``mitogen_mask_remote_name``: if :data:`True`, mask the identity of the
Ansible controller process on remote machines. To simplify diagnostics,
Mitogen produces remote processes named like
`"mitogen:user@controller.name:1234"`, however this may be a privacy issue in
some circumstances.
Process Model Process Model
^^^^^^^^^^^^^ ^^^^^^^^^^^^^

@ -18,6 +18,16 @@ To avail of fixes in an unreleased version, please download a ZIP file
`directly from GitHub <https://github.com/dw/mitogen/>`_. `directly from GitHub <https://github.com/dw/mitogen/>`_.
v0.3.3 (2022-06-03)
-------------------
* :gh:issue:`906` Support packages dynamically inserted into sys.modules, e.g. `distro` >= 1.7.0 as `ansible.module_utils.distro`.
* :gh:issue:`918` Support Python 3.10
* :gh:issue:`920` Support Ansible :ans:conn:`~podman` connection plugin
* :gh:issue:`836` :func:`mitogen.utils.with_router` decorator preserves the docstring in addition to the name.
* :gh:issue:`936` :ans:mod:`fetch` no longer emits `[DEPRECATION WARNING]: The '_remote_checksum()' method is deprecated.`
v0.3.2 (2022-01-12) v0.3.2 (2022-01-12)
------------------- -------------------

@ -201,7 +201,7 @@ nested.py:
print('Connect local%d via %s' % (x, context)) print('Connect local%d via %s' % (x, context))
context = router.local(via=context, name='local%d' % x) context = router.local(via=context, name='local%d' % x)
context.call(os.system, 'pstree -s python -s mitogen') context.call(subprocess.check_call, ['pstree', '-s', 'python', '-s', 'mitogen'])
Output: Output:

@ -101,7 +101,7 @@ to your network topology**.
container='billing0', container='billing0',
) )
internal_box.call(os.system, './run-nightly-billing.py') internal_box.call(subprocess.check_call, ['./run-nightly-billing.py'])
The multiplexer also ensures the remote process is terminated if your Python The multiplexer also ensures the remote process is terminated if your Python
program crashes, communication is lost, or the application code running in the program crashes, communication is lost, or the application code running in the
@ -250,7 +250,7 @@ After:
""" """
Install our application. Install our application.
""" """
os.system('tar zxvf app.tar.gz') subprocess.check_call(['tar', 'zxvf', 'app.tar.gz'])
context.call(install_app) context.call(install_app)
@ -258,7 +258,7 @@ Or even:
.. code-block:: python .. code-block:: python
context.call(os.system, 'tar zxvf app.tar.gz') context.call(subprocess.check_call, ['tar', 'zxvf', 'app.tar.gz'])
Exceptions raised by function calls are propagated back to the parent program, Exceptions raised by function calls are propagated back to the parent program,
and timeouts can be configured to ensure failed calls do not block progress of and timeouts can be configured to ensure failed calls do not block progress of

@ -8,14 +8,14 @@ Usage:
Where: Where:
<hostname> Hostname to install to. <hostname> Hostname to install to.
""" """
import os import subprocess
import sys import sys
import mitogen import mitogen
def install_app(): def install_app():
os.system('tar zxvf my_app.tar.gz') subprocess.check_call(['tar', 'zxvf', 'my_app.tar.gz'])
@mitogen.main() @mitogen.main()

@ -35,7 +35,7 @@ be expected. On the slave, it is built dynamically during startup.
#: Library version as a tuple. #: Library version as a tuple.
__version__ = (0, 3, 2) __version__ = (0, 3, 3)
#: This is :data:`False` in slave contexts. Previously it was used to prevent #: This is :data:`False` in slave contexts. Previously it was used to prevent

@ -30,7 +30,6 @@
import logging import logging
import mitogen.core
import mitogen.parent import mitogen.parent

@ -386,6 +386,20 @@ def _partition(s, sep, find):
return left, sep, s[len(left)+len(sep):] return left, sep, s[len(left)+len(sep):]
def threading__current_thread():
try:
return threading.current_thread() # Added in Python 2.6+
except AttributeError:
return threading.currentThread() # Deprecated in Python 3.10+
def threading__thread_name(thread):
try:
return thread.name # Added in Python 2.6+
except AttributeError:
return thread.getName() # Deprecated in Python 3.10+
if hasattr(UnicodeType, 'rpartition'): if hasattr(UnicodeType, 'rpartition'):
str_partition = UnicodeType.partition str_partition = UnicodeType.partition
str_rpartition = UnicodeType.rpartition str_rpartition = UnicodeType.rpartition
@ -1357,6 +1371,16 @@ class Importer(object):
fp.close() fp.close()
def find_module(self, fullname, path=None): def find_module(self, fullname, path=None):
"""
Return a loader (ourself) or None, for the module with fullname.
Implements importlib.abc.MetaPathFinder.find_module().
Deprecrated in Python 3.4+, replaced by find_spec().
Raises ImportWarning in Python 3.10+.
fullname A (fully qualified?) module name, e.g. "os.path".
path __path__ of parent packge. None for a top level module.
"""
if hasattr(_tls, 'running'): if hasattr(_tls, 'running'):
return None return None
@ -1478,6 +1502,12 @@ class Importer(object):
callback() callback()
def load_module(self, fullname): def load_module(self, fullname):
"""
Return the loaded module specified by fullname.
Implements importlib.abc.Loader.load_module().
Deprecated in Python 3.4+, replaced by create_module() & exec_module().
"""
fullname = to_text(fullname) fullname = to_text(fullname)
_v and self._log.debug('requesting %s', fullname) _v and self._log.debug('requesting %s', fullname)
self._refuse_imports(fullname) self._refuse_imports(fullname)
@ -2687,7 +2717,7 @@ class Latch(object):
raise e raise e
assert cookie == got_cookie, ( assert cookie == got_cookie, (
"Cookie incorrect; got %r, expected %r" \ "Cookie incorrect; got %r, expected %r"
% (binascii.hexlify(got_cookie), % (binascii.hexlify(got_cookie),
binascii.hexlify(cookie)) binascii.hexlify(cookie))
) )
@ -2742,7 +2772,7 @@ class Latch(object):
return 'Latch(%#x, size=%d, t=%r)' % ( return 'Latch(%#x, size=%d, t=%r)' % (
id(self), id(self),
len(self._queue), len(self._queue),
threading.currentThread().getName(), threading__thread_name(threading__current_thread()),
) )
@ -3642,7 +3672,6 @@ class Dispatcher(object):
self._service_recv.notify = None self._service_recv.notify = None
self.recv.close() self.recv.close()
@classmethod @classmethod
@takes_econtext @takes_econtext
def forget_chain(cls, chain_id, econtext): def forget_chain(cls, chain_id, econtext):

@ -103,7 +103,6 @@ import tempfile
import threading import threading
import mitogen.core import mitogen.core
import mitogen.master
import mitogen.parent import mitogen.parent
from mitogen.core import LOG, IOLOG from mitogen.core import LOG, IOLOG
@ -200,7 +199,7 @@ class Process(object):
def _on_stdin(self, msg): def _on_stdin(self, msg):
if msg.is_dead: if msg.is_dead:
IOLOG.debug('%r._on_stdin() -> %r', self, data) IOLOG.debug('%r._on_stdin() -> %r', self, msg)
self.pump.protocol.close() self.pump.protocol.close()
return return
@ -437,7 +436,7 @@ def run(dest, router, args, deadline=None, econtext=None):
fp.write(inspect.getsource(mitogen.core)) fp.write(inspect.getsource(mitogen.core))
fp.write('\n') fp.write('\n')
fp.write('ExternalContext(%r).main()\n' % ( fp.write('ExternalContext(%r).main()\n' % (
_get_econtext_config(context, sock2), _get_econtext_config(econtext, sock2),
)) ))
finally: finally:
fp.close() fp.close()

@ -28,7 +28,6 @@
# !mitogen: minify_safe # !mitogen: minify_safe
import mitogen.core
import mitogen.parent import mitogen.parent

@ -28,7 +28,6 @@
# !mitogen: minify_safe # !mitogen: minify_safe
import mitogen.core
import mitogen.parent import mitogen.parent

@ -28,7 +28,6 @@
# !mitogen: minify_safe # !mitogen: minify_safe
import mitogen.core
import mitogen.parent import mitogen.parent

@ -122,6 +122,13 @@ def is_stdlib_name(modname):
""" """
Return :data:`True` if `modname` appears to come from the standard library. Return :data:`True` if `modname` appears to come from the standard library.
""" """
# `imp.is_builtin()` isn't a documented as part of Python's stdlib API.
#
# """
# Main is a little special - imp.is_builtin("__main__") will return False,
# but BuiltinImporter is still the most appropriate initial setting for
# its __loader__ attribute.
# """ -- comment in CPython pylifecycle.c:add_main_module()
if imp.is_builtin(modname) != 0: if imp.is_builtin(modname) != 0:
return True return True
@ -512,42 +519,57 @@ class PkgutilMethod(FinderMethod):
Find `fullname` using :func:`pkgutil.find_loader`. Find `fullname` using :func:`pkgutil.find_loader`.
""" """
try: try:
# If fullname refers to a submodule that's not already imported
# then the containing package is imported.
# Pre-'import spec' this returned None, in Python3.6 it raises # Pre-'import spec' this returned None, in Python3.6 it raises
# ImportError. # ImportError.
loader = pkgutil.find_loader(fullname) loader = pkgutil.find_loader(fullname)
except ImportError: except ImportError:
e = sys.exc_info()[1] e = sys.exc_info()[1]
LOG.debug('%r._get_module_via_pkgutil(%r): %s', LOG.debug('%r: find_loader(%r) failed: %s', self, fullname, e)
self, fullname, e)
return None return None
IOLOG.debug('%r._get_module_via_pkgutil(%r) -> %r',
self, fullname, loader)
if not loader: if not loader:
LOG.debug('%r: find_loader(%r) returned %r, aborting',
self, fullname, loader)
return return
try: try:
path, is_special = _py_filename(loader.get_filename(fullname)) path = loader.get_filename(fullname)
source = loader.get_source(fullname)
is_pkg = loader.is_package(fullname)
# workaround for special python modules that might only exist in memory
if is_special and is_pkg and not source:
source = '\n'
except (AttributeError, ImportError): except (AttributeError, ImportError):
# - Per PEP-302, get_source() and is_package() are optional,
# calling them may throw AttributeError.
# - get_filename() may throw ImportError if pkgutil.find_loader() # - get_filename() may throw ImportError if pkgutil.find_loader()
# picks a "parent" package's loader for some crap that's been # picks a "parent" package's loader for some crap that's been
# stuffed in sys.modules, for example in the case of urllib3: # stuffed in sys.modules, for example in the case of urllib3:
# "loader for urllib3.contrib.pyopenssl cannot handle # "loader for urllib3.contrib.pyopenssl cannot handle
# requests.packages.urllib3.contrib.pyopenssl" # requests.packages.urllib3.contrib.pyopenssl"
e = sys.exc_info()[1] e = sys.exc_info()[1]
LOG.debug('%r: loading %r using %r failed: %s', LOG.debug('%r: %r.get_file_name(%r) failed: %r', self, loader, fullname, e)
self, fullname, loader, e) return
path, is_special = _py_filename(path)
try:
source = loader.get_source(fullname)
except AttributeError:
# Per PEP-302, get_source() is optional,
e = sys.exc_info()[1]
LOG.debug('%r: %r.get_source() failed: %r', self, loader, fullname, e)
return return
try:
is_pkg = loader.is_package(fullname)
except AttributeError:
# Per PEP-302, is_package() is optional,
e = sys.exc_info()[1]
LOG.debug('%r: %r.is_package(%r) failed: %r', self, loader, fullname, e)
return
# workaround for special python modules that might only exist in memory
if is_special and is_pkg and not source:
source = '\n'
if path is None or source is None: if path is None or source is None:
LOG.debug('%r: path=%r, source=%r, aborting', self, path, source)
return return
if isinstance(source, mitogen.core.UnicodeType): if isinstance(source, mitogen.core.UnicodeType):
@ -567,23 +589,37 @@ class SysModulesMethod(FinderMethod):
""" """
Find `fullname` using its :data:`__file__` attribute. Find `fullname` using its :data:`__file__` attribute.
""" """
module = sys.modules.get(fullname) try:
module = sys.modules[fullname]
except KeyError:
LOG.debug('%r: sys.modules[%r] absent, aborting', self, fullname)
return
if not isinstance(module, types.ModuleType): if not isinstance(module, types.ModuleType):
LOG.debug('%r: sys.modules[%r] absent or not a regular module', LOG.debug('%r: sys.modules[%r] is %r, aborting',
self, fullname) self, fullname, module)
return return
LOG.debug('_get_module_via_sys_modules(%r) -> %r', fullname, module) try:
alleged_name = getattr(module, '__name__', None) resolved_name = module.__name__
if alleged_name != fullname: except AttributeError:
LOG.debug('sys.modules[%r].__name__ is incorrect, assuming ' LOG.debug('%r: %r has no __name__, aborting', self, module)
'this is a hacky module alias and ignoring it. '
'Got %r, module object: %r',
fullname, alleged_name, module)
return return
path, _ = _py_filename(getattr(module, '__file__', '')) if resolved_name != fullname:
LOG.debug('%r: %r.__name__ is %r, aborting',
self, module, resolved_name)
return
try:
path = module.__file__
except AttributeError:
LOG.debug('%r: %r has no __file__, aborting', self, module)
return
path, _ = _py_filename(path)
if not path: if not path:
LOG.debug('%r: %r.__file__ is %r, aborting', self, module, path)
return return
LOG.debug('%r: sys.modules[%r]: found %s', self, fullname, path) LOG.debug('%r: sys.modules[%r]: found %s', self, fullname, path)
@ -628,10 +664,24 @@ class ParentEnumerationMethod(FinderMethod):
module object or any parent package's :data:`__path__`, since they have all module object or any parent package's :data:`__path__`, since they have all
been overwritten. Some men just want to watch the world burn. been overwritten. Some men just want to watch the world burn.
""" """
@staticmethod
def _iter_parents(fullname):
"""
>>> list(ParentEnumerationMethod._iter_parents('a'))
[('', 'a')]
>>> list(ParentEnumerationMethod._iter_parents('a.b.c'))
[('a.b', 'c'), ('a', 'b'), ('', 'a')]
"""
while fullname:
fullname, _, modname = str_rpartition(fullname, u'.')
yield fullname, modname
def _find_sane_parent(self, fullname): def _find_sane_parent(self, fullname):
""" """
Iteratively search :data:`sys.modules` for the least indirect parent of Iteratively search :data:`sys.modules` for the least indirect parent of
`fullname` that is loaded and contains a :data:`__path__` attribute. `fullname` that's from the same package and has a :data:`__path__`
attribute.
:return: :return:
`(parent_name, path, modpath)` tuple, where: `(parent_name, path, modpath)` tuple, where:
@ -644,21 +694,40 @@ class ParentEnumerationMethod(FinderMethod):
* `modpath`: list of module name components leading from `path` * `modpath`: list of module name components leading from `path`
to the target module. to the target module.
""" """
path = None
modpath = [] modpath = []
while True: for pkgname, modname in self._iter_parents(fullname):
pkgname, _, modname = str_rpartition(to_text(fullname), u'.')
modpath.insert(0, modname) modpath.insert(0, modname)
if not pkgname: if not pkgname:
return [], None, modpath return [], None, modpath
pkg = sys.modules.get(pkgname) try:
path = getattr(pkg, '__path__', None) pkg = sys.modules[pkgname]
if pkg and path: except KeyError:
return pkgname.split('.'), path, modpath LOG.debug('%r: sys.modules[%r] absent, skipping', self, pkgname)
continue
try:
resolved_pkgname = pkg.__name__
except AttributeError:
LOG.debug('%r: %r has no __name__, skipping', self, pkg)
continue
LOG.debug('%r: %r lacks __path__ attribute', self, pkgname) if resolved_pkgname != pkgname:
fullname = pkgname LOG.debug('%r: %r.__name__ is %r, skipping',
self, pkg, resolved_pkgname)
continue
try:
path = pkg.__path__
except AttributeError:
LOG.debug('%r: %r has no __path__, skipping', self, pkg)
continue
if not path:
LOG.debug('%r: %r.__path__ is %r, skipping', self, pkg, path)
continue
return pkgname.split('.'), path, modpath
def _found_package(self, fullname, path): def _found_package(self, fullname, path):
path = os.path.join(path, '__init__.py') path = os.path.join(path, '__init__.py')
@ -1167,7 +1236,7 @@ class Broker(mitogen.core.Broker):
def __init__(self, install_watcher=True): def __init__(self, install_watcher=True):
if install_watcher: if install_watcher:
self._watcher = ThreadWatcher.watch( self._watcher = ThreadWatcher.watch(
target=threading.currentThread(), target=mitogen.core.threading__current_thread(),
on_join=self.shutdown, on_join=self.shutdown,
) )
super(Broker, self).__init__() super(Broker, self).__init__()

@ -35,7 +35,6 @@ Support for operating in a mixed threading/forking environment.
import os import os
import socket import socket
import sys import sys
import threading
import weakref import weakref
import mitogen.core import mitogen.core
@ -158,7 +157,7 @@ class Corker(object):
held. This will not return until each thread acknowledges it has ceased held. This will not return until each thread acknowledges it has ceased
execution. execution.
""" """
current = threading.currentThread() current = mitogen.core.threading__current_thread()
s = mitogen.core.b('CORK') * ((128 // 4) * 1024) s = mitogen.core.b('CORK') * ((128 // 4) * 1024)
self._rsocks = [] self._rsocks = []

@ -1508,7 +1508,7 @@ class Connection(object):
def get_preamble(self): def get_preamble(self):
suffix = ( suffix = (
'\nExternalContext(%r).main()\n' %\ '\nExternalContext(%r).main()\n' %
(self.get_econtext_config(),) (self.get_econtext_config(),)
) )
partial = get_core_source_partial() partial = get_core_source_partial()

@ -31,7 +31,6 @@
import logging import logging
import mitogen.core
import mitogen.parent import mitogen.parent

@ -31,7 +31,6 @@
import grp import grp
import logging import logging
import os import os
import os.path
import pprint import pprint
import pwd import pwd
import stat import stat
@ -109,7 +108,8 @@ def get_or_create_pool(size=None, router=None, context=None):
def get_thread_name(): def get_thread_name():
return threading.currentThread().getName() thread = mitogen.core.threading__current_thread()
return mitogen.core.threading__thread_name(thread)
def call(service_name, method_name, call_context=None, **kwargs): def call(service_name, method_name, call_context=None, **kwargs):

@ -29,14 +29,13 @@
# !mitogen: minify_safe # !mitogen: minify_safe
import datetime import datetime
import functools
import logging import logging
import os import os
import sys import sys
import mitogen
import mitogen.core import mitogen.core
import mitogen.master import mitogen.master
import mitogen.parent
iteritems = getattr(dict, 'iteritems', dict.items) iteritems = getattr(dict, 'iteritems', dict.items)
@ -173,12 +172,9 @@ def with_router(func):
do_stuff(blah, 123) do_stuff(blah, 123)
""" """
@functools.wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return run_with_router(func, *args, **kwargs) return run_with_router(func, *args, **kwargs)
if mitogen.core.PY3:
wrapper.func_name = func.__name__
else:
wrapper.func_name = func.func_name
return wrapper return wrapper

@ -28,10 +28,6 @@ NOCOVERAGE="${NOCOVERAGE:-}"
NOCOVERAGE_ERASE="${NOCOVERAGE_ERASE:-$NOCOVERAGE}" NOCOVERAGE_ERASE="${NOCOVERAGE_ERASE:-$NOCOVERAGE}"
NOCOVERAGE_REPORT="${NOCOVERAGE_REPORT:-$NOCOVERAGE}" NOCOVERAGE_REPORT="${NOCOVERAGE_REPORT:-$NOCOVERAGE}"
if [ ! "$UNIT2" ]; then
UNIT2="$(which unit2)"
fi
if [ ! "$NOCOVERAGE_ERASE" ]; then if [ ! "$NOCOVERAGE_ERASE" ]; then
coverage erase coverage erase
fi fi
@ -39,12 +35,12 @@ fi
# First run overwites coverage output. # First run overwites coverage output.
[ "$SKIP_MITOGEN" ] || { [ "$SKIP_MITOGEN" ] || {
if [ ! "$NOCOVERAGE" ]; then if [ ! "$NOCOVERAGE" ]; then
coverage run -a "${UNIT2}" discover \ coverage run -a -m unittest discover \
--start-directory "tests" \ --start-directory "tests" \
--pattern '*_test.py' \ --pattern '*_test.py' \
"$@" "$@"
else else
"${UNIT2}" discover \ python -m unittest discover \
--start-directory "tests" \ --start-directory "tests" \
--pattern '*_test.py' \ --pattern '*_test.py' \
"$@" "$@"
@ -60,12 +56,12 @@ fi
[ "$SKIP_ANSIBLE" ] || { [ "$SKIP_ANSIBLE" ] || {
export PYTHONPATH=`pwd`/tests:$PYTHONPATH export PYTHONPATH=`pwd`/tests:$PYTHONPATH
if [ ! "$NOCOVERAGE" ]; then if [ ! "$NOCOVERAGE" ]; then
coverage run -a "${UNIT2}" discover \ coverage run -a -m unittest discover \
--start-directory "tests/ansible" \ --start-directory "tests/ansible" \
--pattern '*_test.py' \ --pattern '*_test.py' \
"$@" "$@"
else else
"${UNIT2}" discover \ python -m unittest discover \
--start-directory "tests/ansible" \ --start-directory "tests/ansible" \
--pattern '*_test.py' \ --pattern '*_test.py' \
"$@" "$@"

@ -60,7 +60,7 @@ setup(
license = 'New BSD', license = 'New BSD',
url = 'https://github.com/mitogen-hq/mitogen/', url = 'https://github.com/mitogen-hq/mitogen/',
packages = find_packages(exclude=['tests', 'examples']), packages = find_packages(exclude=['tests', 'examples']),
python_requires='>=2.4, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4', python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
zip_safe = False, zip_safe = False,
classifiers = [ classifiers = [
'Environment :: Console', 'Environment :: Console',
@ -70,16 +70,13 @@ setup(
'Operating System :: MacOS :: MacOS X', 'Operating System :: MacOS :: MacOS X',
'Operating System :: POSIX', 'Operating System :: POSIX',
'Programming Language :: Python', 'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.4',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: CPython',
'Topic :: System :: Distributed Computing', 'Topic :: System :: Distributed Computing',
'Topic :: System :: Systems Administration', 'Topic :: System :: Systems Administration',

@ -10,7 +10,7 @@ demonstrator for what does and doesn't work.
## Preparation ## Preparation
See `../image_prep/README.md`. See [`../image_prep/README.md`](../image_prep/README.md).
## `run_ansible_playbook.py` ## `run_ansible_playbook.py`

@ -1,3 +1,2 @@
def path(): def path():
return "integration/module_utils/roles/modrole/module_utils/external2.py" return "integration/module_utils/roles/modrole/module_utils/external2.py"

@ -1,11 +1,11 @@
#!/usr/bin/python #!/usr/bin/python
import json import json
from ansible.module_utils.basic import path import ansible.module_utils.basic
def main(): def main():
print(json.dumps({ print(json.dumps({
'path': path() 'path': ansible.module_utils.basic.path()
})) }))
if __name__ == '__main__': if __name__ == '__main__':

@ -17,7 +17,7 @@
- custom_python_run_script: - custom_python_run_script:
script: | script: |
import atexit, shutil import atexit, os, shutil
path = '{{path}}' path = '{{path}}'
os.mkdir(path, int('777', 8)) os.mkdir(path, int('777', 8))
atexit.register(shutil.rmtree, path) atexit.register(shutil.rmtree, path)

@ -1,8 +1,5 @@
import traceback import traceback
import sys
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase

@ -7,13 +7,11 @@
__metaclass__ = type __metaclass__ = type
import inspect import inspect
import unittest2 import unittest
import ansible.template import ansible.template
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.module_utils.six import string_types
TEMPLATE_KWARGS = {} TEMPLATE_KWARGS = {}
@ -23,7 +21,7 @@ if 'bare_deprecated' in _argspec.args:
TEMPLATE_KWARGS['bare_deprecated'] = False TEMPLATE_KWARGS['bare_deprecated'] = False
class TestCase(unittest2.TestCase): class TestCase(unittest.TestCase):
def runTest(self): def runTest(self):
pass pass

@ -1,8 +1,5 @@
import traceback import traceback
import sys
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase

@ -1,4 +1,3 @@
import sys import sys
from ansible.plugins.strategy import StrategyBase from ansible.plugins.strategy import StrategyBase

@ -5,9 +5,7 @@ required for reliable LRU tests.
import ansible_mitogen.connection import ansible_mitogen.connection
import ansible_mitogen.services import ansible_mitogen.services
import mitogen.service
from ansible.plugins.strategy import StrategyBase
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase

@ -1,4 +1,3 @@
# Monkey-patch os.fork() to produce a latency histogram on run completion. # Monkey-patch os.fork() to produce a latency histogram on run completion.
# Requires 'hdrhsitograms' PyPI module. # Requires 'hdrhsitograms' PyPI module.

@ -1,7 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import io import io
import os import os
import sys
from ansible import constants as C from ansible import constants as C
from ansible.module_utils import six from ansible.module_utils import six

@ -1,4 +1,3 @@
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text

@ -1,3 +1,2 @@
def path(): def path():
return "ansible/lib/module_utils/external2.py" return "ansible/lib/module_utils/external2.py"

@ -1,3 +1,2 @@
def path(): def path():
return 'ansible/lib/module_utils/externalpkg/extmod.py' return 'ansible/lib/module_utils/externalpkg/extmod.py'

@ -4,7 +4,6 @@
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os import os
import pwd import pwd

@ -1,8 +1,6 @@
#!/usr/bin/python #!/usr/bin/python
# I am an Ansible Python JSONARGS module. I should receive an encoding string. # I am an Ansible Python JSONARGS module. I should receive an encoding string.
import sys
json_arguments = """<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>""" json_arguments = """<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"""
print("{") print("{")

@ -5,9 +5,6 @@
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
import os import os
import pwd
import socket
import sys
def main(): def main():

@ -2,13 +2,6 @@
# issue #555: I'm a module that cutpastes an old hack. # issue #555: I'm a module that cutpastes an old hack.
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os
import pwd
import socket
import sys
import sys import sys
reload(sys) reload(sys)

@ -3,12 +3,7 @@
# parameter. # parameter.
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import get_module_path
from ansible.module_utils import six
import os
import pwd
import socket
import sys import sys

@ -1,6 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import subprocess
import sys import sys
os.environ['ANSIBLE_STRATEGY'] = 'mitogen_linear' os.environ['ANSIBLE_STRATEGY'] = 'mitogen_linear'
os.execlp( os.execlp(

@ -1,12 +1,14 @@
# issue #615: 'fetch' with become: was internally using slurp. # issue #615: 'fetch' with become: was internally using slurp.
- hosts: target - hosts: test-targets
any_errors_fatal: True any_errors_fatal: True
gather_facts: no gather_facts: no
# Without Mitogen this causes Ansible to use the slurp module, which is *slow*
become: true become: true
vars: vars:
mitogen_ssh_compression: false mitogen_ssh_compression: false
tasks: tasks:
- block:
- shell: | - shell: |
dd if=/dev/zero of=/tmp/512mb.zero bs=1048576 count=512; dd if=/dev/zero of=/tmp/512mb.zero bs=1048576 count=512;
chmod go= /tmp/512mb.zero chmod go= /tmp/512mb.zero
@ -15,9 +17,16 @@
src: /tmp/512mb.zero src: /tmp/512mb.zero
dest: /tmp/fetch-out dest: /tmp/fetch-out
- file:
path: /tmp/512mb.zero
state: absent
- file: - file:
path: /tmp/fetch-out path: /tmp/fetch-out
state: absent state: absent
delegate_to: localhost delegate_to: localhost
run_once: true
when:
- is_mitogen
tags: tags:
- issue_615 - issue_615

@ -13,36 +13,33 @@
- name: set up test container and run tests inside it - name: set up test container and run tests inside it
block: block:
- name: install deps - name: install deps
block: homebrew:
- name: install docker name:
shell: | - podman
# NOTE: for tracking purposes: https://github.com/docker/for-mac/issues/2359 state: present
# using docker for mac CI workaround: https://github.com/drud/ddev/pull/1748/files#diff-19288f650af2dabdf1dcc5b354d1f245
DOCKER_URL=https://download.docker.com/mac/stable/31259/Docker.dmg && - name: start machine
curl -O -sSL $DOCKER_URL && command:
open -W Docker.dmg && cp -r /Volumes/Docker/Docker.app /Applications cmd: "{{ item.cmd }}"
sudo /Applications/Docker.app/Contents/MacOS/Docker --quit-after-install --unattended && loop:
ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker && - cmd: podman machine init
nohup /Applications/Docker.app/Contents/MacOS/Docker --unattended & - cmd: podman machine start
# wait 2 min for docker to come up - cmd: podman info
counter=0 && timeout: 300
while ! /usr/local/bin/docker ps 2>/dev/null ; do register: podman_machine
if [ $counter -lt 24 ]; then
let counter=counter+1 - debug:
else var: podman_machine
exit 1
fi
sleep 5
done
# python bindings (docker_container) aren't working on this host, so gonna shell out # python bindings (docker_container) aren't working on this host, so gonna shell out
- name: create docker container - name: create container
shell: /usr/local/bin/docker run --name testMitogen -d --rm centos:8 bash -c "sleep infinity & wait" command:
cmd: podman run --name testMitogen -d --rm centos:8 bash -c "sleep infinity & wait"
- name: add container to inventory - name: add container to inventory
add_host: add_host:
name: testMitogen name: testMitogen
ansible_connection: docker ansible_connection: podman
ansible_user: root ansible_user: root
changed_when: false changed_when: false
environment: environment:
@ -82,6 +79,10 @@
PATH: /usr/local/bin/:{{ ansible_env.PATH }} PATH: /usr/local/bin/:{{ ansible_env.PATH }}
- name: remove test container - name: remove test container
shell: /usr/local/bin/docker stop testMitogen command:
cmd: "{{ item.cmd }}"
loop:
- cmd: podman stop testMitogen
- cmd: podman machine stop
tags: tags:
- issue_655 - issue_655

@ -1,11 +1,9 @@
import multiprocessing import multiprocessing
import os import os
import sys import sys
import tempfile import tempfile
import unittest
import mock
import unittest2
import testlib import testlib
import mitogen.parent import mitogen.parent
@ -18,7 +16,7 @@ class NullFixedPolicy(ansible_mitogen.affinity.FixedPolicy):
self.mask = mask self.mask = mask
@unittest2.skipIf( @unittest.skipIf(
reason='Linux only', reason='Linux only',
condition=(not os.uname()[0] == 'Linux') condition=(not os.uname()[0] == 'Linux')
) )
@ -29,139 +27,139 @@ class FixedPolicyTest(testlib.TestCase):
# Uniprocessor . # Uniprocessor .
policy = self.klass(cpu_count=1) policy = self.klass(cpu_count=1)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_controller_2core(self): def test_assign_controller_2core(self):
# Small SMP gets 1.. % cpu_count # Small SMP gets 1.. % cpu_count
policy = self.klass(cpu_count=2) policy = self.klass(cpu_count=2)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_controller() policy.assign_controller()
def test_assign_controller_3core(self): def test_assign_controller_3core(self):
# Small SMP gets 1.. % cpu_count # Small SMP gets 1.. % cpu_count
policy = self.klass(cpu_count=3) policy = self.klass(cpu_count=3)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x4, policy.mask) self.assertEqual(0x4, policy.mask)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x4, policy.mask) self.assertEqual(0x4, policy.mask)
policy.assign_controller() policy.assign_controller()
def test_assign_controller_4core(self): def test_assign_controller_4core(self):
# Big SMP gets a dedicated core. # Big SMP gets a dedicated core.
policy = self.klass(cpu_count=4) policy = self.klass(cpu_count=4)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_controller() policy.assign_controller()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
def test_assign_muxprocess_1core(self): def test_assign_muxprocess_1core(self):
# Uniprocessor . # Uniprocessor .
policy = self.klass(cpu_count=1) policy = self.klass(cpu_count=1)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_muxprocess_2core(self): def test_assign_muxprocess_2core(self):
# Small SMP gets dedicated core. # Small SMP gets dedicated core.
policy = self.klass(cpu_count=2) policy = self.klass(cpu_count=2)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
def test_assign_muxprocess_3core(self): def test_assign_muxprocess_3core(self):
# Small SMP gets a dedicated core. # Small SMP gets a dedicated core.
policy = self.klass(cpu_count=3) policy = self.klass(cpu_count=3)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_muxprocess_4core(self): def test_assign_muxprocess_4core(self):
# Big SMP gets a dedicated core. # Big SMP gets a dedicated core.
policy = self.klass(cpu_count=4) policy = self.klass(cpu_count=4)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_muxprocess(0) policy.assign_muxprocess(0)
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_worker_1core(self): def test_assign_worker_1core(self):
# Balance n % 1 # Balance n % 1
policy = self.klass(cpu_count=1) policy = self.klass(cpu_count=1)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_worker_2core(self): def test_assign_worker_2core(self):
# Balance n % 1 # Balance n % 1
policy = self.klass(cpu_count=2) policy = self.klass(cpu_count=2)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
def test_assign_worker_3core(self): def test_assign_worker_3core(self):
# Balance n % 1 # Balance n % 1
policy = self.klass(cpu_count=3) policy = self.klass(cpu_count=3)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x4, policy.mask) self.assertEqual(0x4, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
def test_assign_worker_4core(self): def test_assign_worker_4core(self):
# Balance n % 1 # Balance n % 1
policy = self.klass(cpu_count=4) policy = self.klass(cpu_count=4)
policy.assign_worker() policy.assign_worker()
self.assertEquals(4, policy.mask) self.assertEqual(4, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(8, policy.mask) self.assertEqual(8, policy.mask)
policy.assign_worker() policy.assign_worker()
self.assertEquals(4, policy.mask) self.assertEqual(4, policy.mask)
def test_assign_subprocess_1core(self): def test_assign_subprocess_1core(self):
# allow all except reserved. # allow all except reserved.
policy = self.klass(cpu_count=1) policy = self.klass(cpu_count=1)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x1, policy.mask) self.assertEqual(0x1, policy.mask)
def test_assign_subprocess_2core(self): def test_assign_subprocess_2core(self):
# allow all except reserved. # allow all except reserved.
policy = self.klass(cpu_count=2) policy = self.klass(cpu_count=2)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x2, policy.mask) self.assertEqual(0x2, policy.mask)
def test_assign_subprocess_3core(self): def test_assign_subprocess_3core(self):
# allow all except reserved. # allow all except reserved.
policy = self.klass(cpu_count=3) policy = self.klass(cpu_count=3)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x2 + 0x4, policy.mask) self.assertEqual(0x2 + 0x4, policy.mask)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x2 + 0x4, policy.mask) self.assertEqual(0x2 + 0x4, policy.mask)
def test_assign_subprocess_4core(self): def test_assign_subprocess_4core(self):
# allow all except reserved. # allow all except reserved.
policy = self.klass(cpu_count=4) policy = self.klass(cpu_count=4)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x4 + 0x8, policy.mask) self.assertEqual(0x4 + 0x8, policy.mask)
policy.assign_subprocess() policy.assign_subprocess()
self.assertEquals(0x4 + 0x8, policy.mask) self.assertEqual(0x4 + 0x8, policy.mask)
@unittest2.skipIf( @unittest.skipIf(
reason='Linux/SMP only', reason='Linux/SMP only',
condition=(not ( condition=(not (
os.uname()[0] == 'Linux' and os.uname()[0] == 'Linux' and
@ -186,13 +184,13 @@ class LinuxPolicyTest(testlib.TestCase):
def test_set_cpu_mask(self): def test_set_cpu_mask(self):
self.policy._set_cpu_mask(0x1) self.policy._set_cpu_mask(0x1)
self.assertEquals(0x1, self._get_cpus()) self.assertEqual(0x1, self._get_cpus())
self.policy._set_cpu_mask(0x2) self.policy._set_cpu_mask(0x2)
self.assertEquals(0x2, self._get_cpus()) self.assertEqual(0x2, self._get_cpus())
self.policy._set_cpu_mask(0x3) self.policy._set_cpu_mask(0x3)
self.assertEquals(0x3, self._get_cpus()) self.assertEqual(0x3, self._get_cpus())
def test_clear_on_popen(self): def test_clear_on_popen(self):
tf = tempfile.NamedTemporaryFile() tf = tempfile.NamedTemporaryFile()
@ -223,11 +221,7 @@ class MockLinuxPolicyTest(testlib.TestCase):
for x in range(1, 4096, 32): for x in range(1, 4096, 32):
policy.assign_subprocess() policy.assign_subprocess()
MockLinuxPolicyTest = unittest2.skipIf( MockLinuxPolicyTest = unittest.skipIf(
condition=(not sys.platform.startswith('linuxPolicy')), condition=(not sys.platform.startswith('linuxPolicy')),
reason='select.select() not supported' reason='select.select() not supported'
)(MockLinuxPolicyTest) )(MockLinuxPolicyTest)
if __name__ == '__main__':
unittest2.main()

@ -1,19 +1,12 @@
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import os.path
import subprocess
import tempfile import tempfile
import time
import unittest2
import mock import mock
import ansible.errors import ansible.errors
import ansible.playbook.play_context import ansible.playbook.play_context
import mitogen.core import mitogen.core
import mitogen.utils
import ansible_mitogen.connection import ansible_mitogen.connection
import ansible_mitogen.plugins.connection.mitogen_local import ansible_mitogen.plugins.connection.mitogen_local
@ -27,7 +20,6 @@ class MuxProcessMixin(object):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
#mitogen.utils.log_to_file()
cls.model = ansible_mitogen.process.get_classic_worker_model( cls.model = ansible_mitogen.process.get_classic_worker_model(
_init_logging=False _init_logging=False
) )
@ -93,20 +85,20 @@ class OptionalIntTest(testlib.TestCase):
func = staticmethod(ansible_mitogen.connection.optional_int) func = staticmethod(ansible_mitogen.connection.optional_int)
def test_already_int(self): def test_already_int(self):
self.assertEquals(0, self.func(0)) self.assertEqual(0, self.func(0))
self.assertEquals(1, self.func(1)) self.assertEqual(1, self.func(1))
self.assertEquals(-1, self.func(-1)) self.assertEqual(-1, self.func(-1))
def test_is_string(self): def test_is_string(self):
self.assertEquals(0, self.func("0")) self.assertEqual(0, self.func("0"))
self.assertEquals(1, self.func("1")) self.assertEqual(1, self.func("1"))
self.assertEquals(-1, self.func("-1")) self.assertEqual(-1, self.func("-1"))
def test_is_none(self): def test_is_none(self):
self.assertEquals(None, self.func(None)) self.assertEqual(None, self.func(None))
def test_is_junk(self): def test_is_junk(self):
self.assertEquals(None, self.func({1:2})) self.assertEqual(None, self.func({1:2}))
class FetchFileTest(ConnectionMixin, testlib.TestCase): class FetchFileTest(ConnectionMixin, testlib.TestCase):
@ -121,7 +113,7 @@ class FetchFileTest(ConnectionMixin, testlib.TestCase):
# transfer_file() uses os.rename rather than direct data # transfer_file() uses os.rename rather than direct data
# overwrite, so we must reopen. # overwrite, so we must reopen.
with open(ofp.name, 'rb') as fp: with open(ofp.name, 'rb') as fp:
self.assertEquals(ifp.read(), fp.read()) self.assertEqual(ifp.read(), fp.read())
class PutDataTest(ConnectionMixin, testlib.TestCase): class PutDataTest(ConnectionMixin, testlib.TestCase):
@ -131,7 +123,8 @@ class PutDataTest(ConnectionMixin, testlib.TestCase):
self.conn.put_data(path, contents) self.conn.put_data(path, contents)
self.wait_for_completion() self.wait_for_completion()
self.assertEquals(contents, open(path, 'rb').read()) with open(path, 'rb') as f:
self.assertEqual(contents, f.read())
os.unlink(path) os.unlink(path)
def test_mode(self): def test_mode(self):
@ -141,7 +134,7 @@ class PutDataTest(ConnectionMixin, testlib.TestCase):
self.conn.put_data(path, contents, mode=int('0123', 8)) self.conn.put_data(path, contents, mode=int('0123', 8))
self.wait_for_completion() self.wait_for_completion()
st = os.stat(path) st = os.stat(path)
self.assertEquals(int('0123', 8), st.st_mode & int('0777', 8)) self.assertEqual(int('0123', 8), st.st_mode & int('0777', 8))
os.unlink(path) os.unlink(path)
@ -165,17 +158,18 @@ class PutFileTest(ConnectionMixin, testlib.TestCase):
path = tempfile.mktemp(prefix='mitotest') path = tempfile.mktemp(prefix='mitotest')
self.conn.put_file(in_path=__file__, out_path=path) self.conn.put_file(in_path=__file__, out_path=path)
self.wait_for_completion() self.wait_for_completion()
self.assertEquals(open(path, 'rb').read(), with open(path, 'rb') as path_f:
open(__file__, 'rb').read()) with open(__file__, 'rb') as __file__f:
self.assertEqual(path_f.read(), __file__f.read())
os.unlink(path) os.unlink(path)
def test_out_path_big(self): def test_out_path_big(self):
path = tempfile.mktemp(prefix='mitotest') path = tempfile.mktemp(prefix='mitotest')
self.conn.put_file(in_path=self.big_path, out_path=path) self.conn.put_file(in_path=self.big_path, out_path=path)
self.wait_for_completion() self.wait_for_completion()
self.assertEquals(open(path, 'rb').read(), with open(path, 'rb') as path_f:
open(self.big_path, 'rb').read()) with open(self.big_path, 'rb') as big_path_f:
self.assertEqual(path_f.read(), big_path_f.read())
#self._compare_times_modes(path, __file__) #self._compare_times_modes(path, __file__)
os.unlink(path) os.unlink(path)
@ -183,7 +177,3 @@ class PutFileTest(ConnectionMixin, testlib.TestCase):
path = tempfile.mktemp(prefix='mitotest') path = tempfile.mktemp(prefix='mitotest')
self.assertRaises(ansible.errors.AnsibleFileNotFound, self.assertRaises(ansible.errors.AnsibleFileNotFound,
lambda: self.conn.put_file(in_path='/nonexistent', out_path=path)) lambda: self.conn.put_file(in_path='/nonexistent', out_path=path))
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,6 @@
import os import os
import sys
import tempfile import tempfile
import mock
import unittest2
import testlib import testlib
from mitogen.core import b from mitogen.core import b
@ -68,7 +65,3 @@ class WatcherTest(testlib.TestCase):
self.tf.flush() self.tf.flush()
watcher.check() watcher.check()
self.assertEqual(environb[b('SOMEKEY')], b('\xff\xff\xff')) self.assertEqual(environb[b('SOMEKEY')], b('\xff\xff\xff'))
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import
import os.path import os.path
import subprocess import subprocess
import tempfile import tempfile
import unittest2 import unittest
import mock import mock
@ -56,18 +55,18 @@ class FindGoodTempDirTest(testlib.TestCase):
class ApplyModeSpecTest(unittest2.TestCase): class ApplyModeSpecTest(unittest.TestCase):
func = staticmethod(ansible_mitogen.target.apply_mode_spec) func = staticmethod(ansible_mitogen.target.apply_mode_spec)
def test_simple(self): def test_simple(self):
spec = 'u+rwx,go=x' spec = 'u+rwx,go=x'
self.assertEquals(int('0711', 8), self.func(spec, 0)) self.assertEqual(int('0711', 8), self.func(spec, 0))
spec = 'g-rw' spec = 'g-rw'
self.assertEquals(int('0717', 8), self.func(spec, int('0777', 8))) self.assertEqual(int('0717', 8), self.func(spec, int('0777', 8)))
class IsGoodTempDirTest(unittest2.TestCase): class IsGoodTempDirTest(unittest.TestCase):
func = staticmethod(ansible_mitogen.target.is_good_temp_dir) func = staticmethod(ansible_mitogen.target.is_good_temp_dir)
def test_creates(self): def test_creates(self):
@ -84,9 +83,10 @@ class IsGoodTempDirTest(unittest2.TestCase):
fp.write('derp') fp.write('derp')
self.assertTrue(os.path.isfile(bleh)) self.assertTrue(os.path.isfile(bleh))
self.assertFalse(self.func(bleh)) self.assertFalse(self.func(bleh))
self.assertEquals(open(bleh).read(), 'derp') with open(bleh) as fp:
self.assertEqual(fp.read(), 'derp')
@unittest2.skipIf( @unittest.skipIf(
os.geteuid() == 0, 'writes by root ignore directory permissions') os.geteuid() == 0, 'writes by root ignore directory permissions')
def test_unwriteable(self): def test_unwriteable(self):
with NamedTemporaryDirectory() as temp_path: with NamedTemporaryDirectory() as temp_path:
@ -105,8 +105,3 @@ class IsGoodTempDirTest(unittest2.TestCase):
os_access.return_value = False os_access.return_value = False
with NamedTemporaryDirectory() as temp_path: with NamedTemporaryDirectory() as temp_path:
self.assertFalse(self.func(temp_path)) self.assertFalse(self.func(temp_path))
if __name__ == '__main__':
unittest2.main()

@ -5,6 +5,12 @@ Measure latency of .fork() setup/teardown.
import mitogen import mitogen
import mitogen.core import mitogen.core
try:
xrange
except NameError:
xrange = range
@mitogen.main() @mitogen.main()
def main(router): def main(router):
t0 = mitogen.core.now() t0 = mitogen.core.now()
@ -12,4 +18,4 @@ def main(router):
t = mitogen.core.now() t = mitogen.core.now()
ctx = router.fork() ctx = router.fork()
ctx.shutdown(wait=True) ctx.shutdown(wait=True)
print '++', 1000 * ((mitogen.core.now() - t0) / (1.0+x)) print('++ %d' % 1000 * ((mitogen.core.now() - t0) / (1.0+x)))

@ -1,10 +1,5 @@
# Verify _receive_one() quadratic behaviour fixed. # Verify _receive_one() quadratic behaviour fixed.
import subprocess
import time
import socket
import mitogen import mitogen
import mitogen.core import mitogen.core

@ -3,9 +3,7 @@ Measure latency of IPC between two local threads.
""" """
import threading import threading
import time
import mitogen
import mitogen.core import mitogen.core
import mitogen.utils import mitogen.utils
import ansible_mitogen.affinity import ansible_mitogen.affinity

@ -2,8 +2,6 @@
Measure latency of .local() setup. Measure latency of .local() setup.
""" """
import time
import mitogen import mitogen
import mitogen.core import mitogen.core
import mitogen.utils import mitogen.utils

@ -2,7 +2,6 @@
import sys import sys
import os import os
import time
import mitogen.core import mitogen.core

@ -2,9 +2,6 @@
Measure latency of local RPC. Measure latency of local RPC.
""" """
import time
import mitogen
import mitogen.core import mitogen.core
import mitogen.utils import mitogen.utils
import ansible_mitogen.affinity import ansible_mitogen.affinity

@ -1,9 +1,6 @@
""" """
Measure latency of local service RPC. Measure latency of local service RPC.
""" """
import time
import mitogen import mitogen
import mitogen.core import mitogen.core
import mitogen.service import mitogen.service

@ -3,9 +3,7 @@ Measure latency of SSH RPC.
""" """
import sys import sys
import time
import mitogen
import mitogen.core import mitogen.core
import mitogen.utils import mitogen.utils
import ansible_mitogen.affinity import ansible_mitogen.affinity

@ -1,11 +1,7 @@
# Verify throughput over sudo and SSH at various compression levels. # Verify throughput over sudo and SSH at various compression levels.
import os import os
import random
import socket
import subprocess
import tempfile import tempfile
import time
import mitogen import mitogen
import mitogen.core import mitogen.core

@ -1,9 +1,4 @@
import time
import threading
import mock import mock
import unittest2
import testlib import testlib
@ -19,7 +14,7 @@ class ShutdownTest(testlib.TestCase):
broker.poller.close = mock.Mock() broker.poller.close = mock.Mock()
broker.shutdown() broker.shutdown()
broker.join() broker.join()
self.assertEquals(1, len(broker.poller.close.mock_calls)) self.assertEqual(1, len(broker.poller.close.mock_calls))
actual_close() actual_close()
@ -31,7 +26,7 @@ class DeferTest(testlib.TestCase):
broker = self.klass() broker = self.klass()
try: try:
broker.defer(lambda: latch.put(123)) broker.defer(lambda: latch.put(123))
self.assertEquals(123, latch.get()) self.assertEqual(123, latch.get())
finally: finally:
broker.shutdown() broker.shutdown()
broker.join() broker.join()
@ -44,7 +39,7 @@ class DeferTest(testlib.TestCase):
e = self.assertRaises(mitogen.core.Error, e = self.assertRaises(mitogen.core.Error,
lambda: broker.defer(lambda: latch.put(123))) lambda: broker.defer(lambda: latch.put(123)))
self.assertEquals(e.args[0], mitogen.core.Waker.broker_shutdown_msg) self.assertEqual(e.args[0], mitogen.core.Waker.broker_shutdown_msg)
class DeferSyncTest(testlib.TestCase): class DeferSyncTest(testlib.TestCase):
@ -53,8 +48,8 @@ class DeferSyncTest(testlib.TestCase):
def test_okay(self): def test_okay(self):
broker = self.klass() broker = self.klass()
try: try:
th = broker.defer_sync(lambda: threading.currentThread()) th = broker.defer_sync(lambda: mitogen.core.threading__current_thread())
self.assertEquals(th, broker._thread) self.assertEqual(th, broker._thread)
finally: finally:
broker.shutdown() broker.shutdown()
broker.join() broker.join()
@ -67,7 +62,3 @@ class DeferSyncTest(testlib.TestCase):
finally: finally:
broker.shutdown() broker.shutdown()
broker.join() broker.join()
if __name__ == '__main__':
unittest2.main()

@ -1,9 +1,5 @@
import os import os
import mitogen
import unittest2
import testlib import testlib
@ -17,12 +13,8 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
stream = self.router.stream_by_id(context.context_id) stream = self.router.stream_by_id(context.context_id)
argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV')) argv = eval(context.call(os.getenv, 'ORIGINAL_ARGV'))
self.assertEquals(argv[0], buildah_path) self.assertEqual(argv[0], buildah_path)
self.assertEquals(argv[1], 'run') self.assertEqual(argv[1], 'run')
self.assertEquals(argv[2], '--') self.assertEqual(argv[2], '--')
self.assertEquals(argv[3], 'container_name') self.assertEqual(argv[3], 'container_name')
self.assertEquals(argv[4], stream.conn.options.python_path) self.assertEqual(argv[4], stream.conn.options.python_path)
if __name__ == '__main__':
unittest2.main()

@ -1,8 +1,6 @@
import pickle import pickle
import sys import sys
import unittest2
import mitogen.core import mitogen.core
import testlib import testlib
@ -14,25 +12,25 @@ class ConstructorTest(testlib.TestCase):
def test_string_noargs(self): def test_string_noargs(self):
e = self.klass('%s%s') e = self.klass('%s%s')
self.assertEquals(e.args[0], '%s%s') self.assertEqual(e.args[0], '%s%s')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_string_args(self): def test_string_args(self):
e = self.klass('%s%s', 1, 1) e = self.klass('%s%s', 1, 1)
self.assertEquals(e.args[0], '11') self.assertEqual(e.args[0], '11')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_from_exc(self): def test_from_exc(self):
ve = plain_old_module.MyError('eek') ve = plain_old_module.MyError('eek')
e = self.klass(ve) e = self.klass(ve)
self.assertEquals(e.args[0], 'plain_old_module.MyError: eek') self.assertEqual(e.args[0], 'plain_old_module.MyError: eek')
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_form_base_exc(self): def test_form_base_exc(self):
ve = SystemExit('eek') ve = SystemExit('eek')
e = self.klass(ve) e = self.klass(ve)
cls = ve.__class__ cls = ve.__class__
self.assertEquals(e.args[0], self.assertEqual(e.args[0],
# varies across 2/3. # varies across 2/3.
'%s.%s: eek' % (cls.__module__, cls.__name__)) '%s.%s: eek' % (cls.__module__, cls.__name__))
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
@ -50,14 +48,14 @@ class ConstructorTest(testlib.TestCase):
def test_bytestring_conversion(self): def test_bytestring_conversion(self):
e = self.klass(mitogen.core.b('bytes')) e = self.klass(mitogen.core.b('bytes'))
self.assertEquals(u'bytes', e.args[0]) self.assertEqual(u'bytes', e.args[0])
self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType)) self.assertTrue(isinstance(e.args[0], mitogen.core.UnicodeType))
def test_reduce(self): def test_reduce(self):
e = self.klass('eek') e = self.klass('eek')
func, (arg,) = e.__reduce__() func, (arg,) = e.__reduce__()
self.assertTrue(func is mitogen.core._unpickle_call_error) self.assertTrue(func is mitogen.core._unpickle_call_error)
self.assertEquals(arg, e.args[0]) self.assertEqual(arg, e.args[0])
class UnpickleCallErrorTest(testlib.TestCase): class UnpickleCallErrorTest(testlib.TestCase):
@ -73,10 +71,10 @@ class UnpickleCallErrorTest(testlib.TestCase):
def test_reify(self): def test_reify(self):
e = self.func(u'some error') e = self.func(u'some error')
self.assertEquals(mitogen.core.CallError, e.__class__) self.assertEqual(mitogen.core.CallError, e.__class__)
self.assertEquals(1, len(e.args)) self.assertEqual(1, len(e.args))
self.assertEquals(mitogen.core.UnicodeType, type(e.args[0])) self.assertEqual(mitogen.core.UnicodeType, type(e.args[0]))
self.assertEquals(u'some error', e.args[0]) self.assertEqual(u'some error', e.args[0])
class PickleTest(testlib.TestCase): class PickleTest(testlib.TestCase):
@ -85,18 +83,18 @@ class PickleTest(testlib.TestCase):
def test_string_noargs(self): def test_string_noargs(self):
e = self.klass('%s%s') e = self.klass('%s%s')
e2 = pickle.loads(pickle.dumps(e)) e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], '%s%s') self.assertEqual(e2.args[0], '%s%s')
def test_string_args(self): def test_string_args(self):
e = self.klass('%s%s', 1, 1) e = self.klass('%s%s', 1, 1)
e2 = pickle.loads(pickle.dumps(e)) e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], '11') self.assertEqual(e2.args[0], '11')
def test_from_exc(self): def test_from_exc(self):
ve = plain_old_module.MyError('eek') ve = plain_old_module.MyError('eek')
e = self.klass(ve) e = self.klass(ve)
e2 = pickle.loads(pickle.dumps(e)) e2 = pickle.loads(pickle.dumps(e))
self.assertEquals(e2.args[0], 'plain_old_module.MyError: eek') self.assertEqual(e2.args[0], 'plain_old_module.MyError: eek')
def test_from_exc_tb(self): def test_from_exc_tb(self):
try: try:
@ -108,7 +106,3 @@ class PickleTest(testlib.TestCase):
e2 = pickle.loads(pickle.dumps(e)) e2 = pickle.loads(pickle.dumps(e))
self.assertTrue(e2.args[0].startswith('plain_old_module.MyError: eek')) self.assertTrue(e2.args[0].startswith('plain_old_module.MyError: eek'))
self.assertTrue('test_from_exc_tb' in e2.args[0]) self.assertTrue('test_from_exc_tb' in e2.args[0])
if __name__ == '__main__':
unittest2.main()

@ -1,11 +1,7 @@
import logging
import time import time
import unittest2
import mitogen.core import mitogen.core
import mitogen.parent import mitogen.parent
import mitogen.master
from mitogen.core import str_partition from mitogen.core import str_partition
import testlib import testlib
@ -79,7 +75,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
def test_bad_return_value(self): def test_bad_return_value(self):
exc = self.assertRaises(mitogen.core.StreamError, exc = self.assertRaises(mitogen.core.StreamError,
lambda: self.local.call(func_with_bad_return_value)) lambda: self.local.call(func_with_bad_return_value))
self.assertEquals( self.assertEqual(
exc.args[0], exc.args[0],
"cannot unpickle '%s'/'CrazyType'" % (__name__,), "cannot unpickle '%s'/'CrazyType'" % (__name__,),
) )
@ -91,7 +87,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
self.broker.defer(stream.on_disconnect, self.broker) self.broker.defer(stream.on_disconnect, self.broker)
exc = self.assertRaises(mitogen.core.ChannelError, exc = self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get()) lambda: recv.get())
self.assertEquals(exc.args[0], self.router.respondent_disconnect_msg) self.assertEqual(exc.args[0], self.router.respondent_disconnect_msg)
def test_aborted_on_local_broker_shutdown(self): def test_aborted_on_local_broker_shutdown(self):
stream = self.router._stream_by_id[self.local.context_id] stream = self.router._stream_by_id[self.local.context_id]
@ -101,7 +97,7 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
self.broker_shutdown = True self.broker_shutdown = True
exc = self.assertRaises(mitogen.core.ChannelError, exc = self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get()) lambda: recv.get())
self.assertEquals(exc.args[0], self.router.respondent_disconnect_msg) self.assertEqual(exc.args[0], self.router.respondent_disconnect_msg)
def test_accepts_returns_context(self): def test_accepts_returns_context(self):
context = self.local.call(func_returns_arg, self.local) context = self.local.call(func_returns_arg, self.local)
@ -114,10 +110,10 @@ class CallFunctionTest(testlib.RouterMixin, testlib.TestCase):
recv = mitogen.core.Receiver(self.router) recv = mitogen.core.Receiver(self.router)
sender = recv.to_sender() sender = recv.to_sender()
sender2 = self.local.call(func_accepts_returns_sender, sender) sender2 = self.local.call(func_accepts_returns_sender, sender)
self.assertEquals(sender.context.context_id, self.assertEqual(sender.context.context_id,
sender2.context.context_id) sender2.context.context_id)
self.assertEquals(sender.dst_handle, sender2.dst_handle) self.assertEqual(sender.dst_handle, sender2.dst_handle)
self.assertEquals(123, recv.get().unpickle()) self.assertEqual(123, recv.get().unpickle())
self.assertRaises(mitogen.core.ChannelError, self.assertRaises(mitogen.core.ChannelError,
lambda: recv.get().unpickle()) lambda: recv.get().unpickle())
@ -132,19 +128,19 @@ class CallChainTest(testlib.RouterMixin, testlib.TestCase):
def test_subsequent_calls_produce_same_error(self): def test_subsequent_calls_produce_same_error(self):
chain = self.klass(self.local, pipelined=True) chain = self.klass(self.local, pipelined=True)
self.assertEquals('xx', chain.call(func_returns_arg, 'xx')) self.assertEqual('xx', chain.call(func_returns_arg, 'xx'))
chain.call_no_reply(function_that_fails, 'x1') chain.call_no_reply(function_that_fails, 'x1')
e1 = self.assertRaises(mitogen.core.CallError, e1 = self.assertRaises(mitogen.core.CallError,
lambda: chain.call(function_that_fails, 'x2')) lambda: chain.call(function_that_fails, 'x2'))
e2 = self.assertRaises(mitogen.core.CallError, e2 = self.assertRaises(mitogen.core.CallError,
lambda: chain.call(func_returns_arg, 'x3')) lambda: chain.call(func_returns_arg, 'x3'))
self.assertEquals(str(e1), str(e2)) self.assertEqual(str(e1), str(e2))
def test_unrelated_overlapping_failed_chains(self): def test_unrelated_overlapping_failed_chains(self):
c1 = self.klass(self.local, pipelined=True) c1 = self.klass(self.local, pipelined=True)
c2 = self.klass(self.local, pipelined=True) c2 = self.klass(self.local, pipelined=True)
c1.call_no_reply(function_that_fails, 'c1') c1.call_no_reply(function_that_fails, 'c1')
self.assertEquals('yes', c2.call(func_returns_arg, 'yes')) self.assertEqual('yes', c2.call(func_returns_arg, 'yes'))
self.assertRaises(mitogen.core.CallError, self.assertRaises(mitogen.core.CallError,
lambda: c1.call(func_returns_arg, 'yes')) lambda: c1.call(func_returns_arg, 'yes'))
@ -154,7 +150,7 @@ class CallChainTest(testlib.RouterMixin, testlib.TestCase):
e1 = self.assertRaises(mitogen.core.CallError, e1 = self.assertRaises(mitogen.core.CallError,
lambda: c1.call(function_that_fails, 'x2')) lambda: c1.call(function_that_fails, 'x2'))
c1.reset() c1.reset()
self.assertEquals('x3', c1.call(func_returns_arg, 'x3')) self.assertEqual('x3', c1.call(func_returns_arg, 'x3'))
class UnsupportedCallablesTest(testlib.RouterMixin, testlib.TestCase): class UnsupportedCallablesTest(testlib.RouterMixin, testlib.TestCase):
@ -170,21 +166,17 @@ class UnsupportedCallablesTest(testlib.RouterMixin, testlib.TestCase):
closure = lambda: a closure = lambda: a
e = self.assertRaises(TypeError, e = self.assertRaises(TypeError,
lambda: self.local.call(closure)) lambda: self.local.call(closure))
self.assertEquals(e.args[0], self.klass.closures_msg) self.assertEqual(e.args[0], self.klass.closures_msg)
def test_lambda_unsupported(self): def test_lambda_unsupported(self):
lam = lambda: None lam = lambda: None
e = self.assertRaises(TypeError, e = self.assertRaises(TypeError,
lambda: self.local.call(lam)) lambda: self.local.call(lam))
self.assertEquals(e.args[0], self.klass.lambda_msg) self.assertEqual(e.args[0], self.klass.lambda_msg)
def test_instance_method_unsupported(self): def test_instance_method_unsupported(self):
class X: class X:
def x(): pass def x(): pass
e = self.assertRaises(TypeError, e = self.assertRaises(TypeError,
lambda: self.local.call(X().x)) lambda: self.local.call(X().x))
self.assertEquals(e.args[0], self.klass.method_msg) self.assertEqual(e.args[0], self.klass.method_msg)
if __name__ == '__main__':
unittest2.main()

@ -1,5 +1,3 @@
import unittest2
import mitogen.core import mitogen.core
import testlib import testlib
@ -14,7 +12,3 @@ class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
self.assertEqual(chan.dst_handle, 123) self.assertEqual(chan.dst_handle, 123)
self.assertIsNotNone(chan.handle) self.assertIsNotNone(chan.handle)
self.assertGreater(chan.handle, 0) self.assertGreater(chan.handle, 0)
if __name__ == '__main__':
unittest2.main()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save