relaxed_types
Project description
This library provides a DSL to do type check in Python. The following is provided:
typed_return: Decorator used to verify the type of the return value
check_type: Checks if a value matches to type and predicate specifications
Any: A sentinel object that matches any python object used with check_type or typed_returned
Values: A predicate function that matches the specified values instead of specifications
Or: A predicate function that performs ensures that one of the specifications match
And: A predicate function that performs ensures all specifications match
ReturnTypeError: The exception that check_type raises if a type check fails
The main goal of this library is to have a simple way to ensure return types dynamically via typed_return.
typed_return
Lists
The following snippet shows how to perform a type check (list of integers):
>>> @typed_return([int])
... def func(v):
... return v + [3, 4]
...
>>> func([1, 2])
[1, 2, 3, 4]
>>> func([1, 2.0])
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for 2.0, expected <type 'int'>. Outer value: [1, 2.0, 3, 4]
Tuples
Different from lists, tuples have a fixed size. The tuple specification length has to match the value length.
>>> @typed_return( (str, int) )
... def func(v):
... return v
...
>>> func( ('hello', 123) )
('hello', 123)
>>> func( ('hello', 'world') )
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for 'world', expected <type 'int'>. Outer value: ('hello', 'world')
Sets
Sets behave the same as lists:
>>> @typed_return({str})
... def func(x):
... return x.union({"test"})
...
>>> func({"a", "b"})
set(['a', 'test', 'b'])
>>> func({"a", "b", 1, 2, 3})
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for 1, expected <type 'str'>. Outer value: set(['a', 1, 2, 3, 'test', 'b'])
Dictionaries
It is possible to specify the expected types for dictionary key values. All keys specified must exist in the dictionary —- the value Any can be specified as a key in order to validate additional keys.
>>> @typed_return({"name": str, "age": int})
... def func(v):
... v['test'] = 'test'
... return v
...
>>> func({"name": "John Doe", "age": 21})
{'test': 'test', 'age': 21, 'name': 'John Doe'}
>>> func({"name": "Guy", "age": "47"})
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for '47', expected <type 'int'>. Outer value: {'test': 'test', 'age': '47', 'name': 'Guy'}
The following example shows how to specify a dictionary with key name as str and any other key as int.
>>> from relaxed_types import *
>>> @typed_return({"name": str, Any: int})
... def func(x):
... return x
...
>>> func({"name": "John Doe", "b": 2, "c": 3})
{"name": "John Doe", "b": 2, "c": 3}
Predicates
Predicates allow you to create custom type checks. A predicate is a function that expects an object and returns a boolean: True means the object passed in matches the expectations and False means it does not.
The following snippet ensures func only returns odd numbers:
>>> def odd(x):
... return x % 2 != 0
...
>>> @typed_return(odd)
... def func(v):
... return v * 3
...
>>> func(1)
3
>>> func(2)
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for 6, expected <function odd at ...>. Outer value: 6
Because of predicate support, you can integrate relaxed_types with other libraries, such as voluptuous:
>>> from voluptuous import Length
>>> @typed_return([int], Length(min=10, max=100))
... def func(l):
... return l * 2
...
>>> func(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> func(range(3))
Traceback (most recent call last):
...
voluptuous.LengthInvalid: length of value must be at least 10
The only issue with this integration is that it might either raise ReturnTypeError or an exception that inherits from voluptuous.errors.Invalid.
Values
Predicate function that matches the specified values (not specifications). This is useful to test for literals:
>>> func(0)
0
>>> func(1)
1
>>> func(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "relaxed_types/__init__.py", line 16, in newfn
check_type(result, expected_type, outer_value=result, extra=extra)
File "relaxed_types/checks.py", line 22, in check_type
_check_predicate(value, expected_type, outer_value)
File "relaxed_types/checks.py", line 35, in _check_predicate
_fail(value, expected_type, outer_value, msg=expected_type.__doc__)
File "relaxed_types/checks.py", line 85, in _fail
raise ReturnTypeError(msg, value)
relaxed_types.exceptions.ReturnTypeError: Expected "2" to be in (0, 1)
Or
Predicate function that matches at least one specification:
>>> @typed_return(Or(int, float))
... def func(x):
... return x
...
>>> func(1)
1
>>> func(1.0)
1.0
>>> func("1")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "relaxed_types/__init__.py", line 16, in newfn
check_type(result, expected_type, outer_value=result, extra=extra)
File "relaxed_types/checks.py", line 22, in check_type
_check_predicate(value, expected_type, outer_value)
File "relaxed_types/checks.py", line 35, in _check_predicate
_fail(value, expected_type, outer_value, msg=expected_type.__doc__)
File "relaxed_types/checks.py", line 85, in _fail
raise ReturnTypeError(msg, value)
relaxed_types.exceptions.ReturnTypeError: '1' did not match Or(<type 'int'>, <type 'float'>).
More details about the last check: Type mismatch for '1', expected <type 'float'>. Outer value: '1'
And
Predicate function that matches all specifications:
>>> from relaxed_types import *
>>> @typed_return({"i": And(int, lambda x: x > 0)})
... def func(x):
... return {"i": x}
...
>>> func(1)
{'i': 1}
>>> func(1.0)
Traceback (most recent call last):
...
relaxed_types.exceptions.ReturnTypeError: 1.0 did not match And(<type 'int'>, <function <lambda> at 0x105f7a848>).
More details about the last check: Type mismatch for 1.0, expected <type 'int'>. Outer value: 1.0
>>> func(-1)
Traceback (most recent call last):
...
relaxed_types.exceptions.ReturnTypeError: -1 did not match And(<type 'int'>, <function <lambda> at 0x105f7a848>).
More details about the last check: Type mismatch for -1, expected <function <lambda> at 0x105f7a848>. Outer value: -1
Combining all together
It’s possible to combine lists, tuples, dictionaries, predicates, and any Python type.
>>> @typed_return(int, lambda x: x > 0)
... def func1(x):
... return x + 10
...
>>>
>>> func1(10)
20
>>> func1(-100)
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for -90, expected <type 'int'>. Outer value: -90
>>> @typed_return([int], lambda x: len(x) > 0)
... def func1(x):
... return x
...
>>>
>>> func1([1, 2])
[1, 2]
>>> func1([])
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for [], expected [<type 'int'>]. Outer value: []
>>> @typed_return([ {"name": lambda x: x.upper() == x} ])
... def func2(x):
... return x
...
>>>
>>> func2([{"name": "JOHN DOE"}])
[{'name': 'JOHN DOE'}]
>>> func2([{"name": "test"}])
Traceback (most recent call last):
...
relaxed_types.ReturnTypeError: Type mismatch for 'test', expected <function <lambda> at 0x10e325758>. Outer value: [{'name': 'test'}]
>>> @typed_return([{"data": Any, "id": And(int, lambda x: x > 0)}])
... def func3(x):
... return x
...
>>> func3([{"data": "price=10", "id": 1}])
[{'data': 'price=10', 'id': 1}]
>>> func3([{"data": 10, "id": 2}])
[{'data': 10, 'id': 2}]
>>> func3([{"data": {"price": 10}, "id": 2}])
[{'data': {'price': 10}, 'id': 2}]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file relaxed_types-1.0.1.tar.gz
.
File metadata
- Download URL: relaxed_types-1.0.1.tar.gz
- Upload date:
- Size: 6.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 41fce54c6b9ca635e3e958e1a2d8832c819545c48595e35e29f6ca34d6abbad9 |
|
MD5 | 6fd5e813930867fecbecec5fc87dab0e |
|
BLAKE2b-256 | 5c66172b9d3ce670b743d64be802e00bfa1a00e0ae2564f7290989b29c46c1a2 |