Library for creating utility classes giving a nice abstraction for type checking and data validation
pip install checktypes==0.5.post1
Library for creating utility classes giving a nice abstraction for type checking and data validation.
Choose a base class alongside CheckType
to inherit from and define a predicate()
staticmethod or classmethod
>>> from checktypes import CheckType
>>> class PositiveInteger(int, CheckType):
... "'int' > 0"
... @staticmethod
... def predicate(n):
... return n > 0
...
>>> class HumanAge(int, CheckType):
... "'int' between 0 and 125"
... minval = 0
... maxval = 125
... @classmethod
... def predicate(cls, val):
... return cls.minval <= val <= cls.maxval
...
Alternatively, you can also use lambdas for shorter class definitions.
>>> class PositiveInteger(int, CheckType):
... "'int' > 0"
... predicate = lambda n: n > 0
...
>>> class HumanAge(int, CheckType):
... "'int' between 0 and 125"
... minval = 0
... maxval = 125
... predicate = classmethod(lambda cls, n: cls.minval <= n <= cls.maxval)
...
Another way is to use the checktype()
factory.
>>> from checktypes import checktype
>>> PositiveInteger = checktype('PositiveInteger', int, lambda n: n > 0, "'int' > 0")
>>> HumanAge = checktype(
... 'HumanAge', int, doc="'int' between 0 and 125", minval=0, maxval=125,
... predicate=classmethod(lambda cls, n: cls.minval <= n < cls.maxval)
... )
...
isinstance()
overload>>> isinstance(1, PositiveInteger)
True
>>> isinstance(-1, PositiveInteger)
False
>>> isinstance('a', PositiveInteger)
False
validate()
classmethod>>> PositiveInteger.validate(1) # No output => the value is a valid one
>>> PositiveInteger.validate(-1)
Traceback (most recent call last):
...
ValueError: expected 'PositiveInteger' ('int' > 0) but got -1
>>> PositiveInteger.validate('a')
Traceback (most recent call last):
...
TypeError: expected 'PositiveInteger' ('int' > 0) but got 'str'
register()
classmethod>>> isinstance(0, PositiveInteger)
False
>>> PositiveInteger.validate(0)
Traceback (most recent call last):
...
ValueError: expected 'PositiveInteger' ('int' > 0) but got 0
>>> PositiveInteger.register(0) # Now let pass 0
>>> isinstance(0, PositiveInteger)
True
>>> PositiveInteger.validate(0)
as_descriptor()
classmethod>>> class Circle:
... radius = PositiveInteger.as_descriptor()
...
>>> c = Circle()
>>> c.radius = 1
>>> c.radius = -1
Traceback (most recent call last):
...
ValueError: expected 'PositiveInteger' ('int' > 0) but got -1 for 'radius' attribute of 'Circle' object
>>> c.radius = 'a'
Traceback (most recent call last):
...
TypeError: expected 'PositiveInteger' ('int' > 0) but got 'str' for 'radius' attribute of 'Circle' object
checktyped
decorator with type hints (3.6+ style)>>> from checktypes import checktyped
>>> @checktyped
... class Point2D:
... x: float
... y: float
...
>>> p = Point2D()
>>> p.x = 0.0
>>> p.y = 1.0
>>> p.x = 'a'
Traceback (most recent call last):
...
TypeError: expected 'float' but got 'str' for 'x' attribute of 'Point2D' object
By concept CheckType
s are not originally meant to be instantiated. But since it's a common task
to cast a value into another type, support have been added to make the constructor return a value
with the same rules as a standard class in python except three things:
>>> PositiveInteger(1)
1
>>> n = PositiveInteger(1)
>>> print(n, type(n), sep=': ')
1: <class 'int'>
isinstance()
check, a ValueError
will be raise.>>> PositiveInteger(-1)
Traceback (most recent call last):
...
ValueError: -1 cannot be interpreted as a 'PositiveInteger' ('int' > 0)
__init__()
and __new__()
are ignored.>>> class MyInt(int, CheckType):
... def __new__(cls, x):
... return 'unexpected thing'
... def __init__(self, x):
... self.my_attr = 'some value'
...
>>> x = MyInt(1)
>>> x
1
>>> x.my_attr
Traceback (most recent call last):
...
AttributeError: 'int' object has no attribute 'my_attr'
default
It provides a value to be returned if the class is called without argument.
One of its merit: it fixes the problem of unfit default value.
>>> class NegativeInteger(int, CheckType):
... default = -1
... predicate = lambda n: n < 0
...
>>> NegativeInteger()
-1
>>> del NegativeInteger.default
>>> NegativeInteger() # int() -> 0
Traceback (most recent call last):
...
ValueError: 0 cannot be interpreted as a 'NegativeInteger'
factory()
It is designed to be a callable with the responsibility of returning the new object.
Specially useful when inheriting from an ABC.
>>> from collections.abc import Sized
>>> class ThreePlace(Sized, CheckType):
... factory = tuple
... predicate = lambda s: len(s) == 3
...
>>> ThreePlace(range(1, 4))
(1, 2, 3)
>>> ThreePlace([4, 5, 6])
(4, 5, 6)
>>> ThreePlace('789')
('7', '8', '9')
>>> def badfactory(*args, **kwarg):
... return 'bad value'
...
>>> ThreePlace.factory = badfactory
>>> ThreePlace((1, 2, 3))
Traceback (most recent call last):
...
ValueError: 'bad value' cannot be interpreted as a 'ThreePlace'
>>> del ThreePlace.factory
>>> ThreePlace.default = 0
>>> ThreePlace()
Traceback (most recent call last):
...
TypeError: 'int' object cannot be interpreted as a 'ThreePlace'