Changeset View
Changeset View
Standalone View
Standalone View
looper/decorators.py
| def short_description(description): | import functools | ||||
| from typing import Any, Callable, TypeVar, cast | |||||
| import looper.exceptions | |||||
| F = TypeVar('F', bound=Callable[..., object]) | |||||
| def short_description(description: str) -> Callable[[F], F]: | |||||
| """Set wrapped.short_description to 'description'. | """Set wrapped.short_description to 'description'. | ||||
| This makes it possible to stick to PEP8 (two newlines before/after | This makes it possible to stick to PEP8 (two newlines before/after | ||||
| function) and still keep the short description next to the function | function) and still keep the short description next to the function | ||||
| itself. | itself. | ||||
| """ | """ | ||||
| def decorator(wrapped): | def decorator(wrapped: F) -> F: | ||||
| wrapped.short_description = description | setattr(wrapped, 'short_description', description) | ||||
| return wrapped | return wrapped | ||||
| return decorator | return decorator | ||||
| def requires_status(*required_statuses: str) -> Callable[[F], F]: | |||||
| """Function decorator to assert a model has a certain status. | |||||
| :raise looper.exceptions.IncorrectStatusError: | |||||
| """ | |||||
| _required_statuses = set(required_statuses) | |||||
| def decorator(wrapped: F) -> F: | |||||
| @functools.wraps(wrapped) | |||||
| def checker(self: Any, *args: object, **kwargs: object) -> object: | |||||
| if self.status not in _required_statuses: | |||||
| raise looper.exceptions.IncorrectStatusError( | |||||
| f'To call {self.__class__}.{wrapped}, {self} needs to have ' | |||||
| f'status in {_required_statuses} but it has {self.status!r}', | |||||
| self.status, | |||||
| _required_statuses, | |||||
| ) | |||||
| return wrapped(self, *args, **kwargs) | |||||
| return cast(F, checker) | |||||
| return decorator | |||||