Skip to main content

Type safe checker and signature decorator

Project description

ty-sig

Type safe checker and signature decorator

from tysig.tysig import TySig

is_type examples

check type of variable including typing types e.g. Union, Optional, List, Dict, Tuple, Literal, Any, AnyStr, TypeVar, etc.

from typing import Union, Optional, Dict, List, Tuple, Any, AnyStr
help(TySig)
Help on class TySig in module tysig.tysig:

class TySig(builtins.object)
 |  Static methods defined here:
 |  
 |  getattr_name(invar) -> Union[str, NoneType]
 |      gets name of type of invar
 |      i.e. gets __name__, or _name, or __origin__ of input object
 |      
 |      :param invar: object we are checking name of
 |      :return: name if exists otherwise None
 |  
 |  is_type(var: object, check_type) -> bool
 |      recursively checks if var is of type check_type
 |      
 |      :param var: variable to check type
 |      :param check_type: type to check against
 |      :return: True if var is of type check_type, else False
 |  
 |  is_typing_type(vtype)
 |      check if type vtype is a typing type of either:
 |      _GenericAlias, _VariadicGenericAlias, or _SpecialForm
 |      
 |      :param vtype: we are checking the type of this type
 |      :return: True if is a typing type, else False
 |  
 |  signature(classobj: bool = False, **in_vars_types)
 |      signature decorator applied to functions to hard check the types
 |      of the arguments including typing types. Also applies default values,
 |      and raises exceptions on failing type checks. Uses is_type function.
 |      
 |      Return types are also checked post execution of function.
 |      
 |      :param classobj: set to True if within a class (self object)
 |      :param in_vars_types: arguments with their types and/or default values
 |                            e.g. @signature(a=int, b=Union[float, str],
 |                                            c=List[Dict[str, Tuple[str, int]]]
 |      :return: applies signature checks and applies default values
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
def print_check(var, ntype, check):
    print("\n", "-" * 30)
    print(f"CHECK: {var} is {ntype} ? --> ", check)
    print("-" * 30)
help(TySig.is_type)
Help on function is_type in module tysig.tysig:

is_type(var: object, check_type) -> bool
    recursively checks if var is of type check_type
    
    :param var: variable to check type
    :param check_type: type to check against
    :return: True if var is of type check_type, else False
AType = Tuple[Union[float, str], Union[int, str], Union[float, str]]
BType = Tuple[Union[float, str], Union[List[int], str],
          Union[float, str]]
a: AType = (2.3, 1, "oik")  # type: ignore
check = TySig.is_type(a, AType)
print_check(a, AType, check)

# False example
check = TySig.is_type(a, BType)
print_check(a, BType, check)

# False example
a2: AType = (2.3, 1.6, "oik")
check = TySig.is_type(a2, AType)
print_check(a2, AType, check)
 ------------------------------
CHECK: (2.3, 1, 'oik') is typing.Tuple[typing.Union[float, str], typing.Union[int, str], typing.Union[float, str]] ? -->  True
------------------------------

 ------------------------------
CHECK: (2.3, 1, 'oik') is typing.Tuple[typing.Union[float, str], typing.Union[typing.List[int], str], typing.Union[float, str]] ? -->  False
------------------------------

 ------------------------------
CHECK: (2.3, 1.6, 'oik') is typing.Tuple[typing.Union[float, str], typing.Union[int, str], typing.Union[float, str]] ? -->  False
------------------------------
BType = Dict[
    int,
    Tuple[
        float,
        str,
        List[
            Union[
                Dict[str, int],
                List[float]
            ]
        ]
    ]
]
b: BType = {
    4: (3.7, "je;p", [[4.5, 90.0], {"h": 3}, {"pp": 1}, []]),
    5: (3.7, "je;p", [[4.5, 90.2], {"h": 3}, {"pp": 1}, [], {"1": 2}])
}
check = TySig.is_type(b, BType)
print_check(b, BType, check)

# False example
b2: BType = {
    4: (3.7, "je;p", [[4.5, 90.0], {"h": 3.1}, {"pp": 1}, []]),
    5: (3.7, "je;p", [[4.5, 90.2], {"h": 3}, {"pp": 1}, [], {"1": 2}])
}
check = TySig.is_type(b2, BType)
print_check(b2, BType, check)
 ------------------------------
CHECK: {4: (3.7, 'je;p', [[4.5, 90.0], {'h': 3}, {'pp': 1}, []]), 5: (3.7, 'je;p', [[4.5, 90.2], {'h': 3}, {'pp': 1}, [], {'1': 2}])} is typing.Dict[int, typing.Tuple[float, str, typing.List[typing.Union[typing.Dict[str, int], typing.List[float]]]]] ? -->  True
------------------------------

 ------------------------------
CHECK: {4: (3.7, 'je;p', [[4.5, 90.0], {'h': 3.1}, {'pp': 1}, []]), 5: (3.7, 'je;p', [[4.5, 90.2], {'h': 3}, {'pp': 1}, [], {'1': 2}])} is typing.Dict[int, typing.Tuple[float, str, typing.List[typing.Union[typing.Dict[str, int], typing.List[float]]]]] ? -->  False
------------------------------
CType = Dict[str,
             Dict[int,
                  Dict[float,
                       List[
                           Tuple[
                               Dict[int, float],
                               Union[int, float],
                               str
                           ]
                       ]]]]
c: CType = {
    "h": {1: {3.3: [({1: 1.}, 5, "hello"), ({3: 1.}, 50, "bbbello")]}},
    "ss": {1: {
        3.5: [({1: 1.}, 5., "hello"), ({3: 1.6}, 50.6, "bbbello")]}},
}
check = TySig.is_type(c, CType)
print_check(c, CType, check)

# False example
c2: CType = {
    "h": {1: {3.3: [({1: 1.}, 5, "hello"), ({3: 1.}, 50, 2.6)]}},
    "ss": {1: {
        3.5: [({1: 1.}, 5., "hello"), ({3: 1.6}, 50.6, "bbbello")]}},
}
check = TySig.is_type(c2, CType)
print_check(c2, CType, check)
 ------------------------------
CHECK: {'h': {1: {3.3: [({1: 1.0}, 5, 'hello'), ({3: 1.0}, 50, 'bbbello')]}}, 'ss': {1: {3.5: [({1: 1.0}, 5.0, 'hello'), ({3: 1.6}, 50.6, 'bbbello')]}}} is typing.Dict[str, typing.Dict[int, typing.Dict[float, typing.List[typing.Tuple[typing.Dict[int, float], typing.Union[int, float], str]]]]] ? -->  True
------------------------------

 ------------------------------
CHECK: {'h': {1: {3.3: [({1: 1.0}, 5, 'hello'), ({3: 1.0}, 50, 2.6)]}}, 'ss': {1: {3.5: [({1: 1.0}, 5.0, 'hello'), ({3: 1.6}, 50.6, 'bbbello')]}}} is typing.Dict[str, typing.Dict[int, typing.Dict[float, typing.List[typing.Tuple[typing.Dict[int, float], typing.Union[int, float], str]]]]] ? -->  False
------------------------------
DType = Tuple[Optional[float], Optional[int], Optional[str]]

d: DType = (2.3, 1, "oik")
check = TySig.is_type(d, DType)
print_check(d, DType, check)

d2: DType = (None, 1, "oik")
check = TySig.is_type(d2, DType)
print_check(d2, DType, check)

d3: DType = (2.3, None, "oik")
check = TySig.is_type(d3, DType)
print_check(d3, DType, check)

d4: DType = (2.3, 1, None)
check = TySig.is_type(d4, DType)
print_check(d4, DType, check)

# False example
d5: DType = (2, 1, None)
check = TySig.is_type(d5, DType)
print_check(d5, DType, check)
 ------------------------------
CHECK: (2.3, 1, 'oik') is typing.Tuple[typing.Union[float, NoneType], typing.Union[int, NoneType], typing.Union[str, NoneType]] ? -->  True
------------------------------

 ------------------------------
CHECK: (None, 1, 'oik') is typing.Tuple[typing.Union[float, NoneType], typing.Union[int, NoneType], typing.Union[str, NoneType]] ? -->  True
------------------------------

 ------------------------------
CHECK: (2.3, None, 'oik') is typing.Tuple[typing.Union[float, NoneType], typing.Union[int, NoneType], typing.Union[str, NoneType]] ? -->  True
------------------------------

 ------------------------------
CHECK: (2.3, 1, None) is typing.Tuple[typing.Union[float, NoneType], typing.Union[int, NoneType], typing.Union[str, NoneType]] ? -->  True
------------------------------

 ------------------------------
CHECK: (2, 1, None) is typing.Tuple[typing.Union[float, NoneType], typing.Union[int, NoneType], typing.Union[str, NoneType]] ? -->  False
------------------------------
print(TySig.is_type("hello", str), "Expecting True")
print(TySig.is_type(44, int), "Expecting True")
print(TySig.is_type(3434.3434, float), "Expecting True")
print(TySig.is_type([], list), "Expecting True")
print(TySig.is_type({1: 2}, dict), "Expecting True")
print(TySig.is_type((1, 2), tuple), "Expecting True")
print(TySig.is_type("hello", int), "Expecting False")
print(TySig.is_type("jhnj", int), "Expecting False")
print(TySig.is_type(3434, float), "Expecting False")
print(TySig.is_type([], tuple), "Expecting False")
print(TySig.is_type({1: 2}, list), "Expecting False")
print(TySig.is_type((1, 2), dict), "Expecting False")
True Expecting True
True Expecting True
True Expecting True
True Expecting True
True Expecting True
True Expecting True
False Expecting False
False Expecting False
False Expecting False
False Expecting False
False Expecting False
False Expecting False

Signature examples

Apply signature decorator on top of any function, which will be type-safe - raising exceptions where granular level types are not correct.

help(TySig.signature)
Help on function signature in module tysig.tysig:

signature(classobj: bool = False, **in_vars_types)
    signature decorator applied to functions to hard check the types
    of the arguments including typing types. Also applies default values,
    and raises exceptions on failing type checks. Uses is_type function.
    
    Return types are also checked post execution of function.
    
    :param classobj: set to True if within a class (self object)
    :param in_vars_types: arguments with their types and/or default values
                          e.g. @signature(a=int, b=Union[float, str],
                                          c=List[Dict[str, Tuple[str, int]]]
    :return: applies signature checks and applies default values

Working and Non-Working examples below

@TySig.signature(a=(8, int), b=float, c=str, d=Tuple[str, List[int]])
def fn(*args, **kwargs):
    print(args)
    print(kwargs)
    return args, kwargs
# working example where the data within the data structures conform to the signature
res = fn("hello", ("dd", [1, 2]), b=2.2)
print(res == ((), {'a': 8, 'c': 'hello', 'd': ('dd', [1, 2]), 'b': 2.2}))
()
{'a': 8, 'c': 'hello', 'd': ('dd', [1, 2]), 'b': 2.2}
True
# non-working example where the data within the data structures do not conform to the signature
try:
    _ = fn("hello", ("dd", [1., 2]), b=2.2)  # list within tuple is only allowed to hold int's
except TypeError as exp:
    print(exp)
'd' type should be 'typing.Tuple[str, typing.List[int]]' instead found '<class 'tuple'>'. If you're using GenericAlias, VariadicGenericAlias, or SpecialForm types then please check the sub argument types are correct
@TySig.signature(
    idx=(0, int),
    name=Optional[str],
    dob=Union[Tuple[int, int, int], str],
    history=Dict[str, Tuple[List[Dict[int, List[Union[str, float]]]],
                            float, Optional[int]]],
    ranking=float
)
def fn2(*args, **kwargs):
    print(args)
    print(kwargs)
    return args, kwargs
# working example where the data within the data structures conform to the signature
res = fn2(
    None, (2000, 1, 1), 2.3, history={
        "gs": (
            [
                {
                    1: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3, 989.],
                    2: [2.3, 989.],
                    3: ["dzf", "JKkj"],
                    4: []
                },
                {
                    16: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3,
                         989.],
                    26: [2.3, 989.],
                    36: ["dzf", "JKkj"],
                    46: []
                }
            ], 98.98, None
        )
    }
)
()
{'idx': 0, 'name': None, 'dob': (2000, 1, 1), 'ranking': 2.3, 'history': {'gs': ([{1: ['dzf', 'JKkj', 1.0, 'jh', 98.8, 'kj', 2.3, 989.0], 2: [2.3, 989.0], 3: ['dzf', 'JKkj'], 4: []}, {16: ['dzf', 'JKkj', 1.0, 'jh', 98.8, 'kj', 2.3, 989.0], 26: [2.3, 989.0], 36: ['dzf', 'JKkj'], 46: []}], 98.98, None)}}
# non-working example where the data within the data structures do not conform to the signature
try:
    _ = fn2(
        None, (2000, 1, 1), 2.3, history={
            "gs": [  # this part of 'history' a list but should be tuple as per signature
                [
                    {
                        1: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3,
                            989.],
                        2: [2.3, 989.],
                        3: ["dzf", "JKkj"],
                        4: []
                    },
                    {
                        16: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3,
                             989.],
                        26: [2.3, 989.],
                        36: ["dzf", "JKkj"],
                        46: []
                    }
                ], 98.98, None
            ]
        }
    )
