Skip to main content

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


Download files

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

Source Distribution

relaxed_types-1.0.1.tar.gz (6.6 kB view details)

Uploaded Source

File details

Details for the file relaxed_types-1.0.1.tar.gz.

File metadata

File hashes

Hashes for relaxed_types-1.0.1.tar.gz
Algorithm Hash digest
SHA256 41fce54c6b9ca635e3e958e1a2d8832c819545c48595e35e29f6ca34d6abbad9
MD5 6fd5e813930867fecbecec5fc87dab0e
BLAKE2b-256 5c66172b9d3ce670b743d64be802e00bfa1a00e0ae2564f7290989b29c46c1a2

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page