# SPDX-License-Identifier: GPLv3-or-later
# Copyright © 2025 pygaindalf Rui Pinheiro
import dataclasses
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, Concatenate, NotRequired, Protocol, TypedDict, Unpack
if TYPE_CHECKING:
import re
from collections.abc import Iterable
from types import FrameType
# MARK: Wrapped Types
type CallguardWrapped[T: object, **P, R] = Callable[Concatenate[T, P], R]
type CallguardWrappedDecorator[T: object, **P, R] = Callable[[CallguardWrapped[T, P, R]], CallguardWrapped[T, P, R]]
[docs]
class CallguardWrappedDecoratorFactory[T: object, **P, R](Protocol):
@classmethod
def __call__(cls, **options: Unpack[CallguardOptions]) -> CallguardWrappedDecorator[T, P, R]: ...
# MARK: Filter
[docs]
class CallguardFilterInfo[T: object](TypedDict):
klass: type[T]
attribute: str
value: Any
guard: bool
decorate: bool
[docs]
class CallguardFilterMethod[T: object](Protocol):
@classmethod
def __call__(cls, **info: Unpack[CallguardFilterInfo[T]]) -> bool: ...
# MARK: Handler
[docs]
@dataclasses.dataclass(slots=True, frozen=True)
class CallguardHandlerInfo[T: object, **P, R]:
method_name: str
check_module: bool
check_self: bool
allow_same_class: bool
allow_same_module: bool
caller_frame: FrameType
callee_frame: FrameType
caller_self: object
callee_self: object
caller_module: str
callee_module: str
default_checker: Callable[[CallguardHandlerInfo], bool]
exception_class: type[Exception]
# MARK: Decorator Options
[docs]
class CallguardOptions[T: object, **P, R](TypedDict):
frames_up: NotRequired[int]
method_name: NotRequired[str | Callable[Concatenate[T, P], str]]
check_module: NotRequired[bool]
allow_same_class: NotRequired[bool]
allow_same_module: NotRequired[bool]
guard: NotRequired[bool]
property_setter_only: NotRequired[bool]
decorator: NotRequired[CallguardWrappedDecorator[T, P, R] | None]
decorator_factory: NotRequired[CallguardWrappedDecoratorFactory[T, P, R] | None]
[docs]
class CallguardGuardMethod[T: object, **P, R](Protocol):
@classmethod
def __call__(cls, method: T, **options: Unpack[CallguardOptions[T, P, R]]) -> T: ...
[docs]
class CallguardClassOptions[T: object](TypedDict):
ignore_patterns: NotRequired[Iterable[str | re.Pattern[str]]]
guard_private_methods: NotRequired[bool]
guard_public_methods: NotRequired[bool]
guard_skip_classmethods: NotRequired[bool]
guard_skip_instancemethods: NotRequired[bool]
guard_skip_properties: NotRequired[bool]
guard_ignore_patterns: NotRequired[Iterable[str | re.Pattern[str]]]
decorate_private_methods: NotRequired[bool]
decorate_public_methods: NotRequired[bool]
decorate_skip_classmethods: NotRequired[bool]
decorate_skip_instancemethods: NotRequired[bool]
decorate_skip_properties: NotRequired[bool]
decorate_ignore_patterns: NotRequired[Iterable[str | re.Pattern[str]]]
decorator: NotRequired[CallguardWrappedDecorator[T, ..., Any]]
decorator_factory: NotRequired[CallguardWrappedDecoratorFactory[T, ..., Any]]
allow_same_class: NotRequired[bool]
allow_same_module: NotRequired[bool]
wrap_getattribute: NotRequired[bool]
wrap_setattr: NotRequired[bool]
# MARK: Exception Classes
[docs]
class CallguardError(RuntimeError):
pass