Skip to main content

Decorator which checks whether the function is called with the correct type of parameters

Project description

PyPI version Python application image

Strong Typing

Decorator which checks at Runtime whether the function is called with the correct type of parameters.
And raises TypeMisMatch if the used parameters in a function call where invalid.

Included decorators

from strongtyping.strong_typing import description
match_typing decorator for a function
match_class_typing decorator for a class
setter property decorator for set
getter_setter property decorator for get and set
from strongtyping.docstring_typing import description
match_docstring decorator for a function
match_class_docstring decorator for a class

The problem:

  • Highlighting
    • Some IDE's will/can highlight that one of the parameters in a function call doesn't match but you can execute the function.
  • Exception??
    • When the call raise an Exception then we know what to do but sometimes we don't get an Exception only a weird result.
def multipler(a: int, b: int):
    return a * b


product = multipler(3, 4)
# >>> 12

product_2 = multipler('Hello', 'World') # Will be highlighted in some IDE's
# >>> TypeError

product_3 = multipler('Hello', 4)
# >>> 'HelloHelloHelloHello'
# No Exception but the result isn’t really what we expect

Now we can say that we will check the types in the function body to prevent this.


def multipler(a: int, b: int):
    if isinstance(a, int) and isinstance(b, int):
        return a * b
    ...

But when your function needs a lot of different parameters with different types you have to create a lot of noising code.
And why should we then use typing in our parameters??

My solution:

I created a decorator called @match_typing which will check at runtime if the parameters which will be used when
calling this function is from the same type as you have defined.

Here are some examples from my tests

# more imports
from strongtyping.strong_typing import match_typing

@match_typing
def func_a(a: str, b: int, c: list):
    ...

func_a('1', 2, [i for i in range(5)])
# >>> True

func_a(1, 2, [i for i in range(5)])
# >>> will raise a TypeMismatch Exception

@match_typing
def func_e(a: List[Union[str, int]], b: List[Union[str, int, tuple]]):
    return f'{len(a)}-{len(b)}'

func_e([1, '2', 3, '4'], [5, ('a', 'b'), '10'])
# >>> '4-3'

func_e([5, ('a', 'b'), '10'], [1, '2', 3, datetime.date])
# >>> will raise a TypeMismatch Exception

I love python and his freedom but with the new option of adding type hints I wanted to get rid of writing if isinstance(value, whatever) in my programs.

In a bigger project, it happened that some developers used a tiny IDE and others a more advanced one which highlighted typing issues. And there the trouble began, we had a bug and after a long debugging session we found out that the issue was a wrong type of an argument, it doesn't crash the program but the output was not what anyone of us had expected.

And that only encouraged me even more to tackle this problem.

Getting Started

  • normal decorator
from strongtyping.strong_typing import match_typing

@match_typing
def foo_bar(a: str, b: int, c: list):
    ...
  • class method decorator
from strongtyping.strong_typing import match_typing

class Foo:
    ...
    @match_typing
    def foo_bar(self, a: int):
        ...
  • use a mix of typed and untyped parameters but then only the typed parameters are checked on runtime
from strongtyping.strong_typing import match_typing

@match_typing
def foo_bar(with_type_a: str, without_type_a, with_type_b: list, without_type_b):
    ...

# no exception
foo_bar('hello', 'world', [1, 2, 3], ('a', 'b'))

# will raise an exception
foo_bar(123, 'world', [1, 2, 3], ('a', 'b'))
  • add your own exception
from strongtyping.strong_typing import match_typing

class SomeException(Exception):
    pass

@match_typing(excep_raise=SomeException)
def foo_bar(with_type_a: str, without_type_a, with_type_b: list, without_type_b):
    ...
  • enable internal cache with cache_size = 1
from strongtyping.strong_typing import match_typing

class MyClass:
    pass

@match_typing(cache_size=1)
def foo_bar(a: tuple, b: MyClass):
    ...
  • disable Exception
    • You can also disable the raise of an Exception and get a warning instead this means your function will
      execute even when the parameters are wrong use only when you know what you're doing
from strongtyping.strong_typing import match_typing

@match_typing(excep_raise=None)
def multipler(a: int, b: int):
    return a * b

print(multipler('Hello', 4))
"""
/StrongTyping/strongtyping/strong_typing.py:208: RuntimeWarning: Incorrect parameters: a: <class 'int'>
  warnings.warn(msg, RuntimeWarning)
HelloHelloHelloHello
"""

At the current state, it will work with

  • builtin types like: str, int, tuple etc
  • from typing:
    • List
    • Tuple
    • Union also nested ( Tuple[Union[str, int], Union[list, tuple]] )
    • Any
    • Dict
    • Set
    • Type
    • Iterator
    • Callable
    • Generator
    • Literal
  • from types:
    • FunctionType
    • MethodType
  • with string types representation like
from strongtyping.strong_typing import match_typing

class A:
    @match_typing
    def func_a(self, a: 'A'):
        ...

match_class_typing

  • this decorator will cover each class function automatically with the match_typing decorator
from strongtyping.strong_typing import match_class_typing

@match_class_typing
class Dummy:
    attr = 100

    def a(self, val: int):
        return val * .25

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    def _my_secure_func(self, val: Union[int, float], other: 'Dummy'):
        return val * other.attr
  • this decorator supports also disabling of raising Exception and internal caching
from strongtyping.strong_typing import match_class_typing

