hardcoded processor for easier use of csrf protection set a variable in one of the higher contexts if it exists there otherwise in the current context return a new context with the same properties but with only the values given in values stored return self dicts as one dictionary compare two contexts by comparing theirs dicts attributes flatten dictionaries because they can be put in a different order set to the original template as opposed to extended or included templates during rendering see bindtemplate a stack container for storing template state rendercontext simplifies the implementation of template nodes by providing a safe place to store state between invocations of a node s render method the rendercontext also provides scoping rules that are more sensible for template local variables the render context stack is pushed before each template is rendered creating a fresh scope with nothing in it name resolution fails if a variable is not found at the top of the requestcontext stack thus variables are local to a specific template and don t affect the rendering of other templates as they would if they were stored in the normal template context this subclass of template context automatically populates itself using the processors defined in the engine s configuration additional processors can be specified as a list of callables using the processors keyword argument placeholder for context processors output empty dict for any new modifications so that context processors don t overwrite them set context processors according to the template engine s settings unset context processors this is for backwardscompatibility requestcontexts created via context new don t include values from context processors create a suitable context from a plain dict and optionally an httprequest the following pattern is required to ensure values from context override those from template context processors hard coded processor for easier use of csrf protection set a variable in one of the higher contexts if it exists there otherwise in the current context return a new context with the same properties but with only the values given in values stored return self dicts as one dictionary compare two contexts by comparing theirs dicts attributes flatten dictionaries because they can be put in a different order set to the original template as opposed to extended or included templates during rendering see bind_template a stack container for storing template state rendercontext simplifies the implementation of template nodes by providing a safe place to store state between invocations of a node s render method the rendercontext also provides scoping rules that are more sensible for template local variables the render context stack is pushed before each template is rendered creating a fresh scope with nothing in it name resolution fails if a variable is not found at the top of the requestcontext stack thus variables are local to a specific template and don t affect the rendering of other templates as they would if they were stored in the normal template context this subclass of template context automatically populates itself using the processors defined in the engine s configuration additional processors can be specified as a list of callables using the processors keyword argument placeholder for context processors output empty dict for any new modifications so that context processors don t overwrite them set context processors according to the template engine s settings unset context processors this is for backwards compatibility requestcontexts created via context new don t include values from context processors create a suitable context from a plain dict and optionally an httprequest the following pattern is required to ensure values from context override those from template context processors
from contextlib import contextmanager
from copy import copy


_builtin_context_processors = ("django.template.context_processors.csrf",)


class ContextPopException(Exception):
    "pop() has been called more times than push()"
    pass


class ContextDict(dict):
    def __init__(self, context, *args, **kwargs):
        super().__init__(*args, **kwargs)

        context.dicts.append(self)
        self.context = context

    def __enter__(self):
        return self

    def __exit__(self, *args, **kwargs):
        self.context.pop()