except TypeError as exp:
    print(exp)
'history' type should be 'typing.Dict[str, typing.Tuple[typing.List[typing.Dict[int, typing.List[typing.Union[str, float]]]], float, typing.Union[int, NoneType]]]' instead found '<class 'dict'>'. If you're using GenericAlias, VariadicGenericAlias, or SpecialForm types then please check the sub argument types are correct
# non-working example where the data within the data structures do not conform to the signature
try:
    _ = fn2(
        None, (2000, 1, "1"), 2.3, history={  # dob tuple, last element is str but should be int
            "gs": (
                [
                    {
                        1: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3,
                            989.],
                        2: [2.3, 989.],
                        3: ["dzf", "JKkj"],
                        4: []
                    },
                    {
                        16: ["dzf", "JKkj", 1., "jh", 98.8, "kj", 2.3,
                             989.],
                        26: [2.3, 989.],
                        36: ["dzf", "JKkj"],
                        46: []
                    }
                ], 98.98, None
            )
        }
    )
except TypeError as exp:
    print(exp)
'dob' type should be 'typing.Union[typing.Tuple[int, int, int], str]' instead found '<class 'tuple'>'. If you're using GenericAlias, VariadicGenericAlias, or SpecialForm types then please check the sub argument types are correct
from datetime import datetime
@TySig.signature(
    name_id=Union[Optional[str], int],
    favnum=(7, int),
    dob=Union[datetime, Tuple[int, int, int]]
)
def fn3(*args, **kwargs):
    print(args)
    print(kwargs)
    return args, kwargs
