# SPDX-License-Identifier: GPLv3-or-later
# Copyright © 2025 pygaindalf Rui Pinheiro
from typing import TYPE_CHECKING, Unpack
from typing import cast as typing_cast
from . import lib
from .callable_decorator import CallguardCallableDecorator
if TYPE_CHECKING:
from .types import CallguardOptions, CallguardWrapped
# MARK: Classmethod Decorator
type WrappedClassmethod[T: object, **P, R] = classmethod[T, P, R]
[docs]
class CallguardClassmethodDecorator[T: object, **P, R]:
[docs]
def __init__(self, **options: Unpack[CallguardOptions[T, P, R]]) -> None:
self.options = options
def __call__(self, method: WrappedClassmethod) -> WrappedClassmethod:
return self.guard(method, **self.options)
[docs]
@staticmethod
def guard(method: WrappedClassmethod, **callguard_options: Unpack[CallguardOptions[T, P, R]]) -> WrappedClassmethod[T, P, R]:
if not lib.callguard_enabled(method):
return method
callguarded = typing_cast("CallguardWrapped[type[T], P, R]", CallguardCallableDecorator.guard(method.__func__, **callguard_options))
if callguarded is method.__func__:
return method
result = classmethod(callguarded)
result.__doc__ = method.__doc__
return result
[docs]
def callguard_classmethod[T: object, **P, R](**callguard_options: Unpack[CallguardOptions[T, P, R]]) -> CallguardClassmethodDecorator[T, P, R]:
return CallguardClassmethodDecorator(**callguard_options)
# MARK: Property Decorator
[docs]
class CallguardPropertyDecorator[T: object, **P, R]:
[docs]
def __init__(self, **options: Unpack[CallguardOptions[T, P, R]]) -> None:
self.options = options
def __call__(self, prop: property) -> property:
return self.guard(prop, **self.options)
[docs]
@staticmethod
def guard(method: property, **callguard_options: Unpack[CallguardOptions[T, P, R]]) -> property:
if not lib.callguard_enabled(method):
return method
getter = method.fget
setter = method.fset
deleter = method.fdel
orig_name = callguard_options.get("method_name", method.__name__)
callguard_options["method_name"] = f"{orig_name}"
callguarded_getter = CallguardCallableDecorator.guard(getter, **callguard_options) if getter else None
callguard_options["method_name"] = f"{orig_name}.setter"
callguarded_setter = CallguardCallableDecorator.guard(setter, **callguard_options) if setter else None
callguard_options["method_name"] = f"{orig_name}.deleter"
callguarded_deleter = CallguardCallableDecorator.guard(deleter, **callguard_options) if deleter else None
if callguarded_getter is getter and callguarded_setter is setter and callguarded_deleter is deleter:
return method
return property(callguarded_getter, callguarded_setter, callguarded_deleter, method.__doc__)
[docs]
def callguard_property[T: object, **P, R](**callguard_options: Unpack[CallguardOptions[T, P, R]]) -> CallguardPropertyDecorator[T, P, R]:
return CallguardPropertyDecorator(**callguard_options)