Coverage for src / lexigram / admin / domain / specifications.py: 0%
36 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-11 02:25 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-11 02:25 +0800
1"""Pure-Python specification pattern for admin domain objects."""
3from __future__ import annotations
5from typing import Any, Generic, TypeVar
7T = TypeVar("T")
10class SpecificationProtocol(Generic[T]):
11 """Base specification with combinator support."""
13 def is_satisfied_by(self, candidate: T) -> bool: # pragma: no cover
14 """Return True if *candidate* satisfies this specification."""
15 raise NotImplementedError
17 def __and__(self, other: SpecificationProtocol[T]) -> AndSpec[T]:
18 return AndSpec(self, other)
20 def __or__(self, other: SpecificationProtocol[T]) -> OrSpec[T]:
21 return OrSpec(self, other)
23 def __invert__(self) -> NotSpec[T]:
24 return NotSpec(self)
27class ActiveUserSpec(SpecificationProtocol[Any]):
28 """Matches candidates whose ``is_active`` attribute is True."""
30 def is_satisfied_by(self, candidate: Any) -> bool:
31 return bool(getattr(candidate, "is_active", False))
34class HasRoleSpec(SpecificationProtocol[Any]):
35 """Matches candidates that have a specific role in their ``roles``."""
37 def __init__(self, role: str) -> None:
38 self._role = role
40 def is_satisfied_by(self, candidate: Any) -> bool:
41 roles = getattr(candidate, "roles", [])
42 return self._role in roles
45class AndSpec(SpecificationProtocol[T]):
46 """Logical AND of two specifications."""
48 def __init__(
49 self, left: SpecificationProtocol[T], right: SpecificationProtocol[T]
50 ) -> None:
51 self._left = left
52 self._right = right
54 def is_satisfied_by(self, candidate: T) -> bool:
55 return self._left.is_satisfied_by(candidate) and self._right.is_satisfied_by(
56 candidate
57 )
60class OrSpec(SpecificationProtocol[T]):
61 """Logical OR of two specifications."""
63 def __init__(
64 self, left: SpecificationProtocol[T], right: SpecificationProtocol[T]
65 ) -> None:
66 self._left = left
67 self._right = right
69 def is_satisfied_by(self, candidate: T) -> bool:
70 return self._left.is_satisfied_by(candidate) or self._right.is_satisfied_by(
71 candidate
72 )
75class NotSpec(SpecificationProtocol[T]):
76 """Logical NOT of a specification."""
78 def __init__(self, spec: SpecificationProtocol[T]) -> None:
79 self._spec = spec
81 def is_satisfied_by(self, candidate: T) -> bool:
82 return not self._spec.is_satisfied_by(candidate)