Source code for stouputils.decorators.abstract
# Imports
from collections.abc import Callable
from functools import wraps
from typing import Any, overload
from .common import get_function_name, get_wrapper_name, set_wrapper_name
from .handle_error import LogLevels, handle_error
# Decorator that marks a function as abstract
@overload
def abstract[T](
func: Callable[..., T],
*,
error_log: LogLevels = LogLevels.RAISE_EXCEPTION
) -> Callable[..., T]: ...
@overload
def abstract[T](
func: None = None,
*,
error_log: LogLevels = LogLevels.RAISE_EXCEPTION
) -> Callable[[Callable[..., T]], Callable[..., T]]: ...
[docs]
def abstract[T](
func: Callable[..., T] | None = None,
*,
error_log: LogLevels = LogLevels.RAISE_EXCEPTION
) -> Callable[..., T] | Callable[[Callable[..., T]], Callable[..., T]]:
""" Decorator that marks a function as abstract.
Contrary to the :py:func:`abc.abstractmethod` decorator that raises a :py:exc:`TypeError`
when you try to instantiate a class that has abstract methods, this decorator raises
a :py:exc:`NotImplementedError` ONLY when the decorated function is called, indicating that the function
must be implemented by a subclass.
Args:
func (Callable[..., T] | None): The function to mark as abstract
error_log (LogLevels): Log level for the error handling:
- :attr:`LogLevels.NONE` - None
- :attr:`LogLevels.WARNING` - Show as warning
- :attr:`LogLevels.WARNING_TRACEBACK` - Show as warning with traceback
- :attr:`LogLevels.ERROR_TRACEBACK` - Show as error with traceback
- :attr:`LogLevels.RAISE_EXCEPTION` - Raise exception
Examples:
.. code-block:: python
>>> class Base:
... @abstract
... def method(self):
... pass
>>> Base().method()
Traceback (most recent call last):
...
NotImplementedError: Function 'method()' is abstract and must be implemented by a subclass
"""
def decorator(func: Callable[..., T]) -> Callable[..., T]:
message: str = f"Function '{get_function_name(func)}()' is abstract and must be implemented by a subclass"
if not func.__doc__:
func.__doc__ = message
@wraps(func)
@handle_error(exceptions=NotImplementedError, error_log=error_log)
def not_implemented_error(*args: tuple[Any, ...], **kwargs: dict[str, Any]) -> Any:
raise NotImplementedError(message)
set_wrapper_name(not_implemented_error, get_wrapper_name("stouputils.decorators.abstract", func))
return not_implemented_error
# Handle both @abstract and @abstract(error_log=...)
if func is None:
return decorator
return decorator(func)