web/lib/django/template/context.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/template/context.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/template/context.py	Tue May 25 02:43:45 2010 +0200
@@ -1,51 +1,53 @@
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.importlib import import_module
 
+# Cache of actual callables.
 _standard_context_processors = None
+# We need the CSRF processor no matter what the user has in their settings,
+# because otherwise it is a security vulnerability, and we can't afford to leave
+# this to human error or failure to read migration instructions.
+_builtin_context_processors =  ('django.core.context_processors.csrf',)
 
 class ContextPopException(Exception):
     "pop() has been called more times than push()"
     pass
 
-class Context(object):
-    "A stack container for variable context"
-    def __init__(self, dict_=None, autoescape=True, current_app=None):
+class BaseContext(object):
+    def __init__(self, dict_=None):
         dict_ = dict_ or {}
         self.dicts = [dict_]
-        self.autoescape = autoescape
-        self.current_app = current_app
 
     def __repr__(self):
         return repr(self.dicts)
 
     def __iter__(self):
-        for d in self.dicts:
+        for d in reversed(self.dicts):
             yield d
 
     def push(self):
         d = {}
-        self.dicts = [d] + self.dicts
+        self.dicts.append(d)
         return d
 
     def pop(self):
         if len(self.dicts) == 1:
             raise ContextPopException
-        return self.dicts.pop(0)
+        return self.dicts.pop()
 
     def __setitem__(self, key, value):
         "Set a variable in the current context"
-        self.dicts[0][key] = value
+        self.dicts[-1][key] = value
 
     def __getitem__(self, key):
         "Get a variable's value, starting at the current context and going upward"
-        for d in self.dicts:
+        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[0][key]
+        del self.dicts[-1][key]
 
     def has_key(self, key):
         for d in self.dicts:
@@ -53,21 +55,58 @@
                 return True
         return False
 
-    __contains__ = has_key
+    def __contains__(self, key):
+        return self.has_key(key)
 
     def get(self, key, otherwise=None):
-        for d in self.dicts:
+        for d in reversed(self.dicts):
             if key in d:
                 return d[key]
         return otherwise
 
+class Context(BaseContext):
+    "A stack container for variable context"
+    def __init__(self, dict_=None, autoescape=True, current_app=None):
+        self.autoescape = autoescape
+        self.current_app = current_app
+        self.render_context = RenderContext()
+        super(Context, self).__init__(dict_)
+
     def update(self, other_dict):
         "Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
         if not hasattr(other_dict, '__getitem__'):
             raise TypeError('other_dict must be a mapping (dictionary-like) object.')
-        self.dicts = [other_dict] + self.dicts
+        self.dicts.append(other_dict)
         return other_dict
 
+class RenderContext(BaseContext):
+    """
+    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.
+    """
+    def __iter__(self):
+        for d in self.dicts[-1]:
+            yield d
+
+    def has_key(self, key):
+        return key in self.dicts[-1]
+
+    def get(self, key, otherwise=None):
+        d = self.dicts[-1]
+        if key in d:
+            return d[key]
+        return otherwise
+
 # This is a function rather than module-level procedural code because we only
 # want it to execute if somebody uses RequestContext.
 def get_standard_processors():
@@ -75,7 +114,10 @@
     global _standard_context_processors
     if _standard_context_processors is None:
         processors = []
-        for path in settings.TEMPLATE_CONTEXT_PROCESSORS:
+        collect = []
+        collect.extend(_builtin_context_processors)
+        collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS)
+        for path in collect:
             i = path.rfind('.')
             module, attr = path[:i], path[i+1:]
             try: