setup_teardown decorator
Moved out of postdeploy changeset as new framework functionality (Setup and teardown method addition for functions, classmethodsa and object methods). Related-bug: #1519050 Change-Id: Iad9866dbf0c0ed3b3c2bf3bc7c6444617828eef6
This commit is contained in:
parent
1d9da48447
commit
a4ffbe502b
|
@ -501,3 +501,310 @@ def check_repos_management(func):
|
|||
"management on nodes. Please see the debug log.")
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
# Setup/Teardown decorators, which is missing in Proboscis.
|
||||
# Usage: like in Nose.
|
||||
# Python.six is less smart
|
||||
|
||||
|
||||
def __getcallargs(func, *positional, **named):
|
||||
if sys.version_info.major < 3:
|
||||
return inspect.getcallargs(func, *positional, **named)
|
||||
else:
|
||||
return inspect.signature(func).bind(*positional, **named).arguments
|
||||
|
||||
|
||||
def __get_arg_names(func):
|
||||
"""get argument names for function
|
||||
|
||||
:param func: func
|
||||
:return: list of function argnames
|
||||
|
||||
>>> def tst_1():
|
||||
... pass
|
||||
|
||||
>>> __get_arg_names(tst_1)
|
||||
[]
|
||||
|
||||
>>> def tst_2(arg):
|
||||
... pass
|
||||
|
||||
>>> __get_arg_names(tst_2)
|
||||
['arg']
|
||||
"""
|
||||
if sys.version_info.major < 3:
|
||||
return [arg for arg in inspect.getargspec(func=func).args]
|
||||
else:
|
||||
return list(inspect.signature(obj=func).parameters.keys())
|
||||
|
||||
|
||||
def __call_in_context(func, context_args):
|
||||
"""call function with substitute arguments from dict
|
||||
|
||||
:param func: function or None
|
||||
:param context_args: dict
|
||||
:return: function call results
|
||||
|
||||
>>> __call_in_context(None, {})
|
||||
|
||||
>>> def print_print():
|
||||
... print ('print')
|
||||
|
||||
>>> __call_in_context(print_print, {})
|
||||
print
|
||||
|
||||
>>> __call_in_context(print_print, {'val': 1})
|
||||
print
|
||||
|
||||
>>> def print_val(val):
|
||||
... print(val)
|
||||
|
||||
>>> __call_in_context(print_val, {'val': 1})
|
||||
1
|
||||
"""
|
||||
if func is None:
|
||||
return
|
||||
|
||||
func_args = __get_arg_names(func)
|
||||
if not func_args:
|
||||
return func()
|
||||
|
||||
if inspect.ismethod(func) and 'cls' in func_args:
|
||||
func_args.remove('cls')
|
||||
# cls if used in @classmethod and could not be posted
|
||||
# via args or kwargs, so classmethod decorators always has access
|
||||
# to it's own class only, except direct class argument
|
||||
elif 'self' in context_args:
|
||||
context_args.setdefault('cls', context_args['self'].__class__)
|
||||
try:
|
||||
arg_values = [context_args[k] for k in func_args]
|
||||
except KeyError as e:
|
||||
raise ValueError("Argument '{}' is missing".format(str(e)))
|
||||
|
||||
return func(*arg_values)
|
||||
|
||||
|
||||
def setup_teardown(setup=None, teardown=None):
|
||||
"""Add setup and teardown for functions and methods.
|
||||
|
||||
:param setup: function
|
||||
:param teardown: function
|
||||
:return:
|
||||
|
||||
>>> def setup_func():
|
||||
... print('setup_func called')
|
||||
|
||||
>>> def teardown_func():
|
||||
... print('teardown_func called')
|
||||
|
||||
>>> @setup_teardown(setup=setup_func, teardown=teardown_func)
|
||||
... def positive_example(arg):
|
||||
... print(arg)
|
||||
|
||||
>>> positive_example(arg=1)
|
||||
setup_func called
|
||||
1
|
||||
teardown_func called
|
||||
|
||||
>>> def print_call(text):
|
||||
... print (text)
|
||||
|
||||
>>> @setup_teardown(
|
||||
... setup=lambda: print_call('setup lambda'),
|
||||
... teardown=lambda: print_call('teardown lambda'))
|
||||
... def positive_example_lambda(arg):
|
||||
... print(arg)
|
||||
|
||||
>>> positive_example_lambda(arg=1)
|
||||
setup lambda
|
||||
1
|
||||
teardown lambda
|
||||
|
||||
>>> def setup_with_self(self):
|
||||
... print(
|
||||
... 'setup_with_self: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=self.cls_val, val=self.val))
|
||||
|
||||
>>> def teardown_with_self(self):
|
||||
... print(
|
||||
... 'teardown_with_self: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=self.cls_val, val=self.val))
|
||||
|
||||
>>> def setup_with_cls(cls):
|
||||
... print(
|
||||
... 'setup_with_cls: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls.cls_val))
|
||||
|
||||
>>> def teardown_with_cls(cls):
|
||||
... print('teardown_with_cls: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls.cls_val))
|
||||
|
||||
>>> class HelpersBase(object):
|
||||
... cls_val = None
|
||||
... def __init__(self):
|
||||
... self.val = None
|
||||
... @classmethod
|
||||
... def cls_setup(cls):
|
||||
... print(
|
||||
... 'cls_setup: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls.cls_val))
|
||||
... @classmethod
|
||||
... def cls_teardown(cls):
|
||||
... print(
|
||||
... 'cls_teardown: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls.cls_val))
|
||||
... def self_setup(self):
|
||||
... print(
|
||||
... 'self_setup: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=self.cls_val, val=self.val))
|
||||
... def self_teardown(self):
|
||||
... print(
|
||||
... 'self_teardown: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=self.cls_val, val=self.val))
|
||||
|
||||
>>> class Test(HelpersBase):
|
||||
... @setup_teardown(
|
||||
... setup=HelpersBase.self_setup,
|
||||
... teardown=HelpersBase.self_teardown)
|
||||
... def test_self_self(self, cls_val=0, val=0):
|
||||
... print(
|
||||
... 'test_self_self: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=cls_val, val=val))
|
||||
... self.val = val
|
||||
... self.cls_val = cls_val
|
||||
... @setup_teardown(
|
||||
... setup=HelpersBase.cls_setup,
|
||||
... teardown=HelpersBase.cls_teardown)
|
||||
... def test_self_cls(self, cls_val=1, val=1):
|
||||
... print(
|
||||
... 'test_self_cls: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=cls_val, val=val))
|
||||
... self.val = val
|
||||
... self.cls_val = cls_val
|
||||
... @setup_teardown(
|
||||
... setup=setup_func,
|
||||
... teardown=teardown_func)
|
||||
... def test_self_none(self, cls_val=2, val=2):
|
||||
... print(
|
||||
... 'test_self_cls: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=cls_val, val=val))
|
||||
... self.val = val
|
||||
... self.cls_val = cls_val
|
||||
... @setup_teardown(
|
||||
... setup=setup_with_self,
|
||||
... teardown=teardown_with_self)
|
||||
... def test_self_ext_self(self, cls_val=-1, val=-1):
|
||||
... print(
|
||||
... 'test_self_ext_self: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=cls_val, val=val))
|
||||
... self.val = val
|
||||
... self.cls_val = cls_val
|
||||
... @setup_teardown(
|
||||
... setup=setup_with_cls,
|
||||
... teardown=teardown_with_cls)
|
||||
... def test_self_ext_cls(self, cls_val=-2, val=-2):
|
||||
... print(
|
||||
... 'test_self_ext_cls: '
|
||||
... 'self.cls_val = {cls_val!s}, self.val = {val!s}'.format(
|
||||
... cls_val=cls_val, val=val))
|
||||
... self.val = val
|
||||
... self.cls_val = cls_val
|
||||
... @classmethod
|
||||
... @setup_teardown(
|
||||
... setup=HelpersBase.cls_setup,
|
||||
... teardown=HelpersBase.cls_teardown)
|
||||
... def test_cls_cls(cls, cls_val=3):
|
||||
... print(
|
||||
... 'test_cls_cls: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls_val))
|
||||
... cls.cls_val = cls_val
|
||||
... @classmethod
|
||||
... @setup_teardown(
|
||||
... setup=setup_func,
|
||||
... teardown=teardown_func)
|
||||
... def test_cls_none(cls, cls_val=4):
|
||||
... print(
|
||||
... 'test_cls_none: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls_val))
|
||||
... cls.cls_val = cls_val
|
||||
... @classmethod
|
||||
... @setup_teardown(
|
||||
... setup=setup_with_cls,
|
||||
... teardown=teardown_with_cls)
|
||||
... def test_cls_ext_cls(cls, cls_val=-3):
|
||||
... print(
|
||||
... 'test_self_ext_cls: cls.cls_val = {cls_val!s}'.format(
|
||||
... cls_val=cls_val))
|
||||
... cls.cls_val = cls_val
|
||||
... @staticmethod
|
||||
... @setup_teardown(setup=setup_func, teardown=teardown_func)
|
||||
... def test_none_none():
|
||||
... print('test')
|
||||
|
||||
>>> test = Test()
|
||||
|
||||
>>> test.test_self_self()
|
||||
self_setup: self.cls_val = None, self.val = None
|
||||
test_self_self: self.cls_val = 0, self.val = 0
|
||||
self_teardown: self.cls_val = 0, self.val = 0
|
||||
|
||||
>>> test.test_self_cls()
|
||||
cls_setup: cls.cls_val = None
|
||||
test_self_cls: self.cls_val = 1, self.val = 1
|
||||
cls_teardown: cls.cls_val = None
|
||||
|
||||
>>> test.test_self_none()
|
||||
setup_func called
|
||||
test_self_cls: self.cls_val = 2, self.val = 2
|
||||
teardown_func called
|
||||
|
||||
>>> test.test_self_ext_self()
|
||||
setup_with_self: self.cls_val = 2, self.val = 2
|
||||
test_self_ext_self: self.cls_val = -1, self.val = -1
|
||||
teardown_with_self: self.cls_val = -1, self.val = -1
|
||||
|
||||
>>> test.test_self_ext_cls()
|
||||
setup_with_cls: cls.cls_val = None
|
||||
test_self_ext_cls: self.cls_val = -2, self.val = -2
|
||||
teardown_with_cls: cls.cls_val = None
|
||||
|
||||
>>> test.test_cls_cls()
|
||||
cls_setup: cls.cls_val = None
|
||||
test_cls_cls: cls.cls_val = 3
|
||||
cls_teardown: cls.cls_val = None
|
||||
|
||||
>>> test.test_cls_none()
|
||||
setup_func called
|
||||
test_cls_none: cls.cls_val = 4
|
||||
teardown_func called
|
||||
|
||||
>>> test.test_cls_ext_cls()
|
||||
setup_with_cls: cls.cls_val = 4
|
||||
test_self_ext_cls: cls.cls_val = -3
|
||||
teardown_with_cls: cls.cls_val = -3
|
||||
|
||||
>>> test.test_none_none()
|
||||
setup_func called
|
||||
test
|
||||
teardown_func called
|
||||
"""
|
||||
def real_decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
real_args = __getcallargs(func, *args, **kwargs)
|
||||
__call_in_context(setup, real_args)
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
finally:
|
||||
__call_in_context(teardown, real_args)
|
||||
return result
|
||||
return wrapper
|
||||
return real_decorator
|
||||
|
|
Loading…
Reference in New Issue