Требовать декорирование реализации абстрактного метода

Рейтинг: 4Ответов: 1Опубликовано: 15.03.2023

Как сделать так, чтобы в классе реализующем C, требовалось продекорировать method_one и method_two переопределённым декоратором decorator? PyCharm молчит если убрать декоратор над реализованным методом.

class C(abc.ABC):
    @staticmethod
    @abc.abstractmethod
    def decorator(function):
        def wrapper(self, *args, **kwargs):
            """"""

        return wrapper

    @decorator  # <-
    @abc.abstractmethod
    def method_one(self, *args, **kwargs):
        """"""

    @decorator  # <-
    @abc.abstractmethod
    def method_two(self, *args, **kwargs):
        """"""

Ответы

▲ 0

Проверяем был ли метод декорирован с помощью специального атрибута wrapped. Мы добавляем его вручную, но вроде бы он проставляется автоматически если использовать functools.wraps

import abc


class C(abc.ABC):
    @abc.abstractmethod
    def method_one(self, *args, **kwargs):
        """Abstract method one"""

    @abc.abstractmethod
    def method_two(self, *args, **kwargs):
        """Abstract method two"""

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)

        # Проверяем, что методы подкласса декорированы
        for method_name in ['method_one', 'method_two']:
            method = getattr(cls, method_name, None)
            if method is None:
                raise TypeError(f"{cls.__name__} must implement {method_name}")

            if not hasattr(method, "__wrapped__"):
                raise TypeError(f"{cls.__name__}.{method_name} must be decorated with a decorator")


def some_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Executing {func.__name__} with decorator")
        return func(*args, **kwargs)

    wrapper.__wrapped__ = func
    return wrapper


class D(C):
    @some_decorator
    def method_one(self, *args, **kwargs):
        print("Executing method_one")

    @some_decorator
    def method_two(self, *args, **kwargs):
        print("Executing method_two")


# Пример некорректного использования (вызывает TypeError)
class E(C):
    def method_one(self, *args, **kwargs):  # Метод не декорирован
        print("Executing method_one")

    @some_decorator
    def method_two(self, *args, **kwargs):
        print("Executing method_two")


d = D()  # Работает без ошибок

try:
    e = E()  # Ошибка
except TypeError as e:
    print(e)