Package: type-enforced
Type EnforcedA pure python (no special compiler required) type enforcer for type annotations. Enforce types in python functions and methods.SetupMake sure you have Python 3.9.x (or higher) installed on your system. You can download ithere. For older python versions (3.7 | 3.8), you should use type_enforced==0.0.16.Note: Certain features are only available on newer python versions:EG: Staticmethod typechecking requirespython>=3.10EG: Union types with|requirepython>=3.10Installationpip install type_enforcedBasic Usageimporttype_enforced@type_enforced.Enforcerdefmy_fn(a:int,b:[int,str]=2,c:int=3)->None:passGetting Startedtype_enforcercontains a basicEnforcerwrapper that can be used to enforce many basic python typing hints.Technical Docs Here.type_enforcercurrently supports many single and multi level python types. This includes class instances and classes themselves. For example, you can force an input to be anint, a number[int, float], an instance of the self definedMyClass, or a even a vector withlist[int]. Items liketyping.List,typing.Dict,typing.Unionandtyping.Optionalare supported.You can pass union types to validate one of multiple types. For example, you could validate an input was an int or a float with[int, float],[int | float]or eventyping.Union[int, float].Nesting is allowed as long as the nested items are iterables (e.g.typing.List,dict, ...). For examle, you could validate that a list is a vector withlist[int]or possiblytyping.List[int].Variables without an annotation for type are not enforced.Supported Type Checking Features:Function/Method Input TypingFunction/Method Return TypingAll standard python types (str,list,int,dict, ...)Union typestyping.Union,separated list (e.g.[int, float])|separated list (e.g.[int | float])Nested types (e.g.dict[str]orlist[int,float])Note: Each parent level must be an iterableSpecifically a variant oflist,set,tupleordictNote:dictkeys are not validated, only valuesDeeply nested types are supported too:dict[dict[int]]list[set[str]]Many of thetyping(package) functions and methods including:Standard typing functions:List,Set,Dict,TupleUnionOptionalSizedEssentially creates a union of:list,tuple,dict,set,str,bytes,bytearray,memoryview,rangeNote: Can not have a nested typeBecause this does not always meet the criteria forNested typesaboveLiteralOnly allow certain values to be passed. Operates slightly differently than other checks.e.g.Literal['a', 'b']will require any passed values that are equal (==) to'a'or'b'.This compares the value of the passed input and not the type of the passed input.Note: Multiple types can be passed in the sameLiteral.Note: Other functions might have support, but there are not currently tests to validate themFeel free to create an issue (or better yet a PR) if you want to add tests/supportInteractive Example>>>importtype_enforced>>>@type_enforced.Enforcer...defmy_fn(a:int,b:[int,str]=2,c:int=3)->None:...pass...>>>my_fn(a=1,b=2,c=3)>>>my_fn(a=1,b='2',c=3)>>>my_fn(a='a',b=2,c=3)Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>File"/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py",line85,in__call__self.__check_type__(assigned_vars.get(key),value,key)File"/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py",line107,in__check_type__self.__exception__(File"/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py",line34,in__exception__raiseTypeError(f"({self.__fn__.__qualname__}):{message}")TypeError:(my_fn):Typemismatchfortypedvariable`a`.Expectedoneofthefollowing`[<class'int'>]` but got `<class 'str'>` instead.Nested Examplesimporttype_enforcedimporttyping@type_enforced.Enforcerdefmy_fn(a:dict[dict[int,float]],# Note: dict keys are not validated, only valuesb:list[typing.Set[str]]# Could also just use set)->None:returnNonemy_fn(a={'i':{'j':1}},b=[{'x'}])# Successmy_fn(a={'i':{'j':'k'}},b=[{'x'}])# Error:# TypeError: (my_fn): Type mismatch for typed variable `a[i][j]`. Expected one of the following `[<class 'int'>]` but got `<class 'str'>` instead.Class and Method UseType enforcer can be applied to methods individually:importtype_enforcedclassmy_class:@type_enforced.Enforcerdefmy_fn(self,b:int):passYou can also enforce all typing for all methods in a class by decorating the class itself.importtype_enforced@type_enforced.Enforcerclassmy_class:defmy_fn(self,b:int):passdefmy_other_fn(self,a:int,b:[int,str]):passYou can also enforce types onstaticmethods andclassmethods if you are usingpython >= 3.10. If you are using a python version less than this,classmethods andstaticmethods methods will not have their types enforced.importtype_enforced@type_enforced.Enforcerclassmy_class:@classmethoddefmy_fn(self,b:int):pass@staticmethoddefmy_other_fn(a:int,b:[int,str]):passValidate class instances and classesType enforcer can enforce class instances and classes. There are a few caveats between the two.To enforce a class instance, simply pass the class itself as a type hint:importtype_enforcedclassFoo():def__init__(self)->None:pass@type_enforced.Enforcerclassmy_class():def__init__(self,object:Foo)->None:self.object=objectx=my_class(Foo())# Works great!y=my_class(Foo)# Fails!Notice how an initialized class instanceFoo()must be passed for the enforcer to not raise an exception.To enforce an uninitialized class object usetyping.Type[classHere]on the class to enforce inputs to be an uninitialized class:importtype_enforcedimporttypingclassFoo():def__init__(self)->None:pass@type_enforced.Enforcerclassmy_class():def__init__(self,object_class:typing.Type[Foo])->None:self.object=object_class()y=my_class(Foo)# Works great!x=my_class(Foo())# Fails