Source code for devhelpers.isatomic

# devhelpers - A Development Toolbox
# Copyright (c) 2021  Michael Sasser <Michael@MichaelSasser.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from functools import wraps
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import TypeVar
from typing import Union


__author__: str = "Michael Sasser"
__email__: str = "Michael@MichaelSasser.org"


ReturnType = TypeVar("ReturnType")


[docs]class NotAtomicError(Exception): def __init__(self, result_1: Any, result_n: Any, iteration: int): msg: str = ( "The function is not atomic! This was confirmed during iteration " f"{iteration}. The return value of the first iteratrion was " f'"{result_1}". The result of iteration {iteration} is ' f'"{result_n}".' ) super().__init__(msg)
[docs]def isatomic( arg: Union[Callable[..., ReturnType], int] ) -> Callable[..., ReturnType]: """Use the decorator to check if a function is atomic. The decorator runs a function over and over again and compares the output. When the output changes, it raises a `NotAtomicError`. Examples -------- .. code-block:: python @isatomic(100) def foo(a, b): pass @isatomic def bar(c, d): pass :param arg: the callable or number of runs """ def isatomic_( func: Callable[..., ReturnType] ) -> Callable[..., ReturnType]: @wraps(func) def wrapper(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: result_1: Any = func(*args, **kwargs) for i in range(2, n + 1): result_n: Any = func(*args, **kwargs) if result_n != result_1: raise NotAtomicError(result_1, result_n, i) return result_1 return wrapper # arg can be func, if no n provided. -> @isatomic # arg can be n, if n provided. -> @isatomic(100) n = 2 if isinstance(arg, int): n = arg return isatomic_ # type: ignore return isatomic_(arg)
# vim: set ft=python :