Skip to content

Commit

Permalink
Merge pull request #719 from s1113950/issue672
Browse files Browse the repository at this point in the history
Resolves ansible 2.9+, Mitogen, and Python 3.5 setup module issue
  • Loading branch information
s1113950 authored Jun 15, 2020
2 parents 67f1ca9 + 81076c9 commit 3a52b44
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 9 deletions.
28 changes: 27 additions & 1 deletion ansible_mitogen/planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ def __init__(self, action, connection, module_name, module_args,
#: Initially ``None``, but set by :func:`invoke`. The raw source or
#: binary contents of the module.
self._module_source = None
#: Initially ``{}``, but set by :func:`invoke`. Optional source to send
#: to :func:`propagate_paths_and_modules` to fix Python3.5 relative import errors
self._overridden_sources = {}

def get_module_source(self):
if self._module_source is None:
Expand Down Expand Up @@ -476,6 +479,7 @@ def _propagate_deps(invocation, planner, context):
context=context,
paths=planner.get_push_files(),
modules=planner.get_module_deps(),
overridden_sources=invocation._overridden_sources
)


Expand Down Expand Up @@ -533,6 +537,26 @@ def _get_planner(name, path, source):
raise ansible.errors.AnsibleError(NO_METHOD_MSG + repr(invocation))


def _fix_py35(invocation, module_source):
"""
super edge case with a relative import error in Python 3.5.1-3.5.3
in Ansible's setup module when using Mitogen
https://github.com/dw/mitogen/issues/672#issuecomment-636408833
We replace a relative import in the setup module with the actual full file path
This works in vanilla Ansible but not in Mitogen otherwise
"""
if invocation.module_name == 'setup' and \
invocation.module_path not in invocation._overridden_sources:
# in-memory replacement of setup module's relative import
# would check for just python3.5 and run this then but we don't know the
# target python at this time yet
module_source = module_source.replace(
b"from ...module_utils.basic import AnsibleModule",
b"from ansible.module_utils.basic import AnsibleModule"
)
invocation._overridden_sources[invocation.module_path] = module_source


def invoke(invocation):
"""
Find a Planner subclass corresponding to `invocation` and use it to invoke
Expand All @@ -555,10 +579,12 @@ def invoke(invocation):

invocation.module_path = mitogen.core.to_text(path)
if invocation.module_path not in _planner_by_path:
module_source = invocation.get_module_source()
_fix_py35(invocation, module_source)
_planner_by_path[invocation.module_path] = _get_planner(
invocation.module_name,
invocation.module_path,
invocation.get_module_source()
module_source
)

planner = _planner_by_path[invocation.module_path](invocation)
Expand Down
29 changes: 21 additions & 8 deletions mitogen/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,28 +746,41 @@ def _forward(self, context, path):
'paths': list,
'modules': list,
})
def propagate_paths_and_modules(self, context, paths, modules):
def propagate_paths_and_modules(self, context, paths, modules, overridden_sources=None):
"""
One size fits all method to ensure a target context has been preloaded
with a set of small files and Python modules.
overridden_sources: optional dict containing source code to override path's source code
"""
for path in paths:
self.propagate_to(context, mitogen.core.to_text(path))
overridden_source = None
if overridden_sources is not None and path in overridden_sources:
overridden_source = overridden_sources[path]
self.propagate_to(context, mitogen.core.to_text(path), overridden_source)
#self.router.responder.forward_modules(context, modules) TODO

@expose(policy=AllowParents())
@arg_spec({
'context': mitogen.core.Context,
'path': mitogen.core.FsPathTypes,
})
def propagate_to(self, context, path):
def propagate_to(self, context, path, overridden_source=None):
"""
If the optional parameter 'overridden_source' is passed, use
that instead of the path's code as source code. This works around some bugs
of source modules such as relative imports on unsupported Python versions
"""
if path not in self._cache:
LOG.debug('caching small file %s', path)
fp = open(path, 'rb')
try:
self._cache[path] = mitogen.core.Blob(fp.read())
finally:
fp.close()
if overridden_source is None:
fp = open(path, 'rb')
try:
self._cache[path] = mitogen.core.Blob(fp.read())
finally:
fp.close()
else:
self._cache[path] = mitogen.core.Blob(overridden_source)
self._forward(context, path)

@expose(policy=AllowParents())
Expand Down

0 comments on commit 3a52b44

Please sign in to comment.