diff --git a/lib/utils.py b/lib/utils.py index d0d07bcdcd1a9a76138046eaae4b2e1ac8e88964..20a3b9b3f9a3937c2ec23df2cc1310f83a852ff0 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -55,6 +55,11 @@ except ImportError: import sha sha1 = sha.new +try: + import functools +except ImportError: + functools = None + from ganeti import errors from ganeti import constants @@ -1501,6 +1506,31 @@ def any(seq, pred=bool): # pylint: disable-msg=W0622 return False +# Even though we're using Python's built-in "partial" function if available, +# this one is always defined for testing. +def _partial(func, *args, **keywords): # pylint: disable-msg=W0622 + """Decorator with partial application of arguments and keywords. + + This function was copied from Python's documentation. + + """ + def newfunc(*fargs, **fkeywords): + newkeywords = keywords.copy() + newkeywords.update(fkeywords) + return func(*(args + fargs), **newkeywords) # pylint: disable-msg=W0142 + + newfunc.func = func + newfunc.args = args + newfunc.keywords = keywords + return newfunc + + +if functools is None: + partial = _partial +else: + partial = functools.partial + + def SingleWaitForFdCondition(fdobj, event, timeout): """Waits for a condition to occur on the socket. diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 6adb630b885a5074d1b9b89badb8830e604ef6b8..fa900c934a3881b6b3b0847f20870200087e48e9 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -1592,5 +1592,32 @@ class TestLineSplitter(unittest.TestCase): "", "x"]) +class TestPartial(testutils.GanetiTestCase): + def test(self): + self._Test(utils.partial) + self._Test(utils._partial) + + def _Test(self, fn): + def _TestFunc1(x, power=2): + return x ** power + + cubic = fn(_TestFunc1, power=3) + self.assertEqual(cubic(1), 1) + self.assertEqual(cubic(3), 27) + self.assertEqual(cubic(4), 64) + + def _TestFunc2(*args, **kwargs): + return (args, kwargs) + + self.assertEqualValues(fn(_TestFunc2, "Hello", "World")("Foo"), + (("Hello", "World", "Foo"), {})) + + self.assertEqualValues(fn(_TestFunc2, "Hello", xyz=123)("Foo"), + (("Hello", "Foo"), {"xyz": 123})) + + self.assertEqualValues(fn(_TestFunc2, xyz=123)("Foo", xyz=999), + (("Foo", ), {"xyz": 999,})) + + if __name__ == '__main__': testutils.GanetiTestProgram()