# working examples where the data within the data structures conform to the signature
_ = fn3(datetime(2000, 2, 20), name_id=None)
_ = fn3(8, (2000, 5, 20), name_id=None)
()
{'favnum': 7, 'dob': datetime.datetime(2000, 2, 20, 0, 0), 'name_id': None}
()
{'favnum': 8, 'dob': (2000, 5, 20), 'name_id': None}
@TySig.signature(
    idx=(0, int),
    name=Optional[str],
    dob=Union[Tuple[int, int, int], str],
    history=(
            {"1": ([{1: []}], 0., None)},
            Dict[str, Tuple[List[Dict[int, List[Union[str, float]]]],
                            float, Optional[int]]]
    ),
    ranking=float
)
def fn4(*args, **kwargs):
    print(args)
    print(kwargs)
    return args, kwargs
# working example where the data within the data structures conform to the signature
_ = fn4(None, (2000, 1, 1), 2.3)
()
{'idx': 0, 'name': None, 'dob': (2000, 1, 1), 'history': {'1': ([{1: []}], 0.0, None)}, 'ranking': 2.3}
@TySig.signature(
    var1=Tuple[str, Any, Any],
    var2=Tuple[AnyStr, Any, str],
    var3=Tuple[Any, AnyStr, Any]
)
def fn5(*args, **kwargs):
    print(args)
    print(kwargs)
    return args, kwargs
