Skip to content

Commit

Permalink
- [bug] The Context.locals_() method becomes a private underscored
Browse files Browse the repository at this point in the history
  method, as this method has a specific internal use. The purpose
  of Context.kwargs has been clarified, in that it only delivers
  top level keyword arguments originally passed to template.render().
  [ticket:219]
  • Loading branch information
zzzeek committed Aug 3, 2013
1 parent 542ad00 commit 420a083
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
0.9.0
- [bug] The Context.locals_() method becomes a private underscored
method, as this method has a specific internal use. The purpose
of Context.kwargs has been clarified, in that it only delivers
top level keyword arguments originally passed to template.render().
[ticket:219]

- [bug] Fixed the babel plugin to properly interpret ${} sections
inside of a "call" tag, i.e. <%self:some_tag attr="${_('foo')}"/>.
Code that's subject to babel escapes in here needs to be
Expand Down
6 changes: 3 additions & 3 deletions doc/build/namespaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,9 @@ So above, the body might be called as:

${self.body(5, y=10, someval=15, delta=7)}

The :class:`.Context` object also supplies a :attr:`~.Context.kwargs` accessor, for
cases when you'd like to pass along whatever is in the context to
a ``body()`` callable:
The :class:`.Context` object also supplies a :attr:`~.Context.kwargs`
accessor, for cases when you'd like to pass along the top level context
arguments to a ``body()`` callable:

.. sourcecode:: mako

Expand Down
4 changes: 2 additions & 2 deletions mako/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from mako import compat


MAGIC_NUMBER = 8
MAGIC_NUMBER = 9

# names which are hardwired into the
# template and are not accessed via the
Expand Down Expand Up @@ -548,7 +548,7 @@ def write_def_decl(self, node, identifiers):
if not self.in_def and (
len(self.identifiers.locally_assigned) > 0 or
len(self.identifiers.argument_declared) > 0):
nameargs.insert(0, 'context.locals_(__M_locals)')
nameargs.insert(0, 'context._locals(__M_locals)')
else:
nameargs.insert(0, 'context')
self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
Expand Down
40 changes: 32 additions & 8 deletions mako/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,22 @@ def lookup(self):

@property
def kwargs(self):
"""Return the dictionary of keyword arguments associated with this
:class:`.Context`.
"""Return the dictionary of top level keyword arguments associated
with this :class:`.Context`.
This dictionary only includes the top-level arguments passed to
:meth:`.Template.render`. It does not include names produced within
the template execution such as local variable names or special names
such as ``self``, ``next``, etc.
The purpose of this dictionary is primarily for the case that
a :class:`.Template` accepts arguments via its ``<%page>`` tag,
which are normally expected to be passed via :meth:`.Template.render`,
except the template is being called in an inheritance context,
using the ``body()`` method. :attr:`.Context.kwargs` can then be
used to propagate these arguments to the inheriting template::
${next.body(**context.kwargs)}
"""
return self._kwargs.copy()
Expand Down Expand Up @@ -144,11 +158,18 @@ def _copy(self):
c.caller_stack = self.caller_stack
return c

def locals_(self, d):
def _locals(self, d):
"""Create a new :class:`.Context` with a copy of this
:class:`.Context`'s current state, updated with the given dictionary."""
:class:`.Context`'s current state,
updated with the given dictionary.
The :attr:`.Context.kwargs` collection remains
unaffected.
if len(d) == 0:
"""

if not d:
return self
c = self._copy()
c._data.update(d)
Expand All @@ -173,19 +194,22 @@ def __nonzero__(self):
return self.__bool__()

def __bool__(self):
return self._get_caller() and True or False
return len(self) and self._get_caller() and True or False

def _get_caller(self):
# this method can be removed once
# codegen MAGIC_NUMBER moves past 7
return self[-1]

def __getattr__(self, key):
return getattr(self._get_caller(), key)

def _push_frame(self):
frame = self.nextcaller or None
self.append(frame)
self.nextcaller = None
return frame

def _pop_frame(self):
self.nextcaller = self.pop()

Expand Down Expand Up @@ -721,10 +745,10 @@ def _inherit_from(context, uri, calling_uri):
ih = self_ns
while ih.inherits is not None:
ih = ih.inherits
lclcontext = context.locals_({'next':ih})
lclcontext = context._locals({'next': ih})
ih.inherits = TemplateNamespace("self:%s" % template.uri,
lclcontext,
template = template,
template=template,
populate_self=False)
context._data['parent'] = lclcontext._data['local'] = ih.inherits
callable_ = getattr(template.module, '_mako_inherit', None)
Expand Down
21 changes: 21 additions & 0 deletions test/test_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Assorted runtime unit tests
"""
from mako import runtime
import unittest
from . import eq_

class ContextTest(unittest.TestCase):
def test_locals_kwargs(self):
c = runtime.Context(None, foo='bar')
eq_(c.kwargs, {'foo': 'bar'})

d = c._locals({'zig': 'zag'})

# kwargs is the original args sent to the Context,
# it's intentionally kept separate from _data
eq_(c.kwargs, {'foo': 'bar'})
eq_(d.kwargs, {'foo': 'bar'})

eq_(d._data['zig'], 'zag')


0 comments on commit 420a083

Please sign in to comment.