Package: safecheck
safecheckOpinionated combination of typechecking libraries. Safecheck is a (very) minimal wrapper of the following libraries to
provide a unified and simple-to-use interface:typecheckingbeartypeshapecheckingjaxtypingSafecheck configures aunifiedtypecheckdecorator that invokesbeartype.beartypeif the function annotations
do not contain anyjaxtyping-related types. If the function containsjaxtyping-related typestypecheckinvokesjaxtyping.jaxtypedwithbeartype.beartypeas a runtime type-checker.safecheckis highly-efficient, it adds no measurable overhead to the underlying type and shape checking logic.One of the goals ofsafecheckis to abstract over the runtime-typechecker and -shapechecker such that the concrete
implementation can be swapped without requiring changes to the codebase.We re-export most of the functionality ofbeartypeandjaxtyping, and it might be a good idea to disallow
imports frombeartypeandjaxtypingif you are using safecheck, e.g. usingrufforFlake8.To unify thejaxtyping.Arrayinterface, we exportjax.Array as JaxArrayifJaxis available,torch.Tensor as TorchArrayifPyTorchis available andnumpy.ndarray as NumpyArrayifNumPyis available.In addition to the unifiedtypecheck, the library provides atypecheck_overloaddecorator.APIdecoratorstypecheck(fn)typechecks a function without jaxtyping annotations, otherwise additionally shapecheck the function.typecheck_overload(fn)ensures that an implementing function satisfied at least one of its defined overloads.introspectionis_instance(obj, hint)likeisinstance(...), butbetter.assert_instance(obj, hint)likeassert isinstance(...), butbetter.is_subtype(subhint, superhint)tests if a type is a subtype ofanother type.validatorsValidators enable runtime validation usingtyping.Annotated, but these annotations are not enforced by any static
type checker and always require a runtime@typecheck.Isfor example:Annotated[str, Is[lambda x: x > 0)]]IsAttrfor example:Annotated[NumpyArray, IsAttr["ndim", IsEqual[1]]]IsEqualfor example:Annotated[list, IsEqual[list(range(42))]]IsSubclassfor example:Annotated[type, IsSubclass[str, bytes]]IsInstancefor example:Annotated[object, IsInstance[str, bytes]]union array typesExported union array types fromsafecheck.Shaped      # Any type at all (e.g. object or string)
Num         # Any integer, unsigned integer, floating, or complex
Real        # Any integer, unsigned integer or floating
Inexact     # Any floating or complex
Float       # Any floating point
Complex     # Any complex
Integer     # Any integer or unsigned integer
UInt        # Any unsigned integer
Int         # Any signed integerconcrete array typesExported array types fromsafecheck.Int8
Int16
Int32
Int64
Float16
Float32
Float64
Bool
UInt8
UInt16
UInt32
UInt64
Complex64
Complex128ExamplesType-checking a simple function.fromsafecheckimporttypecheck@typecheckdeff(x:int)->int:returnx# f(1) -> 1# f("1") -> failsType-checking a simple method.fromsafecheckimporttypecheckclassA:@typecheckdeff(self,x:int)->int:returnx# A().f(1) -> 1# A().f("1") -> failsShape-checking a simple function.fromsafecheckimporttypecheck,NumpyArray,Integer@typecheckdeff(x:Integer[NumpyArray,"n"])->Integer[NumpyArray,"n"]:returnx# import numpy as np# f(np.array([1, 2, 3, 4, 5])) -> array([1, 2, 3, 4, 5])# f(np.array([1.0, 2.0, 3.0, 4.0, 5.0])) -> fails# f(np.array([[1], [2], [3], [4], [5]])) -> failsShape-checking a simple method.fromsafecheckimporttypecheck,NumpyArray,IntegerclassA:@typecheckdeff(self,x:Integer[NumpyArray,"n"])->Integer[NumpyArray,"n"]:returnx# import numpy as np# A().f(np.array([1, 2, 3, 4, 5])) -> array([1, 2, 3, 4, 5])# A().f(np.array([1.0, 2.0, 3.0, 4.0, 5.0])) -> fails# A().f(np.array([[1], [2], [3], [4], [5]])) -> failsType-checking an overloaded function.fromtyping_extensionsimportoverload# python < 3.11, otherwise ``from typing import overload``fromsafecheckimporttypecheck_overload@overloaddeff(x:int)->int:...@typecheck_overloaddeff(x):returnx# f(1) -> 1# f("1") -> failsType-checking an overloaded method.fromtyping_extensionsimportoverload# python < 3.11, otherwise ``from typing import overload``fromsafecheckimporttypecheck_overloadclassA:@overloaddeff(self,x:int)->int:...@typecheck_overloaddeff(self,x):returnx# A().f(1) -> 1# A().f("1") -> failsShape-checking an overloaded function.fromtyping_extensionsimportoverload# python < 3.11, otherwise ``from typing import overload``fromsafecheckimporttypecheck_overload,NumpyArray,Integer@overloaddeff(x:Integer[NumpyArray,"n"])->Integer[NumpyArray,"n"]:...@typecheck_overloaddeff(x):returnx# import numpy as np# f(np.array([1, 2, 3, 4, 5])) -> array([1, 2, 3, 4, 5])# f(np.array([1.0, 2.0, 3.0, 4.0, 5.0])) -> fails# f(np.array([[1], [2], [3], [4], [5]])) -> failsShape-checking an overloaded method.fromtyping_extensionsimportoverload# python < 3.11, otherwise ``from typing import overload``fromsafecheckimporttypecheck_overload,NumpyArray,IntegerclassA:@overloaddeff(self,x:Integer[NumpyArray,"n"])->Integer[NumpyArray,"n"]:...@typecheck_overloaddeff(self,x):returnx# import numpy as np# A().f(np.array([1, 2, 3, 4, 5])) -> array([1, 2, 3, 4, 5])# A().f(np.array([1.0, 2.0, 3.0, 4.0, 5.0])) -> fails# A().f(np.array([[1], [2], [3], [4], [5]])) -> fails