On Tue, May 17, 2011 at 04:24:33PM +0200, Michael Hanselmann wrote:
> This allows checking specific dictionary items, unlike TDict
> or TDictOf.
> 
> Signed-off-by: Michael Hanselmann <[email protected]>
> ---
>  lib/ht.py                  |   48 
> ++++++++++++++++++++++++++++++++++++++++++++
>  test/ganeti.ht_unittest.py |   48 
> ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 96 insertions(+), 0 deletions(-)
> 
> diff --git a/lib/ht.py b/lib/ht.py
> index f2fc0b3..a0c787e 100644
> --- a/lib/ht.py
> +++ b/lib/ht.py
> @@ -292,3 +292,51 @@ def TDictOf(key_type, val_type):
>              compat.all(val_type(v) for v in container.values()))
>  
>    return desc(TAnd(TDict, fn))
> +
> +
> +def _TStrictDictCheck(require_all, exclusive, items, val):
> +  """Helper function for L{TStrictDict}.
> +
> +  """
> +  notfound_fn = lambda _: not exclusive
> +
> +  if require_all and not frozenset(val.keys()).issuperset(items.keys()):
> +    # Requires items not found in value
> +    return False
> +
> +  return compat.all(items.get(key, notfound_fn)(value)
> +                    for (key, value) in val.items())
> +
> +
> +def TStrictDict(require_all, exclusive, items):
> +  """Strict dictionary check with specific keys.
> +
> +  @type require_all: boolean
> +  @param require_all: Whether all keys in L{items} are required
> +  @type exclusive: boolean
> +  @param exclusive: Whether only keys listed in L{items} should be accepted
> +  @type items: dictionary
> +  @param items: Mapping from key (string) to verification function
> +
> +  """
> +  descparts = ["Dictionary containing"]
> +
> +  if exclusive:
> +    descparts.append(" none but the")
> +
> +  if require_all:
> +    descparts.append(" required")
> +
> +  if len(items) == 1:
> +    descparts.append(" key ")
> +  else:
> +    descparts.append(" keys ")
> +
> +  descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
> +                                   for (key, value) in items.items()))
> +
> +  desc = WithDesc("".join(descparts))
> +
> +  return desc(TAnd(TDict,
> +                   compat.partial(_TStrictDictCheck, require_all, exclusive,
> +                                  items)))
> diff --git a/test/ganeti.ht_unittest.py b/test/ganeti.ht_unittest.py
> index 34ae671..5f22578 100755
> --- a/test/ganeti.ht_unittest.py
> +++ b/test/ganeti.ht_unittest.py
> @@ -187,6 +187,54 @@ class TestTypeChecks(unittest.TestCase):
>      self.assertFalse(fn({"x": None}))
>      self.assertFalse(fn({"": 8234}))
>  
> +  def testStrictDictRequireAllExclusive(self):
> +    fn = ht.TStrictDict(True, True, { "a": ht.TInt, })
> +    print fn
> +    self.assertFalse(fn(1))
> +    self.assertFalse(fn(None))
> +    self.assertFalse(fn({}))
> +    self.assertFalse(fn({"a": "Hello", }))
> +    self.assertFalse(fn({"unknown": 999,}))
> +    self.assertFalse(fn({"unknown": None,}))
> +
> +    self.assertTrue(fn({"a": 123, }))
> +    self.assertTrue(fn({"a": -5, }))
> +
> +    fn = ht.TStrictDict(True, True, { "a": ht.TInt, "x": ht.TString, })
> +    print fn
> +    self.assertFalse(fn({}))
> +    self.assertFalse(fn({"a": -5, }))
> +    self.assertTrue(fn({"a": 123, "x": "", }))
> +    self.assertFalse(fn({"a": 123, "x": None, }))
> +
> +  def testStrictDictExclusive(self):
> +    fn = ht.TStrictDict(False, True, { "a": ht.TInt, "b": ht.TList, })
> +    print fn
> +    self.assertTrue(fn({}))
> +    self.assertTrue(fn({"a": 123, }))
> +    self.assertTrue(fn({"b": range(4), }))
> +    self.assertFalse(fn({"b": 123, }))
> +
> +    self.assertFalse(fn({"foo": {}, }))
> +    self.assertFalse(fn({"bar": object(), }))
> +
> +  def testStrictDictRequireAll(self):
> +    fn = ht.TStrictDict(True, False, { "a": ht.TInt, "m": ht.TInt, })
> +    print fn
> +    self.assertTrue(fn({"a": 1, "m": 2, "bar": object(), }))
> +    self.assertFalse(fn({}))
> +    self.assertFalse(fn({"a": 1, "bar": object(), }))
> +    self.assertFalse(fn({"a": 1, "m": [], "bar": object(), }))
> +
> +  def testStrictDict(self):
> +    fn = ht.TStrictDict(False, False, { "a": ht.TInt, })
> +    print fn
> +    self.assertTrue(fn({}))
> +    self.assertFalse(fn({"a": ""}))
> +    self.assertTrue(fn({"a": 11}))
> +    self.assertTrue(fn({"other": 11}))
> +    self.assertTrue(fn({"other": object()}))

Err, wanted to ask: what's with this "print fn" in the tests?

iustin

Reply via email to