# working example where the data within the data structures conform to the signature
_ = fn5((b"2", 3, "d"), ("2.3", b"5.6", "sd"), var1=("j", 6, "k"))
()
{'var2': (b'2', 3, 'd'), 'var3': ('2.3', b'5.6', 'sd'), 'var1': ('j', 6, 'k')}
# non-working example where the data within the data structures do not conform to the signature
try:
    _ = fn5((2, 3, "5"), (2.3, "5.6", "sd"), var1=("j", 6, "k"))
except TypeError as exp:
    print(exp)
'var2' type should be 'typing.Tuple[~AnyStr, typing.Any, str]' instead found '<class 'tuple'>'. If you're using GenericAlias, VariadicGenericAlias, or SpecialForm types then please check the sub argument types are correct
# non-working example where the data within the data structures do not conform to the signature
try:
    _ = fn5((b"2", 3, "5"), ("2.3", b"5.6"), var1=("j", 6, "k"))
except TypeError as exp:
    print(exp)
'var3' type should be 'typing.Tuple[typing.Any, ~AnyStr, typing.Any]' instead found '<class 'tuple'>'. If you're using GenericAlias, VariadicGenericAlias, or SpecialForm types then please check the sub argument types are correct
# Signtaure Return Type check
@TySig.signature(num=int)
def fn15(*args, **kwargs) -> Union[
    Tuple[str, float, List[AnyStr]],
    Optional[Dict[int, List[Dict[float, Any]]]]
]:
    print(args, kwargs)
    num = kwargs.get("num")
    if num == 0:
        return "hello", 5., [b"h", "k", "lll", b"jhjh"]
    elif num == 1:
        return "hello", 5., [b"h", 5, "lll", b"jhjh"]
    elif num == 2:
        return ["hello", 5., [b"h", "5", "lll", b"jhjh"]]
    elif num == 3:
        return [b"hello", 5., [b"h", "5", "lll", b"jhjh"]]
    elif num == 4:
        return {
            1: [
                {
                    2.: "Jhjdshf"
                }
            ]
        }
    elif num == 5:
        return {
            1.: [
                {
                    2.: "Jhjdshf"
                }
            ]
        }
    elif num == 6:
        return {
            1: [
                {
                    2: "Jhjdshf"
                }
            ]
        }
    else:
        return None
