From: Vratislav Podzimek <vpodzime(a)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
--
To view this commit on github, visit
https://github.com/rhinstaller/blivet/commit/83d4f0fee5ec43b4bb1aa444594f...