To install pyguardian, run this command from the console:
pip install pyguardian
To access the guard decorator, it must be imported first:
from pyguardian import guard
The guard decorator's signature is as follows:
@guard(*types, **kwtypes)
The constructor accepts items of type type
, NoneType
and list
/tuple
containing elements of type type
. If an invalid argument is passed to the constructor, a ValueError
is raised with the message:
guard constructor not properly called!
The method below takes one parameter, x
. By passing int
to the guard decorator, x
now only accepts a value of type int
.
@guard(int)
def foo(x):
...
foo(1) # valid call
foo("Hello World") # invalid call
Multiple types for one parameter may also be specified by passing a list
or a tuple
containing elements of type type
:
@guard((int, float))
def foo(x):
...
foo(1) # valid call
foo(1.2) # valid call
By not enforcing a type on a parameter, that parameter will then accept a value of any type.
@guard(int)
def foo(x, y):
...
foo(1, "Hello World") # valid call
foo(1, True) # valid call
Note that the below is also accepted but not encouraged as the constructor accepts None
by itself.
@guard((int, type(None)))
def foo(x):
...
When guarding methods defined inside of a class, object
must be the first argument passed to the guard decorator for instance and class methods. object
does not need to be passed to static methods.
class Foo:
@guard(object, int)
def __init__(self, x):
...
@guard(object, str)
def bar(self, x):
...
@classmethod
@guard(object, float)
def baz(cls, x):
...
@staticmethod
@guard(list)
def qux(x):
...
Guarding functions with parameters that take an arbitrary amount of arguments, i.e. *args
and **kwargs
, works identically to specifying types for other parameters. The obvious difference is that the unpacking operator (*
/**
) should not be passed to the guard decorator when specifying types via keyword.
@guard(args=int)
def foo(*args):
...
foo(1, 2) # valid call
foo(1, True) # invalid call
@guard(kwargs=int)
def foo(**kwargs):
...
foo(a="Hello", b="World") # valid call
foo(a=1, b="World") # invalid call
@guard(int, str)
def foo(*args, **kwargs):
...
foo(1, 2, a="Hello", b="World") # valid call
foo(1, 2, a="Hello", b=1.2) # invalid call
UnknownKeywordArgumentWarning
is a subclass of Warning
.
If there is an unknown keyword passed to the guard decorator, an UnknownKeywordArgumentWarning
is raised:
@guard(y=int)
def foo(x):
...
UnknownKeywordArgumentWarning: guard constructor received unknown keyword argument 'y' which may produce unexpected results as this argument will not be applied.
It is possible to silence the warnings altogether:
import warnings
warnings.filterwarnings("ignore")
InvalidArgumentTypeError
is a subclass of TypeError
.
If a value of type int
is passed to the guarded method, foo
, the method will execute normally. If a value not of type int
is passed, i.e. str
, an InvalidArgumentError
is raised:
@guard(int)
def foo(x):
...
foo(1) # valid call
foo("a") # invalid call
InvalidArgumentTypeError: 'foo' expects value of type 'int' for parameter 'x' but got 'str'