ret = fn15(0)
print(ret)
() {'num': 0}
('hello', 5.0, [b'h', 'k', 'lll', b'jhjh'])
try:
    _ = fn15(1)
except TypeError as exp:
    print(exp)
() {'num': 1}
Return object type does not match signature return type typing.Union[typing.Tuple[str, float, typing.List[~AnyStr]], typing.Dict[int, typing.List[typing.Dict[float, typing.Any]]], NoneType]
try:
    _ = fn15(2)
except TypeError as exp:
    print(exp)
() {'num': 2}
Return object type does not match signature return type typing.Union[typing.Tuple[str, float, typing.List[~AnyStr]], typing.Dict[int, typing.List[typing.Dict[float, typing.Any]]], NoneType]
try:
    _ = fn15(3)
except TypeError as exp:
    print(exp)
() {'num': 3}
Return object type does not match signature return type typing.Union[typing.Tuple[str, float, typing.List[~AnyStr]], typing.Dict[int, typing.List[typing.Dict[float, typing.Any]]], NoneType]
ret = fn15(4)
print(ret)
() {'num': 4}
{1: [{2.0: 'Jhjdshf'}]}
try:
    _ = fn15(5)
except TypeError as exp:
    print(exp)
() {'num': 5}
Return object type does not match signature return type typing.Union[typing.Tuple[str, float, typing.List[~AnyStr]], typing.Dict[int, typing.List[typing.Dict[float, typing.Any]]], NoneType]
try:
    _ = fn15(6)
except TypeError as exp:
    print(exp)
() {'num': 6}
Return object type does not match signature return type typing.Union[typing.Tuple[str, float, typing.List[~AnyStr]], typing.Dict[int, typing.List[typing.Dict[float, typing.Any]]], NoneType]
ret = fn15(-1)
print(ret)
() {'num': -1}
None

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ty-sig-0.1.5.tar.gz (12.7 kB view details)

Uploaded Source

Built Distribution

ty_sig-0.1.5-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file ty-sig-0.1.5.tar.gz.

File metadata

  • Download URL: ty-sig-0.1.5.tar.gz
  • Upload date:
  • Size: 12.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.9.6

File hashes

Hashes for ty-sig-0.1.5.tar.gz
Algorithm Hash digest
SHA256 1d9410d38292545d639ea7514343c9a6eb182ceee73cfe518329ad0367ab21d6
MD5 dd4550c8af5077b378da141bab3137e5
BLAKE2b-256 4dc03b53d96dc95837b7433a7b87b2698bc422533f7cbb49b6e7c8c089307ca1

See more details on using hashes here.

File details

Details for the file ty_sig-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: ty_sig-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.9.6

File hashes

Hashes for ty_sig-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 026cbef1df2136318e86809f2f771076f0ff20124896dbc7f353b037b6355ce7
MD5 e3d222cb03ab2c2c48b2c71f7d0f2b3a
BLAKE2b-256 61d6f0d126519d8b1677d8dfe5b2faae6f09670df6d8efabe5d18d47adb9521d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page