From 0f2de5f951c26e9f9a4cfe9720ca97fd55bd9081 Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Fri, 7 Feb 2020 16:30:04 +0000 Subject: [PATCH] util/exec_control: add once_per_attribute_value Add a decorator to run a method once for all instances that share the value of the specified attribute. --- tests/test_exec_control.py | 42 +++++++++++++++++++++++++++++++++++++- wa/utils/exec_control.py | 24 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/tests/test_exec_control.py b/tests/test_exec_control.py index cc49a2ea..178dfefb 100644 --- a/tests/test_exec_control.py +++ b/tests/test_exec_control.py @@ -21,7 +21,8 @@ from nose.tools import assert_equal, assert_raises from wa.utils.exec_control import (init_environment, reset_environment, activate_environment, once, - once_per_class, once_per_instance) + once_per_class, once_per_instance, + once_per_attribute_value) class MockClass(object): @@ -110,6 +111,18 @@ class AnotherClass(object): self.count += 1 +class NamedClass: + + count = 0 + + def __init__(self, name): + self.name = name + + @once_per_attribute_value('name') + def initilize(self): + NamedClass.count += 1 + + class AnotherSubClass(MockClass): def __init__(self): @@ -352,3 +365,30 @@ class OncePerInstanceEnvironmentTest(TestCase): asc.initilize_once_per_instance() asc.initilize_once_per_instance() assert_equal(asc.count, 2) + + +class OncePerAttributeValueTest(TestCase): + + def setUp(self): + activate_environment('TEST_ENVIRONMENT') + + def tearDown(self): + reset_environment('TEST_ENVIRONMENT') + + def test_once_attribute_value(self): + classes = [ + NamedClass('Rick'), + NamedClass('Morty'), + NamedClass('Rick'), + NamedClass('Morty'), + NamedClass('Morty'), + NamedClass('Summer'), + ] + + for c in classes: + c.initilize() + + for c in classes: + c.initilize() + + assert_equal(NamedClass.count, 3) diff --git a/wa/utils/exec_control.py b/wa/utils/exec_control.py index 6322f2c2..ba4bd194 100644 --- a/wa/utils/exec_control.py +++ b/wa/utils/exec_control.py @@ -105,6 +105,30 @@ def once_per_class(method): return wrapper +def once_per_attribute_value(attr_name): + """ + The specified method will be invoked once for all instances that share the + same value for the specified attribute (sameness is established by comparing + repr() of the values). + """ + def wrapped_once_per_attribute_value(method): + def wrapper(*args, **kwargs): + if __active_environment is None: + activate_environment('default') + + attr_value = getattr(args[0], attr_name) + func_id = repr(method.__name__) + repr(args[0].__class__) + repr(attr_value) + + if func_id in __environments[__active_environment]: + return + else: + __environments[__active_environment].append(func_id) + return method(*args, **kwargs) + + return wrapper + return wrapped_once_per_attribute_value + + def once(method): """ The specified method will be invoked only once within the