From 8bc17ebb9a35d93fe4ea052aa4c09790fad1be08 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Mon, 10 Dec 2012 09:27:07 +0100 Subject: [PATCH] Add optional formatting for OP_DSC_FIELD For some opcodes, the output is not "stable", and depends on the exact input values; this makes it harder to check consistency against Haskell code. To compensate for this, we add a way to override the formatting of the OP_DSC_FIELD; by default, this is always "%s", but if the OP_DSC_FORMATTER is defined (must be a callable), it is used to format the actual value. Signed-off-by: Iustin Pop <iustin@google.com> Reviewed-by: Guido Trotter <ultrotter@google.com> --- lib/opcodes.py | 22 +++++++++++++++++++++- test/ganeti.opcodes_unittest.py | 10 ++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/opcodes.py b/lib/opcodes.py index fd6163953..6e3fed502 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -434,6 +434,10 @@ class _AutoOpParamSlots(objectutils.AutoSlots): slots = mcs._GetSlots(attrs) assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ "Class '%s' uses unknown field in OP_DSC_FIELD" % name + assert ("OP_DSC_FORMATTER" not in attrs or + callable(attrs["OP_DSC_FORMATTER"])), \ + ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" % + (name, type(attrs["OP_DSC_FORMATTER"]))) attrs["OP_ID"] = _NameToId(name) @@ -598,6 +602,9 @@ class OpCode(BaseOpCode): @cvar OP_DSC_FIELD: The name of a field whose value will be included in the string returned by Summary(); see the docstring of that method for details). + @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if + not present, then the field will be simply converted + to string @cvar OP_PARAMS: List of opcode attributes, the default values they should get if not already defined, and types they must match. @cvar OP_RESULT: Callable to verify opcode result @@ -685,7 +692,10 @@ class OpCode(BaseOpCode): field_name = getattr(self, "OP_DSC_FIELD", None) if field_name: field_value = getattr(self, field_name, None) - if isinstance(field_value, (list, tuple)): + field_formatter = getattr(self, "OP_DSC_FORMATTER", None) + if callable(field_formatter): + field_value = field_formatter(field_value) + elif isinstance(field_value, (list, tuple)): field_value = ",".join(str(i) for i in field_value) txt = "%s(%s)" % (txt, field_value) return txt @@ -1958,6 +1968,16 @@ class OpTestDelay(OpCode): ("repeat", 0, ht.TNonNegativeInt, None), ] + def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201 + """Custom formatter for duration. + + """ + try: + v = float(value) + except TypeError: + v = value + return str(v) + class OpTestAllocator(OpCode): """Allocator framework testing. diff --git a/test/ganeti.opcodes_unittest.py b/test/ganeti.opcodes_unittest.py index 3e2db17a5..9cc80ffea 100755 --- a/test/ganeti.opcodes_unittest.py +++ b/test/ganeti.opcodes_unittest.py @@ -121,6 +121,16 @@ class TestOpcodes(unittest.TestCase): self.assertEqual(OpTest(data="node1.example.com").Summary(), "TEST(node1.example.com)") + def testSummaryFormatter(self): + class OpTest(opcodes.OpCode): + OP_DSC_FIELD = "data" + OP_DSC_FORMATTER = lambda _, v: "a" + OP_PARAMS = [ + ("data", ht.NoDefault, ht.TString, None), + ] + self.assertEqual(OpTest(data="").Summary(), "TEST(a)") + self.assertEqual(OpTest(data="b").Summary(), "TEST(a)") + def testTinySummary(self): self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values())) self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_") -- GitLab