From: Vratislav Podzimek vpodzime@redhat.com
Sometimes we need to prevent a method from being called or property from being evaluated if some conditition is not satisfied. The most common case is to check value of some property of the given object. For example, it only makes sense to call the lv.merge() method if 'lv' is a snapshot. Things like this can be facilitated by a decorator that makes sure 'lv' is snapshot for example by checking its 'is_snapshot' property. The code then looks like this:
@requires_property("is_snapshot") def merge(self): # the actual merge goes here
On an LV that is not a snapshot a ValueError is raised.
Of course, the decorator can be made even more generic if it turns out in the future we need it to be. --- blivet/util.py | 20 +++++++++++++++++++ tests/util_test.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+)
diff --git a/blivet/util.py b/blivet/util.py index 8a96350..c914d4b 100644 --- a/blivet/util.py +++ b/blivet/util.py @@ -1082,3 +1082,23 @@ def __new__(cls, *args, **kwargs): return nt.__new__(cls, *args_list)
return TheDefaultNamedTuple + + +def requires_property(prop_name, val=True): + """ + Function returning a decorator that can be used to guard methods and + properties with evaluation of the given property. + + :param str prop_name: property to evaluate + :param val: guard value of the :param:`prop_name` + :type val: :class:`Object` (anything) + """ + def guard(fn): + @wraps(fn) + def func(self, *args, **kwargs): + if getattr(self, prop_name) == val: + return fn(self, *args, **kwargs) + else: + raise ValueError("%s can only be accessed if %s evaluates to %s" % (fn.__name__, prop_name, val)) + return func + return guard diff --git a/tests/util_test.py b/tests/util_test.py index d6792fe..295c519 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -56,3 +56,60 @@ def test_default_namedtuple(self):
dnt = TestTuple() self.assertEqual(dnt, (None, None, 5, None)) + + +class Test(object): + def __init__(self, s): + self._s = s + + @property + def s(self): + return self._s + + @property + def ok(self): + return True + + @property + def nok(self): + return False + + @util.requires_property("s", "hi") + def say_hi(self): + return self.s + ", guys!" + + @property + @util.requires_property("s", "hi") + def hi(self): + return self.s + + @property + @util.requires_property("ok") + def good_news(self): + return "Everything okay!" + + @property + @util.requires_property("nok") + def bad_news(self): + return "Nothing okay!" + + +class TestRequiresProperty(unittest.TestCase): + def test_requires_property(self): + t = Test("hi") + + self.assertEqual(t.s, "hi") + self.assertEqual(t.say_hi(), "hi, guys!") + self.assertEqual(t.hi, "hi") + + t = Test("hello") + self.assertEqual(t.s, "hello") + with self.assertRaises(ValueError): + t.say_hi() + with self.assertRaises(ValueError): + t.hi() + + self.assertEqual(t.good_news, "Everything okay!") + + with self.assertRaises(ValueError): + t.bad_news
anaconda-patches@lists.fedorahosted.org