Validate modules arg spec fixes (#34477)

* Update validate-modules arg_spec introspection to be faster, by only mocking the imports we explicitly list
* The use of types.MethodType in redhat_subscription wasn't py3 compatible, use partial instead
* Remove argument_spec import hacks, make them errors, we can ignore them with ansible-test
* Enable the --arg-spec flag for validate-modules
This commit is contained in:
Matt Martz 2018-01-11 17:41:53 -06:00 committed by Matt Clay
parent 42a0d71413
commit dcc05093db
6 changed files with 149 additions and 63 deletions

View file

@ -19,20 +19,15 @@
import imp
import sys
from modulefinder import ModuleFinder
from contextlib import contextmanager
import mock
from ansible.module_utils.six import reraise
MODULE_CLASSES = [
'ansible.module_utils.basic.AnsibleModule',
'ansible.module_utils.vca.VcaAnsibleModule',
'ansible.module_utils.network.nxos.nxos.NetworkModule',
'ansible.module_utils.network.eos.eos.NetworkModule',
'ansible.module_utils.network.ios.ios.NetworkModule',
'ansible.module_utils.network.iosxr.iosxr.NetworkModule',
'ansible.module_utils.network.junos.junos.NetworkModule',
'ansible.module_utils.openswitch.NetworkModule',
]
@ -40,6 +35,11 @@ class AnsibleModuleCallError(RuntimeError):
pass
class AnsibleModuleImportError(ImportError):
pass
@contextmanager
def add_mocks(filename):
gp = mock.patch('ansible.module_utils.basic.get_platform').start()
gp.return_value = 'linux'
@ -48,64 +48,31 @@ def add_mocks(filename):
mocks = []
for module_class in MODULE_CLASSES:
mocks.append(
mock.patch('ansible.module_utils.basic.AnsibleModule',
new=module_mock)
mock.patch('%s.__init__' % module_class, new=module_mock)
)
for m in mocks:
p = m.start()
p.side_effect = AnsibleModuleCallError()
p.side_effect = AnsibleModuleCallError('AnsibleModuleCallError')
mocks.append(
mock.patch('ansible.module_utils.basic._load_params').start()
)
finder = ModuleFinder()
try:
finder.run_script(filename)
except:
pass
yield module_mock
sys_mock = mock.MagicMock()
sys_mock.__version__ = '0.0.0'
sys_mocks = []
for module, sources in finder.badmodules.items():
if module in sys.modules:
continue
if [s for s in sources if s[:7] in ['ansible', '__main_']]:
parts = module.split('.')
for i in range(len(parts)):
dotted = '.'.join(parts[:i + 1])
# Never mock out ansible or ansible.module_utils
# we may get here if a specific module_utils file needed mocked
if dotted in ('ansible', 'ansible.module_utils',):
continue
sys.modules[dotted] = sys_mock
sys_mocks.append(dotted)
return module_mock, mocks, sys_mocks
def remove_mocks(mocks, sys_mocks):
for m in mocks:
m.stop()
for m in sys_mocks:
try:
del sys.modules[m]
except KeyError:
pass
def get_argument_spec(filename):
module_mock, mocks, sys_mocks = add_mocks(filename)
try:
mod = imp.load_source('module', filename)
if not module_mock.call_args:
mod.main()
except AnsibleModuleCallError:
pass
except Exception:
# We can probably remove this branch, it is here for use while testing
pass
remove_mocks(mocks, sys_mocks)
with add_mocks(filename) as module_mock:
try:
mod = imp.load_source('module', filename)
if not module_mock.call_args:
mod.main()
except AnsibleModuleCallError:
pass
except Exception as e:
reraise(AnsibleModuleImportError, AnsibleModuleImportError('%s' % e), sys.exc_info()[2])
try:
args, kwargs = module_mock.call_args