First swing at making module_common.py more v2-ish

This commit is contained in:
Toshio Kuratomi 2015-02-10 12:35:34 -08:00
parent 0e834509c8
commit 01a6081b49
5 changed files with 134 additions and 128 deletions

View file

@ -1,4 +1,5 @@
# (c) 2013-2014, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2015 Toshio Kuratomi <tkuratomi@ansible.com>
#
# This file is part of Ansible
#
@ -15,6 +16,10 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
# from python and deps
from cStringIO import StringIO
import inspect
@ -34,48 +39,19 @@ REPLACER_COMPLEX = "\"<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>\""
REPLACER_WINDOWS = "# POWERSHELL_COMMON"
REPLACER_VERSION = "\"<<ANSIBLE_VERSION>>\""
class ModuleReplacer(object):
# We could end up writing out parameters with unicode characters so we need to
# specify an encoding for the python source file
ENCODING_STRING = '# -*- coding: utf-8 -*-'
"""
The Replacer is used to insert chunks of code into modules before
transfer. Rather than doing classical python imports, this allows for more
efficient transfer in a no-bootstrapping scenario by not moving extra files
over the wire, and also takes care of embedding arguments in the transferred
modules.
# TODO: Is there a reason we don't use __file__ here?
# Will this fail if ansible is run from an egg/wheel? Do we care?
_THIS_FILE = inspect.getfile(inspect.currentframe())
# we've moved the module_common relative to the snippets, so fix the path
_SNIPPET_PATH = os.path.join(os.path.dirname(this_file), '..', 'module_utils')
This version is done in such a way that local imports can still be
used in the module code, so IDEs don't have to be aware of what is going on.
# ******************************************************************************
Example:
from ansible.module_utils.basic import *
... will result in the insertion basic.py into the module
from the module_utils/ directory in the source tree.
All modules are required to import at least basic, though there will also
be other snippets.
# POWERSHELL_COMMON
Also results in the inclusion of the common code in powershell.ps1
"""
# ******************************************************************************
def __init__(self, strip_comments=False):
# FIXME: these members need to be prefixed with '_' and the rest of the file fixed
this_file = inspect.getfile(inspect.currentframe())
# we've moved the module_common relative to the snippets, so fix the path
self.snippet_path = os.path.join(os.path.dirname(this_file), '..', 'module_utils')
self.strip_comments = strip_comments
# ******************************************************************************
def slurp(self, path):
def _slurp(path):
if not os.path.exists(path):
raise AnsibleError("imported module support code does not exist at %s" % path)
fd = open(path)
@ -83,7 +59,7 @@ class ModuleReplacer(object):
fd.close()
return data
def _find_snippet_imports(self, module_data, module_path):
def _find_snippet_imports(module_data, module_path, strip_comments):
"""
Given the source of the module, convert it to a Jinja2 template to insert
module code and return whether it's a new or old style module.
@ -104,10 +80,10 @@ class ModuleReplacer(object):
for line in lines:
if REPLACER in line:
output.write(self.slurp(os.path.join(self.snippet_path, "basic.py")))
output.write(_slurp(os.path.join(_SNIPPET_PATH, "basic.py")))
snippet_names.append('basic')
if REPLACER_WINDOWS in line:
ps_data = self.slurp(os.path.join(self.snippet_path, "powershell.ps1"))
ps_data = _slurp(os.path.join(_SNIPPET_PATH, "powershell.ps1"))
output.write(ps_data)
snippet_names.append('powershell')
elif line.startswith('from ansible.module_utils.'):
@ -121,9 +97,9 @@ class ModuleReplacer(object):
raise AnsibleError("error importing module in %s, expecting format like 'from ansible.module_utils.basic import *'" % module_path)
snippet_name = tokens[2].split()[0]
snippet_names.append(snippet_name)
output.write(self.slurp(os.path.join(self.snippet_path, snippet_name + ".py")))
output.write(_slurp(os.path.join(_SNIPPET_PATH, snippet_name + ".py")))
else:
if self.strip_comments and line.startswith("#") or line == '':
if strip_comments and line.startswith("#") or line == '':
pass
output.write(line)
output.write("\n")
@ -139,16 +115,42 @@ class ModuleReplacer(object):
return (output.getvalue(), module_style)
# ******************************************************************************
# ******************************************************************************
def modify_module(self, module_path, module_args):
def modify_module(module_path, module_args, strip_comments=False):
"""
Used to insert chunks of code into modules before transfer rather than
doing regular python imports. This allows for more efficient transfer in
a non-bootstrapping scenario by not moving extra files over the wire and
also takes care of embedding arguments in the transferred modules.
This version is done in such a way that local imports can still be
used in the module code, so IDEs don't have to be aware of what is going on.
Example:
from ansible.module_utils.basic import *
... will result in the insertion of basic.py into the module
from the module_utils/ directory in the source tree.
All modules are required to import at least basic, though there will also
be other snippets.
For powershell, there's equivalent conventions like this:
# POWERSHELL_COMMON
which results in the inclusion of the common code from powershell.ps1
"""
with open(module_path) as f:
# read in the module source
module_data = f.read()
(module_data, module_style) = self._find_snippet_imports(module_data, module_path)
(module_data, module_style) = _find_snippet_imports(module_data, module_path, strip_comments)
#module_args_json = jsonify(module_args)
module_args_json = json.dumps(module_args)
@ -167,7 +169,7 @@ class ModuleReplacer(object):
# facility = inject['ansible_syslog_facility']
# module_data = module_data.replace('syslog.LOG_USER', "syslog.%s" % facility)
lines = module_data.split("\n")
lines = module_data.split("\n", 1)
shebang = None
if lines[0].startswith("#!"):
shebang = lines[0].strip()
@ -178,7 +180,12 @@ class ModuleReplacer(object):
# FIXME: more inject stuff here...
#if interpreter_config in inject:
# lines[0] = shebang = "#!%s %s" % (inject[interpreter_config], " ".join(args[1:]))
# module_data = "\n".join(lines)
lines.insert(1, ENCODING_STRING)
else:
lines.insert(0, ENCODING_STRING)
module_data = "\n".join(lines)
return (module_data, module_style, shebang)

View file

@ -43,7 +43,7 @@ BOOLEANS = BOOLEANS_TRUE + BOOLEANS_FALSE
# can be inserted in any module source automatically by including
# #<<INCLUDE_ANSIBLE_MODULE_COMMON>> on a blank line by itself inside
# of an ansible module. The source of this common code lives
# in lib/ansible/module_common.py
# in ansible/executor/module_common.py
import locale
import os

@ -1 +1 @@
Subproject commit 34784b7a617aa35d3b994c9f0795567afc6fb0b0
Subproject commit 095f8681dbdfd2e9247446822e953287c9bca66c

View file

@ -29,7 +29,7 @@ import time
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.executor.module_common import ModuleReplacer
from ansible.executor.module_common import modify_module
from ansible.parsing.utils.jsonify import jsonify
from ansible.plugins import shell_loader
@ -78,7 +78,7 @@ class ActionBase:
def _configure_module(self, module_name, module_args):
'''
Handles the loading and templating of the module code through the
ModuleReplacer class.
modify_module() function.
'''
# Search module path(s) for named module.
@ -94,7 +94,7 @@ class ActionBase:
"run 'git submodule update --init --recursive' to correct this problem." % (module_name))
# insert shared code and arguments into the module
(module_data, module_style, module_shebang) = ModuleReplacer().modify_module(module_path, module_args)
(module_data, module_style, module_shebang) = modify_module(module_path, module_args)
return (module_style, module_shebang, module_data)

View file

@ -34,8 +34,9 @@ import os
import subprocess
import traceback
import optparse
import ansible.utils as utils
import ansible.module_common as module_common
from ansible import utils
from ansible import module_common
import ansible.constants as C
try:
@ -87,8 +88,6 @@ def boilerplate_module(modfile, args, interpreter, check):
#module_data = module_fh.read()
#module_fh.close()
replacer = module_common.ModuleReplacer()
#included_boilerplate = module_data.find(module_common.REPLACER) != -1 or module_data.find("import ansible.module_utils") != -1
complex_args = {}
@ -116,7 +115,7 @@ def boilerplate_module(modfile, args, interpreter, check):
if check:
complex_args['CHECKMODE'] = True
(module_data, module_style, shebang) = replacer.modify_module(
(module_data, module_style, shebang) = module_common.modify_module(
modfile,
complex_args,
args,