привет! 

так выглядит мой декоратор: 

def format_kwargs(kwargs):
    return '{%s}' % ', '.join(
        f'"{key}": {repr(value)}' for key, value in kwargs.items()
    )


def format_args(args, kwargs):
    parts = []
    if args:
        parts.append(f"positional args = {args}")
    if kwargs:
        kwargs_str = format_kwargs(kwargs)
        parts.append(f"keyword kwargs = {kwargs_str}")
    return ", ".join(parts) if parts else "no arguments"


def retry_deco(amount_of_retries, exceptions=None):

    if amount_of_retries < 1:
        raise ValueError("amount_of_retries can not be less than 1")

    if exceptions is None:
        exceptions = ()
    elif not isinstance(exceptions, (list, tuple)):
        exceptions = (exceptions,)
    else:
        exceptions = tuple(exceptions)

    def deco(function):
        def wrapper(*args, **kwargs):
            arguments_str = format_args(args, kwargs)
            function_name = getattr(function, '__name__', '<unnamed>')
            for attempt in range(1, amount_of_retries + 1):
                try:
                    result = function(*args, **kwargs)
                    print(
                        f'run "{function.__name__}" with {arguments_str}, '
                        f"attempt = {attempt}, result = {result}"
                    )
                    return result
                except Exception as e:
                    print(
                        f'run "{function.__name__}" with {arguments_str}, '
                        f'attempt = {attempt}, '
                        f'exception = {type(e).__name__}: {e}'
                    )
                    if isinstance(e, exceptions):
                        raise e
                    if attempt == amount_of_retries:
                        raise e

        return wrapper

    return deco

так выглядят мои тесты:

import unittest
from unittest.mock import patch
from functools import wraps


def mock_deco(*args_deco, **kwargs_deco):
    """decorator which does nothing"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args_func, **kwargs_func):
            return f(*args_func, **kwargs_func)
        return decorated_function
    return decorator


patch('parametrized_deco.retry_deco', mock_deco).start()

from parametrized_deco import retry_deco  # pylint: disable=E402


@retry_deco(3)
def function_succeeds():
    return "success"


@retry_deco(3)
def function_fails():
    raise Exception("failure")


@retry_deco(3)
def function_with_args(a, b):
    return a + b


@retry_deco(3, exceptions=[ValueError])
def function_raises_value_error():
    raise ValueError("error")


class TestRetryDecorator(unittest.TestCase):

    def test_success(self):
        result = function_succeeds()
        self.assertEqual(result, "success")

    def test_function_raises_value_error(self):
        with self.assertRaises(ValueError) as context:
            function_raises_value_error()
        self.assertEqual(str(context.exception), "error")

    def test_function_with_args(self):
        result = function_with_args(2, 3)
        self.assertEqual(result, 5)

    def test_function_fails(self):
        with self.assertRaises(Exception) as context:
            function_fails()
        self.assertEqual(str(context.exception), "failure")


if __name__ == "__main__":
    unittest.main()

помоги придумать граничные тесты