Source code for flask_allows2.requirements

import operator
from abc import ABCMeta
from abc import abstractmethod
from collections.abc import Callable
from typing import Any

from .allows import _call_requirement
from .overrides import current_overrides

__all__ = (
    "Requirement",
    "ConditionalRequirement",
    "C",
    "Or",
    "And",
    "Not",
)


[docs] class Requirement(metaclass=ABCMeta): """ Base for object based Requirements in Flask-Allows. This is quite useful for requirements that have complex logic that is too much to fit inside of a single function. """
[docs] @abstractmethod def fulfill(self, user) -> bool: """ Abstract method called to verify the requirement against the current user and request. .. versionchanged:: 0.5.0 Passing request is now deprecated, pending removal in version 1.0.0 :param user: The current identity :param request: The current request. """ return NotImplemented
def __call__(self, user): return _call_requirement(self.fulfill, user) def __repr__(self): return f"<{self.__class__.__name__}()>"
[docs] class ConditionalRequirement(Requirement): """ Used to combine requirements together in ways other than all-or-nothing, such as with an or-reducer (any requirement must be True):: from flask_allows2 import Or requires(Or(user_is_admin, user_is_moderator)) or negating a requirement:: from flask_allows2 import Not requires(Not(user_logged_in)) Combinations may also nested:: Or(user_is_admin, And(user_is_moderator, HasPermission('view_admin'))) Custom combinators may be built by creating an instance of ConditionalRequirement and supplying any combination of its keyword parameters This class is also exported under the ``C`` alias. :param requirements: Collection of requirements to combine into one logical requirement :param op: Optional, Keyword only. A binary operator that accepts two booleans and returns a boolean. :param until: Optional, Keyword only. A boolean to short circuit on (e.g. if provided with True, then the first True evaluation to return from a requirement ends verification) :param negated: Optional, Keyword only. If true, then the ConditionalRequirement will return the opposite of what it actually evaluated to (e.g. ``ConditionalRequirement(user_logged_in, negated=True)`` returns False if the user is logged in) """ def __init__( self, *requirements: Callable[[type["Requirement"]], bool], **kwargs: Any ): self.requirements = requirements self.op = kwargs.get("op", operator.and_) self.until = kwargs.get("until") self.negated = kwargs.get("negated")
[docs] @classmethod def And(cls, *requirements: Callable[[type["Requirement"]], bool]): """ Short cut helper to construct a combinator that uses :meth:`operator.and_` to reduce requirement results and stops evaluating on the first False. This is also exported at the module level as ``And`` """ return cls(*requirements, op=operator.and_, until=False)
[docs] @classmethod def Or(cls, *requirements: Callable[[type["Requirement"]], bool]): """ Short cut helper to construct a combinator that uses :meth:`operator.or_` to reduce requirement results and stops evaluating on the first True. This is also exported at the module level as ``Or`` """ return cls(*requirements, op=operator.or_, until=True)
[docs] @classmethod def Not(cls, *requirements: Callable[[type["Requirement"]], bool]): """ Shortcut helper to negate a requirement or requirements. This is also exported at the module as ``Not`` """ return cls(*requirements, negated=True)
[docs] def fulfill(self, user: Any): reduced = None requirements = self.requirements # can't use is because is a proxy if current_overrides != None: # noqa: E711 requirements = tuple(r for r in requirements if r not in current_overrides) for r in requirements: result = _call_requirement(r, user) if reduced is None: reduced = result else: reduced = self.op(reduced, result) if self.until == reduced: break if reduced is not None: return not reduced if self.negated else reduced return True
def __and__(self, require: Callable[[type["Requirement"]], bool]): return self.And(self, require) def __or__(self, require: Callable[[type["Requirement"]], bool]): return self.Or(self, require) def __invert__(self): return self.Not(self) def __repr__(self): additional = [] for name in ["op", "negated", "until"]: value = getattr(self, name) if not value: continue additional.append(f"{name}={value!r}") if additional: additional = f" {', '.join(additional)}" else: additional = "" return f"<{self.__class__.__name__} requirements={self.requirements!r}{additional}>" # noqa: E501 def __eq__(self, other): return ( isinstance(other, ConditionalRequirement) and self.op == other.op and self.until == other.until and self.negated == other.negated and self.requirements == other.requirements ) def __hash__(self): return hash((self.requirements, self.op, self.until, self.negated))
(C, And, Or, Not) = ( ConditionalRequirement, ConditionalRequirement.And, ConditionalRequirement.Or, ConditionalRequirement.Not, )