Python package for function argument overload, typechecking and casting
Project description
pyoload
pyoload is a little initiative to integrate tools for typechecking and casting in python functions and classes.
usage
pyoload.annotate
Simple decorator over functions and classes
functions
e.g
from pyoload import *
@annotate
def foo(bar: int) -> str:
...
@annotate
def bar(foo: str):
...
raises pyoload.AnnotationError
when type mismatch
classes
When annotating a class, pyoload wraps the classes __setattr__
with
a typechecker function which typechecks the passed value on each assignment.
It also calls annotate on each of it's methods, except the class has a
__annotate_norecur__
attribute.
But if the assigned attribute does not yet have annotations, it gets them using
type(val)
and adds them to the annotations.
from pyoload import *
@annotate
class Person:
age: 'int'
def __init__(self: Any, age: int, name: str):
self.age = age
self.name = name
djamago = Person(15, 'djamago') # {'age': <class 'int'>, 'name': <class 'str'>}
print(djamago.__annotations__)
pyoload.overload
When decorating a function it:
- annotates the function with the special kwarg
is_overload=True
- gets the function's name using
pyoload.get_name
and if needed creates a new dictionarry value inpyoload.__overloads__[name]
where it stores all overloads and stores a copy in the function's.__pyod_overloads__
attribute.
And on each call it simply loops through each function entry, while
it catches a pyoload.InternalAnnotationError
which is raised when
the special is_overload
is set to true
tip you may raise
pyoload.InternalAnnotationError
inside an overloaded function after carrying out some other checks and pyoload will switch to the next oveload.
@overload
def foo(a: int):
...
@overload
def foo(b: str, c: float):
...
@foo.overload
def foo_hello(d: dict[str, list[int]]):
...
type checking with pyoload.typeMatch(val, type)
this function simply finds type compatibility between the passes arguments
the type could be:
- a class
- a Union e.g
int|str
- a generic alias e.g
dict[str, int]
- a subclass of
pyoload.PyoloadAnnotation
as:pyoload.Checks
pyoload.Values
Casting with pyoload.Cast
Most pyoload decorators support pyoload.Cast
instances,
When used as an annotation the value is casted to the specified type.
def foo(a: str):
print(repr(a))
foo(3.5) # '3.5'
casting recursiion
Using recursion it supports Generic Aliases of dict
and builtin iterable
types as list
and tuple
.
from pyoload import Cast
caster = Cast(dict[str, list[tuple[float]]]) # a dictionary of names of
# places[str] to a list of their
# (x, y) coordinates
# [list[tuple[float]]]
raw = {
4: (
['1.5', 10],
[10, '1.5'],
)
}
print(caster(raw)) # {'4': [(1.5, 10.0), (10.0, 1.5)]}
Note When
pyoload.Cast
receives a Union asint|str
it tries to cast to the listed forms in the specific order, thus if we havetest = (3j, 11.0)
andcaster = Cast(tuple[float|str])
casting withcaster(test)
will give('3j', 11.0)
, since complex3j
can not be converted to float, andpyoload.Cast.cast
will fallback tostr
writing checks pyoload.Checks
It provides a simple API for writing custom functions for checking.
from pyoload import *
Check.register('is_equal')
def isnonecheck(param, value):
print(f'{param=}, {value=}')
if param != value:
raise Check.CheckError(f'{param!r} not equal to {value!r}')
@annotate
def foo(bar: Checks(is_equal=3)):
pass
foo(3) # param=3 value=3
foo('4')
Traceback (most recent call last):
File "C:\pyoload\src\del.py", line 77, in <module>
foo('4')
File "C:\pyoload\src\pyoload\__init__.py", line 514, in wrapper
raise AnnotationErrors(errors)
pyoload.AnnotationErrors: [AnnotationError("Value: '4' does not match annotation: <Checks(is_equal=3)> for argument 'bar' of function __main__.foo")]
It provides builtin checkes as: lt, gt, ge, le, eq, func:Callable
,
type:Any|PyoloadAnnotation
using pyoload.CheckedAttr
and pyoload.CastedAttr
pyoload
provides:
pyoload.CheckedAttr
A descriptor which does the type checking on assignment, andpyoload.CastedAttr
Another descriptor Which stores a casted copy of the values it is assigned
class Person:
age = CheckedAttr(gt=0)
phone = CastedAttr(tuple[int])
def __init__(self, age, phone):
self.age = age
self.phone = phone
temeze = Person(17, "678936798")
print(temeze.age) # 17
print(temeze.phone) # (6, 7, 8, 9, 3, 6, 7, 9, 8)
mballa = Person(0, "123456")
Traceback (most recent call last):
File "C:\pyoload\src\del.py", line 92, in <module>
mballa = Person(0, "123456")
^^^^^^^^^^^^^^^^^^^
File "C:\pyoload\src\del.py", line 84, in __init__
self.age = age
^^^^^^^^
File "C:\pyoload\src\pyoload\__init__.py", line 264, in __set__
self(value)
File "C:\pyoload\src\pyoload\__init__.py", line 227, in __call__
Check.check(name, params, val)
File "C:\pyoload\src\pyoload\__init__.py", line 132, in check
check(params, val)
File "C:\pyoload\src\pyoload\__init__.py", line 187, in gt_check
raise Check.CheckError(f'{val!r} not gt {param!r}')
pyoload.Check.CheckError: 0 not gt 0
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.