class BaseContext:
    def __init__(self, dict_=None):
        self._reset_dicts(dict_)

    def _reset_dicts(self, value=None):
        builtins = {"True": True, "False": False, "None": None}
        self.dicts = [builtins]
        if value is not None:
            self.dicts.append(value)

    def __copy__(self):
        duplicate = copy(super())
        duplicate.dicts = self.dicts[:]
        return duplicate

    def __repr__(self):
        return repr(self.dicts)

    def __iter__(self):
        return reversed(self.dicts)

    def push(self, *args, **kwargs):
        dicts = []
        for d in args:
            if isinstance(d, BaseContext):
                dicts += d.dicts[1:]
            else:
                dicts.append(d)
        return ContextDict(self, *dicts, **kwargs)

    def pop(self):
        if len(self.dicts) == 1:
            raise ContextPopException
        return self.dicts.pop()

    def __setitem__(self, key, value):
        "Set a variable in the current context"
        self.dicts[-1][key] = value

    def set_upward(self, key, value):
        
        context = self.dicts[-1]
        for d in reversed(self.dicts):
            if key in d:
                context = d
                break
        context[key] = value

    def __getitem__(self, key):
        "Get a variable's value, starting at the current context and going upward"
        for d in reversed(self.dicts):
            if key in d:
                return d[key]
        raise KeyError(key)

    def __delitem__(self, key):
        "Delete a variable from the current context"
        del self.dicts[-1][key]

    def __contains__(self, key):
        return any(key in d for d in self.dicts)

    def get(self, key, otherwise=None):
        for d in reversed(self.dicts):
            if key in d:
                return d[key]
        return otherwise

    def setdefault(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            self[key] = default
        return default

    def new(self, values=None):
        
        new_context = copy(self)
        new_context._reset_dicts(values)
        return new_context

    def flatten(self):
        
        flat = {}
        for d in self.dicts:
            flat.update(d)
        return flat

    def __eq__(self, other):
        
        if not isinstance(other, BaseContext):
            return NotImplemented
        
        return self.flatten() == other.flatten()


class Context(BaseContext):
    "A stack container for variable context"

    def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None):
        self.autoescape = autoescape
        self.use_l10n = use_l10n
        self.use_tz = use_tz
        self.template_name = "unknown"
        self.render_context = RenderContext()
        
        
        self.template = None
        super().__init__(dict_)

    @contextmanager
    def bind_template(self, template):
        if self.template is not None:
            raise RuntimeError("Context is already bound to a template")
        self.template = template
        try:
            yield
        finally:
            self.template = None

    def __copy__(self):
        duplicate = super().__copy__()
        duplicate.render_context = copy(self.render_context)
        return duplicate

    def update(self, other_dict):
        "Push other_dict to the stack of dictionaries in the Context"
        if not hasattr(other_dict, "__getitem__"):
            raise TypeError("other_dict must be a mapping (dictionary-like) object.")
        if isinstance(other_dict, BaseContext):
            other_dict = other_dict.dicts[1:].pop()
        return ContextDict(self, other_dict)


class RenderContext(BaseContext):
    

    template = None

    def __iter__(self):
        yield from self.dicts[-1]

    def __contains__(self, key):
        return key in self.dicts[-1]

    def get(self, key, otherwise=None):
        return self.dicts[-1].get(key, otherwise)

    def __getitem__(self, key):
        return self.dicts[-1][key]

    @contextmanager
    def push_state(self, template, isolated_context=True):
        initial = self.template
        self.template = template
        if isolated_context:
            self.push()
        try:
            yield
        finally:
            self.template = initial
            if isolated_context:
                self.pop()


class RequestContext(Context):
    

    def __init__(
        self,
        request,
        dict_=None,
        processors=None,
        use_l10n=None,
        use_tz=None,
        autoescape=True,
    ):
        super().__init__(dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape)
        self.request = request
        self._processors = () if processors is None else tuple(processors)
        self._processors_index = len(self.dicts)

        
        self.update({})

        
        
        self.update({})

    @contextmanager
    def bind_template(self, template):
        if self.template is not None:
            raise RuntimeError("Context is already bound to a template")

        self.template = template
        
        processors = template.engine.template_context_processors + self._processors
        updates = {}
        for processor in processors:
            context = processor(self.request)
            try:
                updates.update(context)
            except TypeError as e:
                raise TypeError(
                    f"Context processor {processor.__qualname__} didn't return a "
                    "dictionary."
                ) from e

        self.dicts[self._processors_index] = updates

        try:
            yield
        finally:
            self.template = None
            
            self.dicts[self._processors_index] = {}

    def new(self, values=None):
        new_context = super().new(values)
        
        
        if hasattr(new_context, "_processors_index"):
            del new_context._processors_index
        return new_context


def make_context(context, request=None, **kwargs):
    
    if context is not None and not isinstance(context, dict):
        raise TypeError(
            "context must be a dict rather than %s." % context.__class__.__name__
        )
    if request is None:
        context = Context(context, **kwargs)
    else:
        
        
        original_context = context
        context = RequestContext(request, **kwargs)
        if original_context:
            context.push(original_context)
    return context