|
1 from django.conf import settings |
|
2 from django.core.exceptions import ImproperlyConfigured |
|
3 |
|
4 _standard_context_processors = None |
|
5 |
|
6 class ContextPopException(Exception): |
|
7 "pop() has been called more times than push()" |
|
8 pass |
|
9 |
|
10 class Context(object): |
|
11 "A stack container for variable context" |
|
12 def __init__(self, dict_=None, autoescape=True): |
|
13 dict_ = dict_ or {} |
|
14 self.dicts = [dict_] |
|
15 self.autoescape = autoescape |
|
16 |
|
17 def __repr__(self): |
|
18 return repr(self.dicts) |
|
19 |
|
20 def __iter__(self): |
|
21 for d in self.dicts: |
|
22 yield d |
|
23 |
|
24 def push(self): |
|
25 d = {} |
|
26 self.dicts = [d] + self.dicts |
|
27 return d |
|
28 |
|
29 def pop(self): |
|
30 if len(self.dicts) == 1: |
|
31 raise ContextPopException |
|
32 return self.dicts.pop(0) |
|
33 |
|
34 def __setitem__(self, key, value): |
|
35 "Set a variable in the current context" |
|
36 self.dicts[0][key] = value |
|
37 |
|
38 def __getitem__(self, key): |
|
39 "Get a variable's value, starting at the current context and going upward" |
|
40 for d in self.dicts: |
|
41 if key in d: |
|
42 return d[key] |
|
43 raise KeyError(key) |
|
44 |
|
45 def __delitem__(self, key): |
|
46 "Delete a variable from the current context" |
|
47 del self.dicts[0][key] |
|
48 |
|
49 def has_key(self, key): |
|
50 for d in self.dicts: |
|
51 if key in d: |
|
52 return True |
|
53 return False |
|
54 |
|
55 __contains__ = has_key |
|
56 |
|
57 def get(self, key, otherwise=None): |
|
58 for d in self.dicts: |
|
59 if key in d: |
|
60 return d[key] |
|
61 return otherwise |
|
62 |
|
63 def update(self, other_dict): |
|
64 "Like dict.update(). Pushes an entire dictionary's keys and values onto the context." |
|
65 self.dicts = [other_dict] + self.dicts |
|
66 return other_dict |
|
67 |
|
68 # This is a function rather than module-level procedural code because we only |
|
69 # want it to execute if somebody uses RequestContext. |
|
70 def get_standard_processors(): |
|
71 global _standard_context_processors |
|
72 if _standard_context_processors is None: |
|
73 processors = [] |
|
74 for path in settings.TEMPLATE_CONTEXT_PROCESSORS: |
|
75 i = path.rfind('.') |
|
76 module, attr = path[:i], path[i+1:] |
|
77 try: |
|
78 mod = __import__(module, {}, {}, [attr]) |
|
79 except ImportError, e: |
|
80 raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e)) |
|
81 try: |
|
82 func = getattr(mod, attr) |
|
83 except AttributeError: |
|
84 raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr)) |
|
85 processors.append(func) |
|
86 _standard_context_processors = tuple(processors) |
|
87 return _standard_context_processors |
|
88 |
|
89 class RequestContext(Context): |
|
90 """ |
|
91 This subclass of template.Context automatically populates itself using |
|
92 the processors defined in TEMPLATE_CONTEXT_PROCESSORS. |
|
93 Additional processors can be specified as a list of callables |
|
94 using the "processors" keyword argument. |
|
95 """ |
|
96 def __init__(self, request, dict=None, processors=None): |
|
97 Context.__init__(self, dict) |
|
98 if processors is None: |
|
99 processors = () |
|
100 else: |
|
101 processors = tuple(processors) |
|
102 for processor in get_standard_processors() + processors: |
|
103 self.update(processor(request)) |