<|file_name|>additional.py<|end_file_name|><｜fim▁begin｜>from contextlib import contextmanager
from functools import wraps

from werkzeug.local import LocalProxy, LocalStack

_additional_ctx_stack = LocalStack()

__all__ = ("current_additions", "Additional", "AdditionalManager")


@LocalProxy
def current_additions():
    """
    Proxy to the currently added requirements
    """
    rv = _additional_ctx_stack.top
    if rv is None:
        return None
    return rv[1]


def _isinstance(f):
    @wraps(f)
    def check(self, other):
        if not isinstance(other, Additional):
            return NotImplemented
        return f(self, other)

    return check


class Additional(object):
    """<｜fim▁hole｜>    Requirements can be added by passing them into the constructor or
    by calling the ``add`` method. They can be removed from this object
    by calling the ``remove`` method. To check if a requirement has been added
    to the current conext, you may call ``is_added`` or use ``in``::

        some_req in additional
        additional.is_added(some)req)

    Additional objects can be iterated and length checked::

        additional = Additional(some_req)
        assert len(additional) == 1
        assert list(additional) == [some_req]

    Additional objects may be combined and compared to each other with the following
    operators:

    ``+`` creates a new additional object by combining two others, the new
    additional supplies all requirements that both parents did.

    ``+=`` similar to ``+`` except it is an inplace update.

    ``-`` creates a new additional instance by removing any requirements from
    the first instance that are contained in the second instance.

    ``-=`` similar to ``-`` except it is an inplace update.

    ``==`` compares two additional instances and returns true if both have
    the same added requirements.

    ``!=`` similar to ``!=`` except returns true if both have different
    requirements contained in them.
    """

    def __init__(self, *requirements):
        self._requirements = set(requirements)

    def add(self, requirement, *requirements):
        self._requirements.update((requirement,) + requirements)

    def remove(self, requirement, *requirements):
        self._requirements.difference_update((requirement,) + requirements)

    @_isinstance
    def __add__(self, other):
        requirements = self._requirements | other._requirements
        return Additional(*requirements)

    @_isinstance
    def __iadd__(self, other):
        if len(other._requirements) > 0:
            self._requirements.add(*other._requirements)
        return self

    @_isinstance
    def __sub__(self, other):
        requirements = self._requirements - other._requirements
        return Additional(*requirements)

    @_isinstance
    def __isub__(self, other):
        if len(other._requirements) > 0:
            self.remove(*other._requirements)
        return self

    @_isinstance
    def __eq__(self, other):
        return self._requirements == other._requirements

    @_isinstance
    def __ne__(self, other):
        return not self == other

    def __iter__(self):
        return iter(self._requirements)

    def is_added(self, requirement):
        return requirement in self._requirements

    def __contains__(self, requirement):
        return self.is_added(requirement)

    def __len__(self):
        return len(self._requirements)

    def __bool__(self):
        return len(self) != 0

    __nonzero__ = __bool__

    def __repr__(self):
        return "Additional({!r})".format(self._requirements)


class AdditionalManager(object):
    """
    Used to manage the process of adding and removing additional requirements
    to be run. This class shouldn't be used directly, instead use
    ``allows.additional`` to access these controls.
    """

    def push(self, additional, use_parent=False):
        """
        Binds an additional to the current context, optionally use the
        current additionals in conjunction with this additional

        If ``use_parent`` is true, a new additional is created from the
        parent and child additionals rather than manipulating either
        directly.
        """
        current = self.current
        if use_parent and current:
            additional = current + additional

        _additional_ctx_stack.push((self, additional))

    def pop(self):
        """
        Pops the latest additional context.

        If the additional context was pushed by a different additional manager,
        a ``RuntimeError`` is raised.
        """
        rv = _additional_ctx_stack.pop()
        if rv is None or rv[0] is not self:
            raise RuntimeError(
                "popped wrong additional context ({} instead of {})".format(rv, self)
            )

    @property
    def current(self):
        """
        Returns the current additional context if set otherwise None
        """
        try:
            return _additional_ctx_stack.top[1]
        except TypeError:
            return None

    @contextmanager
    def additional(self, additional, use_parent=False):
        """
        Allows temporarily pushing an additional context, yields the new context
        into the following block.
        """
        self.push(additional, use_parent)
        yield self.current
        self.pop()<｜fim▁end｜>
    Container object that allows to run extra requirements on checks. These
    additional requirements will be run at most once per check and will
    occur in no guarenteed order.
