Awesome
Introduction
magic-constraints implemented (or hacked) a bunch of abstract base classes (ABCs for short) to support type introspection, that is, the isinstance
/issubclass
operations in Python. Specialization of ABC is support, i.e. Sequence[int]
and Sequence[int]
are specialized versions of Sequence
.
Also, magic-constraints provides several decorators to enable runtime type/value checking on the parameters and return value of user-defined function and method. Especially, thoses decorators fit well with the type annotation feature introduced in Python 3.x:
from magic_constraints import function_constraints, Optional
# foobar should accept either an int object or a None object.
@function_constraints
def function(foobar: Optional[int]) -> float:
if foobar is None:
# should fail the return type checking.
return 42
else:
# good case.
return 42.0
Runtime:
# ok.
>>> function(1)
42.0
# failed.
# 1.0 is float, while foobar requrie int or type(None).
>>> function(1.0)
Traceback (most recent call last):
...
magic_constraints.exception.MagicTypeError:
MagicTypeError: argument unmatched.
-----------------------------------
argument: 1.0
parameter: Parameter(name='foobar', type_=Optional[int])
-----------------------------------
# failed.
# when foobar is None, the function returns a float,
# leading to unmatched return type error.
>>> function(None)
Traceback (most recent call last):
...
magic_constraints.exception.MagicTypeError:
MagicTypeError: return value unmatched.
---------------------------------------
ret: 42
return_type: ReturnType(type_=float)
---------------------------------------
Quick Start
Install
$ pip install magic-constraints
Usage Of ABCs
magic-constraints provides Sequence/MutableSequence/ImmutableSequence
. You can invoke isinstance
/issubclass
operatios on :
from magic_constraints import Sequence, MutableSequence, ImmutableSequence
# True.
isinstance([1, 2, 3], Sequence)
# True.
isinstance([1, 2, 3], MutableSequence)
# True.
isinstance((1, 2, 3), ImmutableSequence)
# True, Sequence with int.
isinstance([1, 2, 3], Sequence[int])
# False, 2.0 is float.
isinstance([1, 2.0, 3], Sequence[int])
# True.
isinstance([(1, 2), (3, 4)], Sequence[ImmutableSequence[int]])
# False, 3.0 is float.
isinstance([(1, 2), (3.0, 4)], Sequence[ImmutableSequence[int]])
# False, [3, 4] is MutableSequence.
isinstance([(1, 2), [3, 4]], Sequence[ImmutableSequence[int]])
# True
issubclass(MutableSequence, Sequence)
# True
issubclass(ImmutableSequence, Sequence)
# False
issubclass(MutableSequence, ImmutableSequence)
# False
issubclass(ImmutableSequence, MutableSequence)
More avaliable ABCs:
name | supported specialization(s) |
---|---|
Sequence | [ type ] , [ type , ... ] |
MutableSequence | [ type ] , [ type , ... ] |
ImmutableSequence | [ type ] , [ type , ... ] |
Set | [ type ] |
MutableSet | [ type ] |
ImmutableSet | [ type ] |
Mapping | [ type , type ] |
MutableMapping | [ type , type ] |
ImmutableMapping | [ type , type ] |
Iterator | [ type ] , [ type , ... ] |
Iterable | [ type ] , [ type , ... ] |
Callable | [ [ type , ... ] , type ] , [ Ellipsis , type ] |
Any | not support |
Union | [ type , ... ] |
Optional | [ type ] |
NoneType | not support |
Usage Of Decorators
Declaration on function parameters and return value:
from magic_constraints import (
function_constraints,
Sequence, Mapping,
)
@function_constraints
def func1(foo: str, bar: Sequence[int]) -> Mapping[str, Sequence[int]]:
return {foo: bar}
More decorators:
from magic_constraints.decorator import (
function_constraints,
method_constraints,
class_initialization_constraints,
)
Runtime Type/Value Checking
Exceptoin would be raised if there's something wrong in the invocation of decorated function, i.e. input argument is not an instance of declared type.
Only derived classes of SyntaxError
and TypeError
would be raised:
- anything related to types, such as failing to pass
isinstance
, would raise an exception with derived type ofTypeError
. - besides (1), anything related to the promise of interface (function) invocation, would raise an exception with derived type of
SyntaxError
.
Example:
# ok.
>>> func1('key', [1, 2, 3])
{'key': [1, 2, 3]}
# failed, bar requires a sequnce.
>>> func1('42 is not a sequence', 42)
Traceback (most recent call last):
...
magic_constraints.exception.MagicTypeError:
MagicTypeError: argument unmatched.
-----------------------------------
argument: 42
parameter: Parameter(name='bar', type_=Sequence[int])
-----------------------------------
# failed, bar requires a sequence of ints.
>>> func1('2.0 is not int', [1, 2.0, 3])
Traceback (most recent call last):
...
magic_constraints.exception.MagicTypeError:
MagicTypeError: argument unmatched.
-----------------------------------
argument: [1, 2.0, 3]
parameter: Parameter(name='bar', type_=Sequence[int])
-----------------------------------