Pastebin

Validating ListProperty for GAE

Added by andreasblixt 1 year, 7 months ago.

# List validation for Google App Engine
# by Andreas Blixt <andreas@blixt.org>
# MIT license

# Don't miss the === EXAMPLE === section below.

class ValidatingList(list):
    """A wrapper of the list class that will throw an exception if an attempt to
    add a value of any type other than the type specified in its constructor is
    made.
    """
    def __init__(self, type, sequence = None):
        self._type = type
        if sequence: self.extend(sequence)

    def __setitem__(self, i, y):
        self._validate(y)
        super(ValidatingList, self).__setitem__(i, y)

    def __setslice__(self, i, j, y):
        for v in y: self._validate(v)
        super(ValidatingList, self).__setslice__(i, j, y)

    def _validate(self, value):
        # Check whether the value that is being appended is of a valid type.
        if not isinstance(value, self._type):
            raise TypeError('Unexpected type \'%s\'; expected \'%s\'.' % (
                            type(value).__name__, self._type.__name__))

    def append(self, object):
        self._validate(object)
        super(ValidatingList, self).append(object)

    def extend(self, iterable):
        for v in iterable: self._validate(v)
        super(ValidatingList, self).extend(iterable)


# === EXAMPLE ===
class ListProperty(object):
    """Simplified ListProperty descriptor.
    """
    def __init__(self, item_type, default = None):
        if not isinstance(item_type, type):
            raise TypeError('\'item_type\' must be a type.')

        self.type = item_type

        # For the sake of simplicity, values are stored on the descriptor
        # instance. In the GAE db module, they are stored on the model instance.
        self.value = ValidatingList(item_type, default)

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = ValidatingList(self.type, value)

class StringListProperty(ListProperty):
    def __init__(self, default = None):
        super(StringListProperty, self).__init__(basestring, default)

class SimpleModel(object):
    list_of_strings = StringListProperty(default = ['1', '2', '3'])

test = SimpleModel()

# None of the following will throw an error:
test.list_of_strings[1:2] = ['3', '2']
test.list_of_strings.extend(['4', '5'])
test.list_of_strings.append('6')
test.list_of_strings = ['1', '2', '3']
test.list_of_strings += ['6', '7']
test.list_of_strings[2] = '8'

# All of the following will throw an error:
test.list_of_strings[1:2] = [3, 2]
test.list_of_strings.extend(['4', 5])
test.list_of_strings.append(6)
test.list_of_strings = ['1', 2, '3']
test.list_of_strings += [6, '7']
test.list_of_strings[2] = 8

Make a comment

Comments

Comment by andreasblixt, 1 year, 7 months ago
Note that the above example is not suitable for multiple instances of SimpleModel (they all refer to the same StringListProperty descriptor and thus they would use the same list.) This can be solved by giving the ListProperty a meta-class that will call a method on the descriptor that gives it the name of the attribute on the model ('list_of_strings' above.) Then the descriptor can store the value on the model instance instead.

Not logged in (Log in)