From 0e0d22dbba5a52e27d3aab320afcdd721cdc0e7d Mon Sep 17 00:00:00 2001 From: Lauro Neto Date: Fri, 8 Jan 2010 17:40:49 -0300 Subject: Adding metaclass for tests - DocModifier --- tests/util/helper/docmodifier.py | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/util/helper/docmodifier.py (limited to 'tests/util') diff --git a/tests/util/helper/docmodifier.py b/tests/util/helper/docmodifier.py new file mode 100644 index 000000000..6df8c1145 --- /dev/null +++ b/tests/util/helper/docmodifier.py @@ -0,0 +1,89 @@ + +'''Helper metaclass do 'decorate' docstrings from base test case classes''' + +import new + + +def copy_func(func): + '''Helper function to copy a function object (except docstring)''' + return new.function(func.func_code, func.func_globals, func.func_name, + func.func_defaults, func.func_closure) + + +class DocModifier(type): + '''Metaclass for modifiying method documentation. + + It allows the managed class to modify the method documentation, adding + prefixes and suffixes to a given set of methods. + + For example, you have some unittest.TestCases that run on different set + of data. These methods could be written once in a base class and the + inheriting class just setup different setUp/tearDown methods with the + proper data. To differentiate the methods when using docstrings, you + can add a suffix or prefix to the docstring. + + Variables in the implementing class: + + doc_prefix - The prefix to the docstring + doc_suffix - The suffix to the docstring + doc_filter - The function to filter the methods. If not provided, this + no docstrings are changed.''' + + def __new__(mcs, name, bases, dct): + + # FIXME currently we have to define doc_filter on each subclass + filter_function = dct.get('doc_filter') + if not filter_function: + filter_function = lambda x: False + + for base in bases: + for attr in [x for x in base.__dict__ if filter_function(x)]: + original = getattr(base, attr) + + if original.__doc__: + copy = copy_func(original) + copy.__doc__ = (dct.get('doc_prefix', '') + + original.__doc__ + + dct.get('doc_suffix', '')) + dct[attr] = copy + + return type.__new__(mcs, name, bases, dct) + + def __init__(mcs, name, bases, dct): + super(DocModifier, mcs).__init__(name, bases, dct) + + +if __name__ == '__main__': + + # tests + class BaseTest(object): + __metaclass__ = DocModifier + + def testBase(self): + '''base''' + + def testWithoutDoc(self): + pass + + class Implementing(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_prefix = 'prefix' + doc_suffix = 'suffix' + + class OnlyPrefix(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_prefix = 'prefix' + + class OnlySuffix(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_suffix = 'suffix' + + assert(Implementing.testBase.__doc__ == 'prefixbasesuffix') + assert(Implementing.testWithoutDoc.__doc__ == None) + assert(OnlySuffix.testBase.__doc__ == 'basesuffix') + assert(OnlySuffix.testWithoutDoc.__doc__ == None) + assert(OnlyPrefix.testBase.__doc__ == 'prefixbase') + assert(OnlyPrefix.testWithoutDoc.__doc__ == None) -- cgit v1.2.3