mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Allowed Context.push to behave as a context mananger.
Thanks Loic Bistuer for the review.
This commit is contained in:
committed by
Tim Graham
parent
828359e52d
commit
a3e7d73ed7
@@ -12,6 +12,21 @@ class ContextPopException(Exception):
|
||||
"pop() has been called more times than push()"
|
||||
pass
|
||||
|
||||
|
||||
class ContextDict(dict):
|
||||
def __init__(self, context, *args, **kwargs):
|
||||
super(ContextDict, self).__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(object):
|
||||
def __init__(self, dict_=None):
|
||||
self._reset_dicts(dict_)
|
||||
@@ -34,10 +49,8 @@ class BaseContext(object):
|
||||
for d in reversed(self.dicts):
|
||||
yield d
|
||||
|
||||
def push(self):
|
||||
d = {}
|
||||
self.dicts.append(d)
|
||||
return d
|
||||
def push(self, *args, **kwargs):
|
||||
return ContextDict(self, *args, **kwargs)
|
||||
|
||||
def pop(self):
|
||||
if len(self.dicts) == 1:
|
||||
@@ -83,6 +96,7 @@ class BaseContext(object):
|
||||
new_context._reset_dicts(values)
|
||||
return new_context
|
||||
|
||||
|
||||
class Context(BaseContext):
|
||||
"A stack container for variable context"
|
||||
def __init__(self, dict_=None, autoescape=True, current_app=None,
|
||||
@@ -106,6 +120,7 @@ class Context(BaseContext):
|
||||
self.dicts.append(other_dict)
|
||||
return other_dict
|
||||
|
||||
|
||||
class RenderContext(BaseContext):
|
||||
"""
|
||||
A stack container for storing Template state.
|
||||
|
||||
@@ -95,10 +95,9 @@ class FilterNode(Node):
|
||||
def render(self, context):
|
||||
output = self.nodelist.render(context)
|
||||
# Apply filters.
|
||||
context.update({'var': output})
|
||||
filtered = self.filter_expr.resolve(context)
|
||||
context.pop()
|
||||
return filtered
|
||||
with context.push(var=output):
|
||||
return self.filter_expr.resolve(context)
|
||||
|
||||
|
||||
class FirstOfNode(Node):
|
||||
def __init__(self, variables, escape=False):
|
||||
@@ -143,71 +142,69 @@ class ForNode(Node):
|
||||
parentloop = context['forloop']
|
||||
else:
|
||||
parentloop = {}
|
||||
context.push()
|
||||
try:
|
||||
values = self.sequence.resolve(context, True)
|
||||
except VariableDoesNotExist:
|
||||
values = []
|
||||
if values is None:
|
||||
values = []
|
||||
if not hasattr(values, '__len__'):
|
||||
values = list(values)
|
||||
len_values = len(values)
|
||||
if len_values < 1:
|
||||
context.pop()
|
||||
return self.nodelist_empty.render(context)
|
||||
nodelist = NodeList()
|
||||
if self.is_reversed:
|
||||
values = reversed(values)
|
||||
unpack = len(self.loopvars) > 1
|
||||
# Create a forloop value in the context. We'll update counters on each
|
||||
# iteration just below.
|
||||
loop_dict = context['forloop'] = {'parentloop': parentloop}
|
||||
for i, item in enumerate(values):
|
||||
# Shortcuts for current loop iteration number.
|
||||
loop_dict['counter0'] = i
|
||||
loop_dict['counter'] = i+1
|
||||
# Reverse counter iteration numbers.
|
||||
loop_dict['revcounter'] = len_values - i
|
||||
loop_dict['revcounter0'] = len_values - i - 1
|
||||
# Boolean values designating first and last times through loop.
|
||||
loop_dict['first'] = (i == 0)
|
||||
loop_dict['last'] = (i == len_values - 1)
|
||||
with context.push():
|
||||
try:
|
||||
values = self.sequence.resolve(context, True)
|
||||
except VariableDoesNotExist:
|
||||
values = []
|
||||
if values is None:
|
||||
values = []
|
||||
if not hasattr(values, '__len__'):
|
||||
values = list(values)
|
||||
len_values = len(values)
|
||||
if len_values < 1:
|
||||
return self.nodelist_empty.render(context)
|
||||
nodelist = NodeList()
|
||||
if self.is_reversed:
|
||||
values = reversed(values)
|
||||
unpack = len(self.loopvars) > 1
|
||||
# Create a forloop value in the context. We'll update counters on each
|
||||
# iteration just below.
|
||||
loop_dict = context['forloop'] = {'parentloop': parentloop}
|
||||
for i, item in enumerate(values):
|
||||
# Shortcuts for current loop iteration number.
|
||||
loop_dict['counter0'] = i
|
||||
loop_dict['counter'] = i+1
|
||||
# Reverse counter iteration numbers.
|
||||
loop_dict['revcounter'] = len_values - i
|
||||
loop_dict['revcounter0'] = len_values - i - 1
|
||||
# Boolean values designating first and last times through loop.
|
||||
loop_dict['first'] = (i == 0)
|
||||
loop_dict['last'] = (i == len_values - 1)
|
||||
|
||||
pop_context = False
|
||||
if unpack:
|
||||
# If there are multiple loop variables, unpack the item into
|
||||
# them.
|
||||
try:
|
||||
unpacked_vars = dict(zip(self.loopvars, item))
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
pop_context = True
|
||||
context.update(unpacked_vars)
|
||||
else:
|
||||
context[self.loopvars[0]] = item
|
||||
# In TEMPLATE_DEBUG mode provide source of the node which
|
||||
# actually raised the exception
|
||||
if settings.TEMPLATE_DEBUG:
|
||||
for node in self.nodelist_loop:
|
||||
pop_context = False
|
||||
if unpack:
|
||||
# If there are multiple loop variables, unpack the item into
|
||||
# them.
|
||||
try:
|
||||
unpacked_vars = dict(zip(self.loopvars, item))
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
pop_context = True
|
||||
context.update(unpacked_vars)
|
||||
else:
|
||||
context[self.loopvars[0]] = item
|
||||
# In TEMPLATE_DEBUG mode provide source of the node which
|
||||
# actually raised the exception
|
||||
if settings.TEMPLATE_DEBUG:
|
||||
for node in self.nodelist_loop:
|
||||
try:
|
||||
nodelist.append(node.render(context))
|
||||
except Exception as e:
|
||||
if not hasattr(e, 'django_template_source'):
|
||||
e.django_template_source = node.source
|
||||
raise
|
||||
else:
|
||||
for node in self.nodelist_loop:
|
||||
nodelist.append(node.render(context))
|
||||
except Exception as e:
|
||||
if not hasattr(e, 'django_template_source'):
|
||||
e.django_template_source = node.source
|
||||
raise
|
||||
else:
|
||||
for node in self.nodelist_loop:
|
||||
nodelist.append(node.render(context))
|
||||
if pop_context:
|
||||
# The loop variables were pushed on to the context so pop them
|
||||
# off again. This is necessary because the tag lets the length
|
||||
# of loopvars differ to the length of each set of items and we
|
||||
# don't want to leave any vars from the previous loop on the
|
||||
# context.
|
||||
context.pop()
|
||||
context.pop()
|
||||
if pop_context:
|
||||
# The loop variables were pushed on to the context so pop them
|
||||
# off again. This is necessary because the tag lets the length
|
||||
# of loopvars differ to the length of each set of items and we
|
||||
# don't want to leave any vars from the previous loop on the
|
||||
# context.
|
||||
context.pop()
|
||||
return nodelist.render(context)
|
||||
|
||||
class IfChangedNode(Node):
|
||||
@@ -500,10 +497,9 @@ class WithNode(Node):
|
||||
def render(self, context):
|
||||
values = dict([(key, val.resolve(context)) for key, val in
|
||||
six.iteritems(self.extra_context)])
|
||||
context.update(values)
|
||||
output = self.nodelist.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
with context.push(**values):
|
||||
return self.nodelist.render(context)
|
||||
|
||||
|
||||
@register.tag
|
||||
def autoescape(parser, token):
|
||||
|
||||
@@ -164,11 +164,8 @@ def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||
return t.render(Context(dictionary))
|
||||
# Add the dictionary to the context stack, ensuring it gets removed again
|
||||
# to keep the context_instance in the same state it started in.
|
||||
context_instance.update(dictionary)
|
||||
try:
|
||||
with context_instance.push(dictionary):
|
||||
return t.render(context_instance)
|
||||
finally:
|
||||
context_instance.pop()
|
||||
|
||||
def select_template(template_name_list):
|
||||
"Given a list of template names, returns the first that can be loaded."
|
||||
|
||||
@@ -47,22 +47,21 @@ class BlockNode(Node):
|
||||
|
||||
def render(self, context):
|
||||
block_context = context.render_context.get(BLOCK_CONTEXT_KEY)
|
||||
context.push()
|
||||
if block_context is None:
|
||||
context['block'] = self
|
||||
result = self.nodelist.render(context)
|
||||
else:
|
||||
push = block = block_context.pop(self.name)
|
||||
if block is None:
|
||||
block = self
|
||||
# Create new block so we can store context without thread-safety issues.
|
||||
block = BlockNode(block.name, block.nodelist)
|
||||
block.context = context
|
||||
context['block'] = block
|
||||
result = block.nodelist.render(context)
|
||||
if push is not None:
|
||||
block_context.push(self.name, push)
|
||||
context.pop()
|
||||
with context.push():
|
||||
if block_context is None:
|
||||
context['block'] = self
|
||||
result = self.nodelist.render(context)
|
||||
else:
|
||||
push = block = block_context.pop(self.name)
|
||||
if block is None:
|
||||
block = self
|
||||
# Create new block so we can store context without thread-safety issues.
|
||||
block = BlockNode(block.name, block.nodelist)
|
||||
block.context = context
|
||||
context['block'] = block
|
||||
result = block.nodelist.render(context)
|
||||
if push is not None:
|
||||
block_context.push(self.name, push)
|
||||
return result
|
||||
|
||||
def super(self):
|
||||
@@ -133,10 +132,9 @@ class BaseIncludeNode(Node):
|
||||
in six.iteritems(self.extra_context)])
|
||||
if self.isolated_context:
|
||||
return template.render(context.new(values))
|
||||
context.update(values)
|
||||
output = template.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
with context.push(**values):
|
||||
return template.render(context)
|
||||
|
||||
|
||||
class ConstantIncludeNode(BaseIncludeNode):
|
||||
def __init__(self, template_path, *args, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user