Skip to main content

Library for creating utility classes giving a nice abstraction for type checking and data validation

Project description

# CheckTypes package

Library for creating utility classes giving a nice abstraction for type
checking and data validation.

## Basic examples

### Creation

#### Object Oriented API

Choose a base class alongside `CheckType` to inherit from and define a `predicate()` staticmethod or classmethod

```python
>>> 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.

```python
>>> 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)
...

```

#### Functional API

Another way is to use the `checktype()` factory.

```python
>>> 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)
... )
...

```

### Usage

#### `isinstance()` overload

```python
>>> isinstance(1, PositiveInteger)
True
>>> isinstance(-1, PositiveInteger)
False
>>> isinstance('a', PositiveInteger)
False

```

#### `validate()` classmethod

```python
>>> 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

```python
>>> 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

```python
>>> 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)

```python
>>> 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

```

#### Instantiation

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:

##### 1 - The returned value will never be an instance of the class but an instance of one a its bases.

```python
>>> PositiveInteger(1)
1
>>> n = PositiveInteger(1)
>>> print(n, type(n), sep=': ')
1: <class 'int'>

```

##### 2 - If the value doesn't satisfy the `isinstance()` check, a `ValueError` will be raise.

```python
>>> PositiveInteger(-1)
Traceback (most recent call last):
...
ValueError: -1 cannot be interpreted as a 'PositiveInteger' ('int' > 0)

```

##### 3 - `__init__()` and `__new__()` are ignored.


```python
>>> 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'


```

##### Still, two class attributes can be supplied to add support for better instantiation:

###### 1 - `default`

It provide a value to be returned if the class is called without argument.
One of its merit: it fixes the problem of unfit default value.

```python
>>> 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'

```

###### 2 - `factory()`

It is designed to be a callable with the responsibility of returning the new object.
Specially useful when inheriting from an ABC.

```python
>>> 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')

```

###### Be aware that the returned value will still be checked.

```python
>>> 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'

```

##### For further documentation, see the module docstrings or the help of CheckType.
##### For other examples, see [Recipes.md](https://gitlab.com/yahya-abou-imran/checktypes/blob/master/Recipes.md).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for checktypes, version 0.4rc1
Filename, size File type Python version Upload date Hashes
Filename, size checktypes-0.4rc1.tar.gz (19.7 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page