@match_class_typing(excep_raise=None)
class Dummy:
    attr = 100

    def a(self, val: int):
        return val * 3

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    def _my_secure_func(self, val: Union[int, float], other: 'Dummy'):
        return val * other.attr
  • single class methods inside of a allready decorated class can be overwritten with the match_typing decorator
from strongtyping.strong_typing import match_class_typing
from strongtyping.strong_typing import match_typing

@match_class_typing
class Dummy:
    attr = 100

    @match_typing(excep_raise=None)  # this decorator will be used
    def a(self, val: int):
        return val * 3

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    def _my_secure_func(self, val: Union[int, float], other: 'Dummy'):
        return val * other.attr

setter

this decorator can replace your @foo.setter from property and check your typing

from strongtyping.strong_typing import setter

class Dummy:
    attr = 100
    val = 'foo'

    @setter
    def b(self, val: str):
        self.val = val

d = Dummy()
d.b == 'foo'  # will raise AttributeError 

d.b = 'bar'  # works like a charm

d.b = 1  # will raise TypeMisMatch

getter_setter

this decorator can replace @propery from property and check your typing

from strongtyping.strong_typing import getter_setter

class Dummy:
    attr = 100

    @getter_setter  # here you will have all in one place (DRY) 
    def c(self, val: int = None):
        if val is not None:
            self.attr = val
        return self.attr

d = Dummy()
d.c == 100  # works like a charm

d.c = 1  # works like a charm

d.c = 'foobar'  # will raise TypeMisMatch

reST docstrings

When working with docstrings in reST style format use the decorator match_docstring

from strongtyping.docstring_typing import match_docstring

@match_docstring
def func_a(a):
    """
    :param a:
    :type a: list
    ...
    """

@match_docstring
def func_a(a, b):
    """
    :param int a: foo
    :vartype b: str
    ...
    """

@match_docstring
def func_a(a, b):
    """
    :parameter int a: foo
    :argument str b: bar
    ...
    """

At the current state, it will work with basically everything which is written here https://gist.github.com/jesuGMZ/d83b5e9de7ccc16f71c02adf7d2f3f44

  • extended with support for
    • Iterator
    • Callable
    • Generator
    • FunctionType
    • MethodType

please check tests/test_typing to see what is supported and if something is missing feel free to create an issue.

match_class_docstring

  • this decorator will cover each class function automatically with the match_docstring decorator
from strongtyping.docstring_typing import match_class_docstring

@match_class_docstring
class Dummy:
    attr = 100

    def a(self, val: int):
        """
        :param int val: foo
        """
        return val * .25

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    def _my_secure_func(self, val, other):
        """
        :param val: foo
        :type val: int or float
        :param other: Dummy
        :return:
        """
        return val * other.attr
  • this decorator supports also disabling of raising Exception and internal caching
from strongtyping.docstring_typing import match_class_docstring

@match_class_docstring(excep_raise=None)
class Dummy:
    attr = 100

    def a(self, val: int):
        """
        :param int val: foo
        """
        return val * 5

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    def _my_secure_func(self, val, other):
        """
        :param val: foo
        :type val: int or float
        :param other: this class
        :type other: Dummy
        :return:
        """
        return val * other.attr
  • single class methods inside of a allready decorated class can be overwritten with the match_docstring decorator
from strongtyping.docstring_typing import match_class_docstring
from strongtyping.docstring_typing import match_docstring

@match_class_docstring
class Other:
    attr = 100

    def a(self, val: int):
        """
        :param int val: foo
        """
        return val * 2

    def b(self):
        return 'b'

    def c(self):
        return 'c'

    @match_docstring(excep_raise=None)  # this decorator will be used
    def _my_secure_func(self, other):
        """
        :param other: instance of same class
        :type other: Other
        :return:
        """
        return 2 * other.attr

Package

Tested for Versions

  • 3.6, 3.7, 3.8, 3.9.0b4

Installing

  • pip install strongtyping

Versioning

  • For the versions available, see the tags on this repository.

Authors

  • Felix Eisenmenger

License

  • This project is licensed under the MIT License - see the LICENSE.md file for details

Special thanks

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

strongtyping-1.4.2.tar.gz (14.5 kB view details)

Uploaded Source

Built Distribution

strongtyping-1.4.2-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file strongtyping-1.4.2.tar.gz.

File metadata

  • Download URL: strongtyping-1.4.2.tar.gz
  • Upload date:
  • Size: 14.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.3

File hashes

Hashes for strongtyping-1.4.2.tar.gz
Algorithm Hash digest
SHA256 ec3047602587067940a0956497d7b11686b6f77d40f8733fdb735f9c03acbfd0
MD5 2c66b87c34a361abeb1687663d619aac
BLAKE2b-256 86841a4d14dc9c8dd923c6caa9c865a7521190e7b159f6d8d4c5ff3e7555b941

See more details on using hashes here.

File details

Details for the file strongtyping-1.4.2-py3-none-any.whl.

File metadata

  • Download URL: strongtyping-1.4.2-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.8.3

File hashes

Hashes for strongtyping-1.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6adffe720522116e4e7ae16f64c323c3aff0fa19e49d5d1fa43ed6974dce4438
MD5 16642d57f6a89cb708e46182eb8dfee5
BLAKE2b-256 82b901324ce5131c67b31ee9bedaeed13c1d5157aaffe90455b0b80c57b470e0

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