Skip to content

Commit

Permalink
feat(config): add a list Property for validating input lists
Browse files Browse the repository at this point in the history
This property can validate the type and size (exact and maximum) of an
incoming list.
  • Loading branch information
jrs65 committed Feb 19, 2020
1 parent 08a0250 commit b32ad1d
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
69 changes: 69 additions & 0 deletions caput/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,75 @@ def _prop(val):
return prop


def list_type(type_=None, length=None, maxlength=None, default=None):
"""A property type that validates lists against required properties.
Parameters
----------
type_ : type, optional
Type to apply. If `None` does not attempt to validate elements against type.
length : int, optional
Exact length of the list we expect. If `None` (default) accept any length.
maxlength : int, optional
Maximum length of the list. If `None` (default) there is no maximum length.
default : optional
The optional default value.
Returns
-------
prop : Property
A property instance setup to validate the type.
Examples
--------
Should be used like::
class Project(object):
mode = list_type(int, length=2, default=[3, 4])
"""

def _prop(val):

if not isinstance(val, (list, tuple)):
raise ValueError("Expected to receive a list, but got '%s.'" % repr(val))

if type_:
for ii, item in enumerate(val):
if not isinstance(item, type_):
raise ValueError(
"Expected to receive a list with items of type %s, but got "
"'%s.' at position %i" % (type_, val, ii)
)

if length and len(val) != length:
raise ValueError(
"List expected to be of length %i, but was actually length %i"
% (length, len(val))
)

if maxlength and len(val) > maxlength:
raise ValueError(
"Maximum length of list is %i is, but list was actually length %i"
% (maxlength, len(val))
)

return val

if default:
try:
_prop(default)
except ValueError as e:
raise ValueError(
"Default value %s does not satisfy property requirements: %s"
% (default, repr(e))
)

prop = Property(proptype=_prop, default=default)

return prop


if __name__ == "__main__":
import doctest

Expand Down
31 changes: 31 additions & 0 deletions caput/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class PersonWithPet(Person):
petage = 36


class ListTypeTests(Person):
list_max_length = config.list_type(maxlength=2)
list_exact_length = config.list_type(length=2)
list_type = config.list_type(type_=int)


class TestConfig(unittest.TestCase):

testdict = {"name": "Richard", "ageinyears": 40, "petname": "Sooty"}
Expand Down Expand Up @@ -67,3 +73,28 @@ def test_pickle(self):
self.assertEqual(person2.name, "Richard")
self.assertEqual(person2.age, 40.0)
self.assertEqual(person2.petname, "Sooty")

def test_list_type(self):

lt = ListTypeTests()

with self.assertRaises(ValueError):
lt.read_config({"list_max_length": [1, 3, 4]})

# Should work fine
lt = ListTypeTests()
lt.read_config({"list_max_length": [1, 2]})

with self.assertRaises(ValueError):
lt.read_config({"list_exact_length": [3]})

# Work should fine
lt = ListTypeTests()
lt.read_config({"list_exact_length": [1, 2]})

with self.assertRaises(ValueError):
lt.read_config({"list_type": ["hello"]})

# Work should fine
lt = ListTypeTests()
lt.read_config({"list_type": [1, 2]})

0 comments on commit b32ad1d

Please sign in to comment.