diff --git a/CHANGELOG.md b/CHANGELOG.md index edc031fa08f555bf3b0dcff7531103efdc48ed9a..e0740ef9ed9814ba6f50dc70d6da3d2552053586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ # v2.18.4 * added `map_through` to `unpack_dict` +* added `execute_if_attribute_none` +* added `execute_if_attribute_not_none` diff --git a/docs/coding/decorators.rst b/docs/coding/decorators.rst index e743c13814a3154195e1a870311a7534a6cd4404..52020b492423541999125e7431a576aa3276fbb9 100644 --- a/docs/coding/decorators.rst +++ b/docs/coding/decorators.rst @@ -1,6 +1,10 @@ Decorators ========== +.. autofunction:: satella.coding.decorators.execute_if_attribute_none + +.. autofunction:: satella.coding.decorators.execute_if_attribute_not_none + .. autofunction:: satella.coding.decorators.transform_result .. autofunction:: satella.coding.decorators.transform_arguments diff --git a/satella/__init__.py b/satella/__init__.py index f9ba0d3c5b94f2c7863d6a95392d13104eba039b..2d29c1ea52f58e3fe8247706a9a0b13c0d800a15 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.18.4a2' +__version__ = '2.18.4' diff --git a/satella/coding/decorators/__init__.py b/satella/coding/decorators/__init__.py index 5c5784e14a38dfc4aed627e0838887d2649d52c6..3fc063c0f938cee8d73029c01e7754f7d7d9db98 100644 --- a/satella/coding/decorators/__init__.py +++ b/satella/coding/decorators/__init__.py @@ -1,6 +1,6 @@ from .arguments import auto_adapt_to_methods, attach_arguments, for_argument, \ execute_before, copy_arguments, replace_argument_if, transform_result, \ - transform_arguments + transform_arguments, execute_if_attribute_none, execute_if_attribute_not_none from .decorators import wraps, chain_functions, has_keys, short_none, memoize, return_as_list, \ default_return, cache_memoize, call_method_on_exception from .flow_control import loop_while, queue_get @@ -12,4 +12,5 @@ __all__ = ['retry', 'transform_result', 'transform_arguments', 'chain_functions', 'has_keys', 'short_none', 'auto_adapt_to_methods', 'attach_arguments', 'for_argument', 'loop_while', 'memoize', 'copy_arguments', 'replace_argument_if', 'return_as_list', - 'default_return', 'cache_memoize', 'call_method_on_exception'] + 'default_return', 'cache_memoize', 'call_method_on_exception', + 'execute_if_attribute_none', 'execute_if_attribute_not_none'] diff --git a/satella/coding/decorators/arguments.py b/satella/coding/decorators/arguments.py index e415ed5bbf252e186ba0e3690b075971187e50a4..e7fa1308811e391abd596cff94b292560ddd98de 100644 --- a/satella/coding/decorators/arguments.py +++ b/satella/coding/decorators/arguments.py @@ -373,3 +373,40 @@ def for_argument(*t_ops: ForArgumentArg, **t_kwops: ForArgumentArg): return inner return outer + + +def execute_if_attribute_none(attribute: str): + """ + Decorator for instancemethods. + + This will execute the function only if provided attribute is None. + Otherwise it will return a None + + :param attribute: name of the attribute to check + """ + def outer(fun): + @wraps(fun) + def inner(self, *args, **kwargs): + if getattr(self, attribute) is None: + return fun(self, *args, **kwargs) + return inner + return outer + + +def execute_if_attribute_not_none(attribute: str): + """ + Decorator for instancemethods. + + This will execute the function only if provided attribute is not None. + Otherwise it will return a None + + :param attribute: name of the attribute to check + """ + def outer(fun): + @wraps(fun) + def inner(self, *args, **kwargs): + if getattr(self, attribute) is not None: + return fun(self, *args, **kwargs) + return inner + return outer + diff --git a/tests/test_coding/test_decorators.py b/tests/test_coding/test_decorators.py index 0826265ac6c18b22c835d9c5d0168be29f9c8fb1..425f384fcb756ca0dc63325f391a137a297b2623 100644 --- a/tests/test_coding/test_decorators.py +++ b/tests/test_coding/test_decorators.py @@ -9,7 +9,8 @@ from satella.coding import wraps, chain_functions, postcondition, \ from satella.coding.decorators import auto_adapt_to_methods, attach_arguments, \ execute_before, loop_while, memoize, copy_arguments, replace_argument_if, \ retry, return_as_list, default_return, transform_result, transform_arguments, \ - cache_memoize, call_method_on_exception + cache_memoize, call_method_on_exception, execute_if_attribute_none, \ + execute_if_attribute_not_none from satella.coding.predicates import x from satella.exceptions import PreconditionError @@ -18,6 +19,42 @@ logger = logging.getLogger(__name__) class TestDecorators(unittest.TestCase): + def test_execute_if_attribute_not_none(self): + class ExecIfAttrNone: + def __init__(self): + self.attr = None + self.called = 0 + + @execute_if_attribute_not_none('attr') + def run_me(self): + self.called += 1 + + e = ExecIfAttrNone() + self.assertEqual(e.called, 0) + e.run_me() + self.assertEqual(e.called, 0) + e.attr = 2 + e.run_me() + self.assertEqual(e.called, 1) + + def test_execute_if_attribute_none(self): + class ExecIfAttrNone: + def __init__(self): + self.attr = None + self.called = 0 + + @execute_if_attribute_none('attr') + def run_me(self): + self.called += 1 + + e = ExecIfAttrNone() + self.assertEqual(e.called, 0) + e.run_me() + self.assertEqual(e.called, 1) + e.attr = 2 + e.run_me() + self.assertEqual(e.called, 1) + def test_call_method_on_exception(self): a = {'called': False} slf = self