Skip to content

Commit

Permalink
Cache LazyRoleSet full contents evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
jace committed Dec 12, 2023
1 parent d83b409 commit fdf3609
Showing 1 changed file with 13 additions and 5 deletions.
18 changes: 13 additions & 5 deletions src/coaster/sqlalchemy/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ class LazyRoleSet(abc.MutableSet):
'_present',
'_not_present',
'_scanned_granted_by',
'_contents_fully_evaluated',
)

def __init__(
Expand All @@ -409,8 +410,10 @@ def __init__(
self._present: t.Set[str] = set(initial)
#: Roles the actor does not have
self._not_present: t.Set[str] = set()
# Relationships that have been scanned already
#: Relationships that have been scanned already
self._scanned_granted_by: t.Set[str] = set() # Contains relattr
#: Has :meth:`_contents` been called?
self._contents_fully_evaluated = False

def __repr__(self) -> str: # pragma: no cover
return f'LazyRoleSet({self.obj!r}, {self.actor!r}, {self._present!r})'
Expand Down Expand Up @@ -508,9 +511,11 @@ def _role_is_present(self, role: str) -> bool:

def _contents(self) -> t.Set[str]:
"""Return all available roles."""
# Populate cache (TODO: cache this step to avoid repeat checks)
for role in self.obj.__roles__:
self._role_is_present(role)
if not self._contents_fully_evaluated:
# Populate cache
for role in self.obj.__roles__:
self._role_is_present(role)
self._contents_fully_evaluated = True
# self._present may have roles that are not specified in self.obj.__roles__,
# notably implicit roles like `all` and `auth`. Therefore we must return the
# cache instead of capturing available roles in the loop above
Expand Down Expand Up @@ -573,8 +578,11 @@ def has_any(self, roles: t.Iterable[str]) -> bool:

def copy(self) -> LazyRoleSet:
"""Return a shallow copy of the :class:`LazyRoleSet`."""
# pylint: disable=protected-access
result = LazyRoleSet(self.obj, self.actor, self._present)
result._not_present = set(self._not_present) # pylint: disable=protected-access
result._not_present = set(self._not_present)
result._scanned_granted_by = self._scanned_granted_by
result._contents_fully_evaluated = self._contents_fully_evaluated
return result

# Sets offer these names as synonyms for operators
Expand Down

0 comments on commit fdf3609

Please sign in to comment.