Skip to content
Snippets Groups Projects
Commit fe823ebd authored by Mike Bayer's avatar Mike Bayer
Browse files

- more cleanup

- move Namespace docs inline to attributes on Namespace
- document Cache methods, add to caching.rst
- use functools.partial for partials, part of [ticket:156]
parent 7a8dfc0b
No related branches found
No related tags found
No related merge requests found
...@@ -119,3 +119,9 @@ sections programmatically: ...@@ -119,3 +119,9 @@ sections programmatically:
# invalidate an arbitrary key # invalidate an arbitrary key
template.cache.invalidate('somekey') template.cache.invalidate('somekey')
API Reference
==============
.. autoclass:: mako.cache.Cache
:members:
:show-inheritance:
\ No newline at end of file
...@@ -334,48 +334,6 @@ API Reference ...@@ -334,48 +334,6 @@ API Reference
.. autoclass:: mako.runtime.Namespace .. autoclass:: mako.runtime.Namespace
:show-inheritance: :show-inheritance:
:members: :members:
.. py:attribute:: attr
allows access module level attributes by name. This
accessor allows templates to supply "scalar" attributes which
are particularly handy in inheritance relationships. See the
example in :ref:`inheritance_toplevel`.
.. py:attribute:: module
the Python module referenced by this Namespace.
If the namespace references a :class:`.Template`, then this module
is the equivalent of ``template.module``, i.e. the generated
module for the template.
.. py:attribute:: filename
the path of the filesystem file used for this
Namespace's module or template. If this is a pure module-based
Namespace, this evaluates to ``module.__file__``. If a
template-based namespace, it evaluates to the original
template file location.
.. py:attribute:: template
the :class:`.Template` object referenced by this
:class:`.Namespace`, if any.
.. py:attribute:: uri
the uri for this Namespace's template (i.e. whatever
was sent to :meth:`.TemplateLookup.get_template()`). This is the equivalent
of :attr:`Template.uri`.
.. py:attribute:: context
The :class:`.Context` object for this namespace.
Namespaces are often created with copies of contexts that
contain slightly different data, particularly in inheritance
scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one
can traverse an entire chain of templates that inherit from
one-another.
.. autofunction:: mako.runtime.supports_caller .. autofunction:: mako.runtime.supports_caller
......
...@@ -7,12 +7,33 @@ class BeakerMissing(object): ...@@ -7,12 +7,33 @@ class BeakerMissing(object):
raise exceptions.RuntimeException("the Beaker package is required to use cache functionality.") raise exceptions.RuntimeException("the Beaker package is required to use cache functionality.")
class Cache(object): class Cache(object):
"""Represents a data content cache made available to the module
space of a :class:`.Template` object.
:class:`.Cache` is a wrapper on top of a Beaker CacheManager object.
This object in turn references any number of "containers", each of
which defines its own backend (i.e. file, memory, memcached, etc.)
independently of the rest.
"""
def __init__(self, id, starttime): def __init__(self, id, starttime):
self.id = id self.id = id
self.starttime = starttime self.starttime = starttime
self.def_regions = {} self.def_regions = {}
def put(self, key, value, **kwargs): def put(self, key, value, **kwargs):
"""Place a value in the cache.
:param key: the value's key.
:param value: the value
:param \**kwargs: cache configuration arguments. The
backend is configured using these arguments upon first request.
Subsequent requests that use the same series of configuration
values will use that same backend.
"""
defname = kwargs.pop('defname', None) defname = kwargs.pop('defname', None)
expiretime = kwargs.pop('expiretime', None) expiretime = kwargs.pop('expiretime', None)
createfunc = kwargs.pop('createfunc', None) createfunc = kwargs.pop('createfunc', None)
...@@ -20,6 +41,16 @@ class Cache(object): ...@@ -20,6 +41,16 @@ class Cache(object):
self._get_cache(defname, **kwargs).put_value(key, starttime=self.starttime, expiretime=expiretime) self._get_cache(defname, **kwargs).put_value(key, starttime=self.starttime, expiretime=expiretime)
def get(self, key, **kwargs): def get(self, key, **kwargs):
"""Retrieve a value from the cache.
:param key: the value's key.
:param \**kwargs: cache configuration arguments. The
backend is configured using these arguments upon first request.
Subsequent requests that use the same series of configuration
values will use that same backend.
"""
defname = kwargs.pop('defname', None) defname = kwargs.pop('defname', None)
expiretime = kwargs.pop('expiretime', None) expiretime = kwargs.pop('expiretime', None)
createfunc = kwargs.pop('createfunc', None) createfunc = kwargs.pop('createfunc', None)
...@@ -27,6 +58,15 @@ class Cache(object): ...@@ -27,6 +58,15 @@ class Cache(object):
return self._get_cache(defname, **kwargs).get_value(key, starttime=self.starttime, expiretime=expiretime, createfunc=createfunc) return self._get_cache(defname, **kwargs).get_value(key, starttime=self.starttime, expiretime=expiretime, createfunc=createfunc)
def invalidate(self, key, **kwargs): def invalidate(self, key, **kwargs):
"""Invalidate a value in the cache.
:param key: the value's key.
:param \**kwargs: cache configuration arguments. The
backend is configured using these arguments upon first request.
Subsequent requests that use the same series of configuration
values will use that same backend.
"""
defname = kwargs.pop('defname', None) defname = kwargs.pop('defname', None)
expiretime = kwargs.pop('expiretime', None) expiretime = kwargs.pop('expiretime', None)
createfunc = kwargs.pop('createfunc', None) createfunc = kwargs.pop('createfunc', None)
...@@ -34,12 +74,27 @@ class Cache(object): ...@@ -34,12 +74,27 @@ class Cache(object):
self._get_cache(defname, **kwargs).remove_value(key, starttime=self.starttime, expiretime=expiretime) self._get_cache(defname, **kwargs).remove_value(key, starttime=self.starttime, expiretime=expiretime)
def invalidate_body(self): def invalidate_body(self):
"""Invalidate the cached content of the "body" method for this template.
"""
self.invalidate('render_body', defname='render_body') self.invalidate('render_body', defname='render_body')
def invalidate_def(self, name): def invalidate_def(self, name):
"""Invalidate the cached content of a particular <%def> within this template."""
self.invalidate('render_%s' % name, defname='render_%s' % name) self.invalidate('render_%s' % name, defname='render_%s' % name)
def invalidate_closure(self, name): def invalidate_closure(self, name):
"""Invalidate a nested <%def> within this template.
Caching of nested defs is a blunt tool as there is no
management of scope - nested defs that use cache tags
need to have names unique of all other nested defs in the
template, else their content will be overwritten by
each other.
"""
self.invalidate(name, defname=name) self.invalidate(name, defname=name)
def _get_cache(self, defname, type=None, **kw): def _get_cache(self, defname, type=None, **kw):
......
...@@ -31,8 +31,9 @@ class TemplateCollection(object): ...@@ -31,8 +31,9 @@ class TemplateCollection(object):
""" """
def has_template(self, uri): def has_template(self, uri):
"""Return ``True`` if this :class:`.TemplateLookup` is capable of """Return ``True`` if this :class:`.TemplateLookup` is
returning a :class:`.Template` object for the given URL. capable of returning a :class:`.Template` object for the
given URL.
:param uri: String uri of the template to be resolved. :param uri: String uri of the template to be resolved.
...@@ -47,13 +48,14 @@ class TemplateCollection(object): ...@@ -47,13 +48,14 @@ class TemplateCollection(object):
"""Return a :class:`.Template` object corresponding to the given """Return a :class:`.Template` object corresponding to the given
URL. URL.
The default implementation raises :class:`.NotImplementedError`. The default implementation raises
Implementations should raise :class:`.TemplateLookupException` if :class:`.NotImplementedError`. Implementations should
the given uri cannot be resolved. raise :class:`.TemplateLookupException` if the given uri
cannot be resolved.
:param uri: String uri of the template to be resolved. :param uri: String uri of the template to be resolved.
:param relativeto: if present, the given URI is assumed to be relative :param relativeto: if present, the given URI is assumed to
to this uri. be relative to this uri.
""" """
raise NotImplementedError() raise NotImplementedError()
...@@ -67,11 +69,13 @@ class TemplateCollection(object): ...@@ -67,11 +69,13 @@ class TemplateCollection(object):
def adjust_uri(self, uri, filename): def adjust_uri(self, uri, filename):
"""Adjust the given uri based on the calling filename. """Adjust the given uri based on the calling filename.
When this method is called from the runtime, the 'filename' parameter When this method is called from the runtime, the
is taken directly to the 'filename' attribute of the calling template. 'filename' parameter is taken directly to the 'filename'
Therefore a custom TemplateCollection subclass can place any string attribute of the calling template. Therefore a custom
identifier desired in the "filename" parameter of the Template objects TemplateCollection subclass can place any string
it constructs and have them come back here. identifier desired in the "filename" parameter of the
Template objects it constructs and have them come back
here.
""" """
return uri return uri
...@@ -100,33 +104,40 @@ class TemplateLookup(TemplateCollection): ...@@ -100,33 +104,40 @@ class TemplateLookup(TemplateCollection):
''') ''')
:param directories: A list of directory names which will be searched for :param directories: A list of directory names which will be
a particular template URI. The URI is appended to each directory and searched for a particular template URI. The URI is appended
the filesystem checked. to each directory and the filesystem checked.
:param collection_size: Approximate size of the collection used to :param collection_size: Approximate size of the collection used
store templates. If left at its default of -1, the size is unbounded, to store templates. If left at its default of -1, the size
and a plain Python dictionary is used to relate URI strings to :class:`.Template` is unbounded, and a plain Python dictionary is used to
instances. Otherwise, a least-recently-used cache object is used which relate URI strings to :class:`.Template` instances.
will maintain the size of the collection approximately to the number given. Otherwise, a least-recently-used cache object is used which
will maintain the size of the collection approximately to
the number given.
:param filesystem_checks: When at its default value of ``True``, each :param filesystem_checks: When at its default value of ``True``,
call to :meth:`TemplateLookup.get_template()` will compare the filesystem last modified each call to :meth:`TemplateLookup.get_template()` will
time to the time in which an existing :class:`.Template` object was created. compare the filesystem last modified time to the time in
This allows the :class:`.TemplateLookup` to regenerate a new :class:`.Template` which an existing :class:`.Template` object was created.
whenever the original source has been updated. Set this to ``False`` for a This allows the :class:`.TemplateLookup` to regenerate a
very minor performance increase. new :class:`.Template` whenever the original source has
been updated. Set this to ``False`` for a very minor
performance increase.
:param modulename_callable: A callable which, when present, is passed the :param modulename_callable: A callable which, when present,
path of the source file as well as the requested URI, and then returns the is passed the path of the source file as well as the
full path of the generated Python module file. This is used to inject requested URI, and then returns the full path of the
alternate schemes for Pyhton module location. If left at its default generated Python module file. This is used to inject
of ``None``, the built in system of generation based on ``module_directory`` alternate schemes for Pyhton module location. If left at
plus ``uri`` is used. its default of ``None``, the built in system of generation
based on ``module_directory`` plus ``uri`` is used.
All other keyword parameters available for :class:`.Template` are mirrored here. All other keyword parameters available for
When new :class:`.Template` objects are created, the keywords established with :class:`.Template` are mirrored here. When new
this :class:`.TemplateLookup` are passed on to each new :class:`.Template`. :class:`.Template` objects are created, the keywords
established with this :class:`.TemplateLookup` are passed on
to each new :class:`.Template`.
""" """
...@@ -289,8 +300,9 @@ class TemplateLookup(TemplateCollection): ...@@ -289,8 +300,9 @@ class TemplateLookup(TemplateCollection):
return template return template
def put_string(self, uri, text): def put_string(self, uri, text):
"""Place a new :class:`.Template` object into this :class:`.TemplateLookup`, """Place a new :class:`.Template` object into this
based on the given string of text. :class:`.TemplateLookup`, based on the given string of
text.
""" """
self._collection[uri] = Template( self._collection[uri] = Template(
...@@ -300,8 +312,9 @@ class TemplateLookup(TemplateCollection): ...@@ -300,8 +312,9 @@ class TemplateLookup(TemplateCollection):
**self.template_args) **self.template_args)
def put_template(self, uri, template): def put_template(self, uri, template):
"""Place a new :class:`.Template` object into this :class:`.TemplateLookup`, """Place a new :class:`.Template` object into this
based on the given :class:`.Template` object. :class:`.TemplateLookup`, based on the given
:class:`.Template` object.
""" """
self._collection[uri] = template self._collection[uri] = template
......
...@@ -16,10 +16,11 @@ class Node(object): ...@@ -16,10 +16,11 @@ class Node(object):
self.lineno = lineno self.lineno = lineno
self.pos = pos self.pos = pos
self.filename = filename self.filename = filename
@property
def exception_kwargs(self): def exception_kwargs(self):
return {'source':self.source, 'lineno':self.lineno, 'pos':self.pos, 'filename':self.filename} return {'source':self.source, 'lineno':self.lineno,
exception_kwargs = property(exception_kwargs) 'pos':self.pos, 'filename':self.filename}
def get_children(self): def get_children(self):
return [] return []
...@@ -43,7 +44,9 @@ class TemplateNode(Node): ...@@ -43,7 +44,9 @@ class TemplateNode(Node):
return self.nodes return self.nodes
def __repr__(self): def __repr__(self):
return "TemplateNode(%s, %r)" % (util.sorted_dict_repr(self.page_attributes), self.nodes) return "TemplateNode(%s, %r)" % (
util.sorted_dict_repr(self.page_attributes),
self.nodes)
class ControlLine(Node): class ControlLine(Node):
"""defines a control line, a line-oriented python line or end tag. """defines a control line, a line-oriented python line or end tag.
...@@ -77,7 +80,8 @@ class ControlLine(Node): ...@@ -77,7 +80,8 @@ class ControlLine(Node):
return self._undeclared_identifiers return self._undeclared_identifiers
def is_ternary(self, keyword): def is_ternary(self, keyword):
"""return true if the given keyword is a ternary keyword for this ControlLine""" """return true if the given keyword is a ternary keyword
for this ControlLine"""
return keyword in { return keyword in {
'if':set(['else', 'elif']), 'if':set(['else', 'elif']),
...@@ -186,7 +190,8 @@ class Expression(Node): ...@@ -186,7 +190,8 @@ class Expression(Node):
) )
class _TagMeta(type): class _TagMeta(type):
"""metaclass to allow Tag to produce a subclass according to its keyword""" """metaclass to allow Tag to produce a subclass according to
its keyword"""
_classmap = {} _classmap = {}
...@@ -198,7 +203,8 @@ class _TagMeta(type): ...@@ -198,7 +203,8 @@ class _TagMeta(type):
def __call__(cls, keyword, attributes, **kwargs): def __call__(cls, keyword, attributes, **kwargs):
if ":" in keyword: if ":" in keyword:
ns, defname = keyword.split(':') ns, defname = keyword.split(':')
return type.__call__(CallNamespaceTag, ns, defname, attributes, **kwargs) return type.__call__(CallNamespaceTag, ns, defname,
attributes, **kwargs)
try: try:
cls = _TagMeta._classmap[keyword] cls = _TagMeta._classmap[keyword]
...@@ -226,22 +232,25 @@ class Tag(Node): ...@@ -226,22 +232,25 @@ class Tag(Node):
__metaclass__ = _TagMeta __metaclass__ = _TagMeta
__keyword__ = None __keyword__ = None
def __init__(self, keyword, attributes, expressions, nonexpressions, required, **kwargs): def __init__(self, keyword, attributes, expressions,
nonexpressions, required, **kwargs):
"""construct a new Tag instance. """construct a new Tag instance.
this constructor not called directly, and is only called by subclasses. this constructor not called directly, and is only called
by subclasses.
keyword - the tag keyword :param keyword: the tag keyword
attributes - raw dictionary of attribute key/value pairs :param attributes: raw dictionary of attribute key/value pairs
expressions - a set of identifiers that are legal attributes, :param expressions: a set of identifiers that are legal attributes,
which can also contain embedded expressions which can also contain embedded expressions
nonexpressions - a set of identifiers that are legal attributes, :param nonexpressions: a set of identifiers that are legal
which cannot contain embedded expressions attributes, which cannot contain embedded expressions
\**kwargs - other arguments passed to the Node superclass (lineno, pos) :param \**kwargs:
other arguments passed to the Node superclass (lineno, pos)
""" """
super(Tag, self).__init__(**kwargs) super(Tag, self).__init__(**kwargs)
...@@ -251,7 +260,8 @@ class Tag(Node): ...@@ -251,7 +260,8 @@ class Tag(Node):
missing = [r for r in required if r not in self.parsed_attributes] missing = [r for r in required if r not in self.parsed_attributes]
if len(missing): if len(missing):
raise exceptions.CompileException( raise exceptions.CompileException(
"Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), "Missing attribute(s): %s" %
",".join([repr(m) for m in missing]),
**self.exception_kwargs) **self.exception_kwargs)
self.parent = None self.parent = None
self.nodes = [] self.nodes = []
...@@ -275,9 +285,9 @@ class Tag(Node): ...@@ -275,9 +285,9 @@ class Tag(Node):
code = ast.PythonCode(m.group(1).rstrip(), code = ast.PythonCode(m.group(1).rstrip(),
**self.exception_kwargs) **self.exception_kwargs)
# we aren't discarding "declared_identifiers" here, # we aren't discarding "declared_identifiers" here,
# which we do so that list comprehension-declared variables # which we do so that list comprehension-declared
# aren't counted. As yet can't find a condition that # variables aren't counted. As yet can't find a
# requires it here. # condition that requires it here.
undeclared_identifiers = \ undeclared_identifiers = \
undeclared_identifiers.union( undeclared_identifiers.union(
code.undeclared_identifiers) code.undeclared_identifiers)
...@@ -308,11 +318,11 @@ class Tag(Node): ...@@ -308,11 +318,11 @@ class Tag(Node):
def __repr__(self): def __repr__(self):
return "%s(%r, %s, %r, %r)" % (self.__class__.__name__, return "%s(%r, %s, %r, %r)" % (self.__class__.__name__,
self.keyword, self.keyword,
util.sorted_dict_repr(self.attributes), util.sorted_dict_repr(self.attributes),
(self.lineno, self.pos), (self.lineno, self.pos),
self.nodes self.nodes
) )
class IncludeTag(Tag): class IncludeTag(Tag):
__keyword__ = 'include' __keyword__ = 'include'
...@@ -334,7 +344,8 @@ class IncludeTag(Tag): ...@@ -334,7 +344,8 @@ class IncludeTag(Tag):
identifiers = self.page_args.undeclared_identifiers.\ identifiers = self.page_args.undeclared_identifiers.\
difference(set(["__DUMMY"])).\ difference(set(["__DUMMY"])).\
difference(self.page_args.declared_identifiers) difference(self.page_args.declared_identifiers)
return identifiers.union(super(IncludeTag, self).undeclared_identifiers()) return identifiers.union(super(IncludeTag, self).
undeclared_identifiers())
class NamespaceTag(Tag): class NamespaceTag(Tag):
__keyword__ = 'namespace' __keyword__ = 'namespace'
...@@ -350,8 +361,8 @@ class NamespaceTag(Tag): ...@@ -350,8 +361,8 @@ class NamespaceTag(Tag):
self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self)))) self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self))))
if not 'name' in attributes and not 'import' in attributes: if not 'name' in attributes and not 'import' in attributes:
raise exceptions.CompileException( raise exceptions.CompileException(
"'name' and/or 'import' attributes are required " "'name' and/or 'import' attributes are required "
"for <%namespace>", "for <%namespace>",
**self.exception_kwargs) **self.exception_kwargs)
def declared_identifiers(self): def declared_identifiers(self):
...@@ -386,7 +397,8 @@ class DefTag(Tag): ...@@ -386,7 +397,8 @@ class DefTag(Tag):
raise exceptions.CompileException( raise exceptions.CompileException(
"Missing parenthesis in %def", "Missing parenthesis in %def",
**self.exception_kwargs) **self.exception_kwargs)
self.function_decl = ast.FunctionDecl("def " + name + ":pass", **self.exception_kwargs) self.function_decl = ast.FunctionDecl("def " + name + ":pass",
**self.exception_kwargs)
self.name = self.function_decl.funcname self.name = self.function_decl.funcname
self.decorator = attributes.get('decorator', '') self.decorator = attributes.get('decorator', '')
self.filter_args = ast.ArgumentList( self.filter_args = ast.ArgumentList(
...@@ -414,7 +426,8 @@ class CallTag(Tag): ...@@ -414,7 +426,8 @@ class CallTag(Tag):
('args'), ('expr',), ('expr',), **kwargs) ('args'), ('expr',), ('expr',), **kwargs)
self.expression = attributes['expr'] self.expression = attributes['expr']
self.code = ast.PythonCode(self.expression, **self.exception_kwargs) self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) self.body_decl = ast.FunctionArgs(attributes.get('args', ''),
**self.exception_kwargs)
def declared_identifiers(self): def declared_identifiers(self):
return self.code.declared_identifiers.union(self.body_decl.argnames) return self.code.declared_identifiers.union(self.body_decl.argnames)
...@@ -474,7 +487,8 @@ class PageTag(Tag): ...@@ -474,7 +487,8 @@ class PageTag(Tag):
(), (),
(), (),
**kwargs) **kwargs)
self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) self.body_decl = ast.FunctionArgs(attributes.get('args', ''),
**self.exception_kwargs)
self.filter_args = ast.ArgumentList( self.filter_args = ast.ArgumentList(
attributes.get('expression_filter', ''), attributes.get('expression_filter', ''),
**self.exception_kwargs) **self.exception_kwargs)
......
...@@ -27,7 +27,7 @@ class Context(object): ...@@ -27,7 +27,7 @@ class Context(object):
self.namespaces = {} self.namespaces = {}
# "capture" function which proxies to the generic "capture" function # "capture" function which proxies to the generic "capture" function
self._data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs) self._data['capture'] = util.partial(capture, self)
# "caller" stack used by def calls with content # "caller" stack used by def calls with content
self.caller_stack = self._data['caller'] = CallerStack() self.caller_stack = self._data['caller'] = CallerStack()
...@@ -185,6 +185,14 @@ class Namespace(object): ...@@ -185,6 +185,14 @@ class Namespace(object):
"""Provides access to collections of rendering methods, which """Provides access to collections of rendering methods, which
can be local, from other templates, or from imported modules. can be local, from other templates, or from imported modules.
To access a particular rendering method referenced by a
:class:`.Namespace`, use plain attribute access::
${some_namespace.foo(x, y, z)}
:class:`.Namespace` also contains several built-in attributes
described here.
""" """
def __init__(self, name, context, module=None, def __init__(self, name, context, module=None,
...@@ -213,14 +221,49 @@ class Namespace(object): ...@@ -213,14 +221,49 @@ class Namespace(object):
else: else:
self.callables = None self.callables = None
if populate_self and self.template is not None: if populate_self and self.template is not None:
(lclcallable, lclcontext) = _populate_self_namespace(context, self.template, self_ns=self) lclcallable, lclcontext = \
_populate_self_namespace(context, self.template, self_ns=self)
template = None
"""The :class:`.Template` object referenced by this
:class:`.Namespace`, if any.
"""
context = None
"""The :class:`.Context` object for this namespace.
Namespaces are often created with copies of contexts that
contain slightly different data, particularly in inheritance
scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one
can traverse an entire chain of templates that inherit from
one-another.
"""
@property @property
def module(self): def module(self):
"""The Python module referenced by this Namespace.
If the namespace references a :class:`.Template`, then
this module is the equivalent of ``template.module``,
i.e. the generated module for the template.
"""
return self._module or self.template.module return self._module or self.template.module
@property @property
def filename(self): def filename(self):
"""The path of the filesystem file used for this
Namespace's module or template.
If this is a pure module-based
Namespace, this evaluates to ``module.__file__``. If a
template-based namespace, it evaluates to the original
template file location.
"""
if self._module: if self._module:
return self._module.__file__ return self._module.__file__
else: else:
...@@ -228,10 +271,25 @@ class Namespace(object): ...@@ -228,10 +271,25 @@ class Namespace(object):
@property @property
def uri(self): def uri(self):
"""The uri for this Namespace's template.
I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.
This is the equivalent of :attr:`Template.uri`.
"""
return self.template.uri return self.template.uri
@property @property
def attr(self): def attr(self):
"""Access module level attributes by name.
This accessor allows templates to supply "scalar"
attributes which are particularly handy in inheritance
relationships. See the example in
:ref:`inheritance_toplevel`.
"""
if not hasattr(self, '_attr'): if not hasattr(self, '_attr'):
self._attr = _NSAttr(self) self._attr = _NSAttr(self)
return self._attr return self._attr
...@@ -260,14 +318,33 @@ class Namespace(object): ...@@ -260,14 +318,33 @@ class Namespace(object):
if self.context.namespaces.has_key(key): if self.context.namespaces.has_key(key):
return self.context.namespaces[key] return self.context.namespaces[key]
else: else:
ns = Namespace(uri, self.context._copy(), templateuri=uri, calling_uri=self._templateuri) ns = Namespace(uri, self.context._copy(),
templateuri=uri,
calling_uri=self._templateuri)
self.context.namespaces[key] = ns self.context.namespaces[key] = ns
return ns return ns
def get_template(self, uri): def get_template(self, uri):
"""Return a :class:`.Template` from the given uri.
The uri resolution is relative to the uri of this :class:`.Namespace`
object's :class:`.Template`.
"""
return _lookup_template(self.context, uri, self._templateuri) return _lookup_template(self.context, uri, self._templateuri)
def get_cached(self, key, **kwargs): def get_cached(self, key, **kwargs):
"""Return a value from the :class:`.Cache` referenced by this
:class:`.Namespace` object's :class:`.Template`.
The advantage to this method versus direct access to the
:class:`.Cache` is that the configuration parameters
declared in ``<%page>`` take effect here, thereby calling
up the same configured backend as that configured
by ``<%page>``.
"""
if self.template: if self.template:
if not self.template.cache_enabled: if not self.template.cache_enabled:
createfunc = kwargs.get('createfunc', None) createfunc = kwargs.get('createfunc', None)
...@@ -286,10 +363,15 @@ class Namespace(object): ...@@ -286,10 +363,15 @@ class Namespace(object):
@property @property
def cache(self): def cache(self):
"""Return the :class:`.Cache` object referenced by this :class:`.Namespace` object's
:class:`.Template`.
"""
return self.template.cache return self.template.cache
def include_file(self, uri, **kwargs): def include_file(self, uri, **kwargs):
"""include a file at the given uri""" """Include a file at the given uri"""
_include_file(self.context, uri, self._templateuri, **kwargs) _include_file(self.context, uri, self._templateuri, **kwargs)
def _populate(self, d, l): def _populate(self, d, l):
...@@ -307,13 +389,13 @@ class Namespace(object): ...@@ -307,13 +389,13 @@ class Namespace(object):
if self.template: if self.template:
def get(key): def get(key):
callable_ = self.template._get_def_callable(key) callable_ = self.template._get_def_callable(key)
return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) return util.partial(callable_, self.context)
for k in self.template.module._exports: for k in self.template.module._exports:
yield (k, get(k)) yield (k, get(k))
if self._module: if self._module:
def get(key): def get(key):
callable_ = getattr(self._module, key) callable_ = getattr(self._module, key)
return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) return util.partial(callable_, self.context)
for k in dir(self._module): for k in dir(self._module):
if k[0] != '_': if k[0] != '_':
yield (k, get(k)) yield (k, get(k))
...@@ -324,18 +406,21 @@ class Namespace(object): ...@@ -324,18 +406,21 @@ class Namespace(object):
if self.template and self.template.has_def(key): if self.template and self.template.has_def(key):
callable_ = self.template._get_def_callable(key) callable_ = self.template._get_def_callable(key)
return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) return util.partial(callable_, self.context)
if self._module and hasattr(self._module, key): if self._module and hasattr(self._module, key):
callable_ = getattr(self._module, key) callable_ = getattr(self._module, key)
return lambda *args, **kwargs:callable_(self.context, *args, **kwargs) return util.partial(callable_, self.context)
if self.inherits is not None: if self.inherits is not None:
return getattr(self.inherits, key) return getattr(self.inherits, key)
raise AttributeError("Namespace '%s' has no member '%s'" % (self.name, key)) raise AttributeError(
"Namespace '%s' has no member '%s'" %
(self.name, key))
def supports_caller(func): def supports_caller(func):
"""Apply a caller_stack compatibility decorator to a plain Python function. """Apply a caller_stack compatibility decorator to a plain
Python function.
See the example in :ref:`namespaces_python_modules`. See the example in :ref:`namespaces_python_modules`.
...@@ -350,7 +435,8 @@ def supports_caller(func): ...@@ -350,7 +435,8 @@ def supports_caller(func):
return wrap_stackframe return wrap_stackframe
def capture(context, callable_, *args, **kwargs): def capture(context, callable_, *args, **kwargs):
"""Execute the given template def, capturing the output into a buffer. """Execute the given template def, capturing the output into
a buffer.
See the example in :ref:`namespaces_python_modules`. See the example in :ref:`namespaces_python_modules`.
...@@ -358,9 +444,9 @@ def capture(context, callable_, *args, **kwargs): ...@@ -358,9 +444,9 @@ def capture(context, callable_, *args, **kwargs):
if not callable(callable_): if not callable(callable_):
raise exceptions.RuntimeException( raise exceptions.RuntimeException(
"capture() function expects a callable as " "capture() function expects a callable as "
"its argument (i.e. capture(func, *args, **kwargs))" "its argument (i.e. capture(func, *args, **kwargs))"
) )
context._push_buffer() context._push_buffer()
try: try:
callable_(*args, **kwargs) callable_(*args, **kwargs)
...@@ -391,15 +477,20 @@ def _decorate_inline(context, fn): ...@@ -391,15 +477,20 @@ def _decorate_inline(context, fn):
return decorate_render return decorate_render
def _include_file(context, uri, calling_uri, **kwargs): def _include_file(context, uri, calling_uri, **kwargs):
"""locate the template from the given uri and include it in the current output.""" """locate the template from the given uri and include it in
the current output."""
template = _lookup_template(context, uri, calling_uri) template = _lookup_template(context, uri, calling_uri)
(callable_, ctx) = _populate_self_namespace(context._clean_inheritance_tokens(), template) (callable_, ctx) = _populate_self_namespace(
context._clean_inheritance_tokens(),
template)
callable_(ctx, **_kwargs_for_include(callable_, context._orig, **kwargs)) callable_(ctx, **_kwargs_for_include(callable_, context._orig, **kwargs))
def _inherit_from(context, uri, calling_uri): def _inherit_from(context, uri, calling_uri):
"""called by the _inherit method in template modules to set up the inheritance chain at the start """called by the _inherit method in template modules to set
of a template's execution.""" up the inheritance chain at the start of a template's
execution."""
if uri is None: if uri is None:
return None return None
template = _lookup_template(context, uri, calling_uri) template = _lookup_template(context, uri, calling_uri)
...@@ -408,7 +499,10 @@ def _inherit_from(context, uri, calling_uri): ...@@ -408,7 +499,10 @@ def _inherit_from(context, uri, calling_uri):
while ih.inherits is not None: while ih.inherits is not None:
ih = ih.inherits ih = ih.inherits
lclcontext = context.locals_({'next':ih}) lclcontext = context.locals_({'next':ih})
ih.inherits = Namespace("self:%s" % template.uri, lclcontext, template = template, populate_self=False) ih.inherits = Namespace("self:%s" % template.uri,
lclcontext,
template = template,
populate_self=False)
context._data['parent'] = lclcontext._data['local'] = ih.inherits context._data['parent'] = lclcontext._data['local'] = ih.inherits
callable_ = getattr(template.module, '_mako_inherit', None) callable_ = getattr(template.module, '_mako_inherit', None)
if callable_ is not None: if callable_ is not None:
...@@ -424,7 +518,9 @@ def _inherit_from(context, uri, calling_uri): ...@@ -424,7 +518,9 @@ def _inherit_from(context, uri, calling_uri):
def _lookup_template(context, uri, relativeto): def _lookup_template(context, uri, relativeto):
lookup = context._with_template.lookup lookup = context._with_template.lookup
if lookup is None: if lookup is None:
raise exceptions.TemplateLookupException("Template '%s' has no TemplateLookup associated" % context._with_template.uri) raise exceptions.TemplateLookupException(
"Template '%s' has no TemplateLookup associated" %
context._with_template.uri)
uri = lookup.adjust_uri(uri, relativeto) uri = lookup.adjust_uri(uri, relativeto)
try: try:
return lookup.get_template(uri) return lookup.get_template(uri)
...@@ -433,7 +529,9 @@ def _lookup_template(context, uri, relativeto): ...@@ -433,7 +529,9 @@ def _lookup_template(context, uri, relativeto):
def _populate_self_namespace(context, template, self_ns=None): def _populate_self_namespace(context, template, self_ns=None):
if self_ns is None: if self_ns is None:
self_ns = Namespace('self:%s' % template.uri, context, template=template, populate_self=False) self_ns = Namespace('self:%s' % template.uri,
context, template=template,
populate_self=False)
context._data['self'] = context._data['local'] = self_ns context._data['self'] = context._data['local'] = self_ns
if hasattr(template.module, '_mako_inherit'): if hasattr(template.module, '_mako_inherit'):
ret = template.module._mako_inherit(template, context) ret = template.module._mako_inherit(template, context)
...@@ -442,7 +540,8 @@ def _populate_self_namespace(context, template, self_ns=None): ...@@ -442,7 +540,8 @@ def _populate_self_namespace(context, template, self_ns=None):
return (template.callable_, context) return (template.callable_, context)
def _render(template, callable_, args, data, as_unicode=False): def _render(template, callable_, args, data, as_unicode=False):
"""create a Context and return the string output of the given template and template callable.""" """create a Context and return the string
output of the given template and template callable."""
if as_unicode: if as_unicode:
buf = util.FastEncodingBuffer(unicode=True) buf = util.FastEncodingBuffer(unicode=True)
...@@ -457,7 +556,8 @@ def _render(template, callable_, args, data, as_unicode=False): ...@@ -457,7 +556,8 @@ def _render(template, callable_, args, data, as_unicode=False):
context._outputting_as_unicode = as_unicode context._outputting_as_unicode = as_unicode
context._with_template = template context._with_template = template
_render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data)) _render_context(template, callable_, context, *args,
**_kwargs_for_callable(callable_, data))
return context._pop_buffer().getvalue() return context._pop_buffer().getvalue()
def _kwargs_for_callable(callable_, data): def _kwargs_for_callable(callable_, data):
...@@ -484,7 +584,8 @@ def _kwargs_for_include(callable_, data, **kwargs): ...@@ -484,7 +584,8 @@ def _kwargs_for_include(callable_, data, **kwargs):
def _render_context(tmpl, callable_, context, *args, **kwargs): def _render_context(tmpl, callable_, context, *args, **kwargs):
import mako.template as template import mako.template as template
# create polymorphic 'self' namespace for this template with possibly updated context # create polymorphic 'self' namespace for this
# template with possibly updated context
if not isinstance(tmpl, template.DefTemplate): if not isinstance(tmpl, template.DefTemplate):
# if main render method, call from the base of the inheritance stack # if main render method, call from the base of the inheritance stack
(inherit, lclcontext) = _populate_self_namespace(context, tmpl) (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
...@@ -495,13 +596,16 @@ def _render_context(tmpl, callable_, context, *args, **kwargs): ...@@ -495,13 +596,16 @@ def _render_context(tmpl, callable_, context, *args, **kwargs):
_exec_template(callable_, context, args=args, kwargs=kwargs) _exec_template(callable_, context, args=args, kwargs=kwargs)
def _exec_template(callable_, context, args=None, kwargs=None): def _exec_template(callable_, context, args=None, kwargs=None):
"""execute a rendering callable given the callable, a Context, and optional explicit arguments """execute a rendering callable given the callable, a
Context, and optional explicit arguments
the contextual Template will be located if it exists, and the error handling options specified the contextual Template will be located if it exists, and
on that Template will be interpreted here. the error handling options specified on that Template will
be interpreted here.
""" """
template = context._with_template template = context._with_template
if template is not None and (template.format_exceptions or template.error_handler): if template is not None and \
(template.format_exceptions or template.error_handler):
error = None error = None
try: try:
callable_(context, *args, **kwargs) callable_(context, *args, **kwargs)
...@@ -513,7 +617,6 @@ def _exec_template(callable_, context, args=None, kwargs=None): ...@@ -513,7 +617,6 @@ def _exec_template(callable_, context, args=None, kwargs=None):
else: else:
callable_(context, *args, **kwargs) callable_(context, *args, **kwargs)
def _render_error(template, context, error): def _render_error(template, context, error):
if template.error_handler: if template.error_handler:
result = template.error_handler(context, error) result = template.error_handler(context, error)
......
...@@ -26,8 +26,8 @@ class Template(object): ...@@ -26,8 +26,8 @@ class Template(object):
representing the template text, or a filename representing a filesystem representing the template text, or a filename representing a filesystem
path to a source file. path to a source file.
:param text: textual template source. This argument is mutually exclusive :param text: textual template source. This argument is mutually
versus the "filename" parameter. exclusive versus the "filename" parameter.
:param filename: filename of the source template. This argument is :param filename: filename of the source template. This argument is
mutually exclusive versus the "text" parameter. mutually exclusive versus the "text" parameter.
...@@ -62,11 +62,12 @@ class Template(object): ...@@ -62,11 +62,12 @@ class Template(object):
string encoding is performed. See :ref:`usage_unicode`. string encoding is performed. See :ref:`usage_unicode`.
:param error_handler: Python callable which is called whenever :param error_handler: Python callable which is called whenever
compile or runtime exceptions occur. The callable is passed the compile or runtime exceptions occur. The callable is passed
current context as well as the exception. If the callable returns the current context as well as the exception. If the
``True``, the exception is considered to be handled, else it callable returns ``True``, the exception is considered to
is re-raised after the function completes. Is used to provide custom be handled, else it is re-raised after the function
error-rendering functions. completes. Is used to provide custom error-rendering
functions.
:param format_exceptions: if ``True``, exceptions which occur during :param format_exceptions: if ``True``, exceptions which occur during
the render phase of this template will be caught and the render phase of this template will be caught and
...@@ -75,42 +76,48 @@ class Template(object): ...@@ -75,42 +76,48 @@ class Template(object):
runtime exceptions are propagated outwards. runtime exceptions are propagated outwards.
:param imports: String list of Python statements, typically individual :param imports: String list of Python statements, typically individual
"import" lines, which will be placed into the module level preamble "import" lines, which will be placed into the module level
of all generated Python modules. See the example in :ref:`filtering_default_filters`. preamble of all generated Python modules. See the example
in :ref:`filtering_default_filters`.
:param input_encoding: Encoding of the template's source code. Can :param input_encoding: Encoding of the template's source code. Can
be used in lieu of the coding comment. be used in lieu of the coding comment. See
See :ref:`usage_unicode` as well as :ref:`unicode_toplevel` :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for
for details on source encoding. details on source encoding.
:param lookup: a :class:`.TemplateLookup` instance that will be used :param lookup: a :class:`.TemplateLookup` instance that will be used
for all file lookups via the ``<%namespace>``, ``<%include>``, for all file lookups via the ``<%namespace>``,
and ``<%inherit>`` tags. See :ref:`usage_templatelookup`. ``<%include>``, and ``<%inherit>`` tags. See
:ref:`usage_templatelookup`.
:param module_directory: Filesystem location where generated Python :param module_directory: Filesystem location where generated
module files will be placed. Python module files will be placed.
:param module_filename: Overrides the filename of the generated Python :param module_filename: Overrides the filename of the generated
module file. For advanced usage only. Python module file. For advanced usage only.
:param output_encoding: The encoding to use when :meth:`.render` is called. :param output_encoding: The encoding to use when :meth:`.render`
See :ref:`usage_unicode` as well as :ref:`unicode_toplevel`. is called. See :ref:`usage_unicode` as well as
:ref:`unicode_toplevel`.
:param preprocessor: Python callable which will be passed the full template :param preprocessor: Python callable which will be passed
source before it is parsed. The return result of the callable will be used the full template source before it is parsed. The return
as the template source code. result of the callable will be used as the template source
code.
:param strict_undefined: Replaces the automatic usage of ``UNDEFINED`` for :param strict_undefined: Replaces the automatic usage of
any undeclared variables not located in the :class:`.Context` with an immediate ``UNDEFINED`` for any undeclared variables not located in
raise of ``NameError``. The advantage is immediate reporting of missing the :class:`.Context` with an immediate raise of
variables which include the name. New in 0.3.6. ``NameError``. The advantage is immediate reporting of
missing variables which include the name. New in 0.3.6.
:param uri: string uri or other identifier for this template. If not provided,
the uri is generated from the filesystem path, or from the :param uri: string uri or other identifier for this template.
in-memory identity of a non-file-based template. The primary usage of the If not provided, the uri is generated from the filesystem
uri is to provide a key within :class:`.TemplateLookup`, as well as to path, or from the in-memory identity of a non-file-based
generate the file path of the generated Python module file, template. The primary usage of the uri is to provide a key
if ``module_directory`` is specified. within :class:`.TemplateLookup`, as well as to generate the
file path of the generated Python module file, if
``module_directory`` is specified.
""" """
...@@ -264,12 +271,13 @@ class Template(object): ...@@ -264,12 +271,13 @@ class Template(object):
def render(self, *args, **data): def render(self, *args, **data):
"""Render the output of this template as a string. """Render the output of this template as a string.
if the template specifies an output encoding, the string will be if the template specifies an output encoding, the string
encoded accordingly, else the output is raw (raw output uses cStringIO will be encoded accordingly, else the output is raw (raw
and can't handle multibyte characters). a Context object is created output uses cStringIO and can't handle multibyte
corresponding to the given data. Arguments that are explictly declared characters). a Context object is created corresponding
by this template's internal rendering method are also pulled from the to the given data. Arguments that are explictly declared
given \*args, \**data members. by this template's internal rendering method are also
pulled from the given \*args, \**data members.
""" """
return runtime._render(self, self.callable_, args, data) return runtime._render(self, self.callable_, args, data)
...@@ -371,7 +379,8 @@ class ModuleTemplate(Template): ...@@ -371,7 +379,8 @@ class ModuleTemplate(Template):
self.cache_enabled = cache_enabled self.cache_enabled = cache_enabled
class DefTemplate(Template): class DefTemplate(Template):
"""a Template which represents a callable def in a parent template.""" """a Template which represents a callable def in a parent
template."""
def __init__(self, parent, callable_): def __init__(self, parent, callable_):
self.parent = parent self.parent = parent
...@@ -387,11 +396,11 @@ class DefTemplate(Template): ...@@ -387,11 +396,11 @@ class DefTemplate(Template):
return self.parent.get_def(name) return self.parent.get_def(name)
class ModuleInfo(object): class ModuleInfo(object):
"""Stores information about a module currently loaded into memory, """Stores information about a module currently loaded into
provides reverse lookups of template source, module source code based on memory, provides reverse lookups of template source, module
a module's identifier. source code based on a module's identifier.
""" """
_modules = weakref.WeakValueDictionary() _modules = weakref.WeakValueDictionary()
def __init__(self, def __init__(self,
......
...@@ -43,7 +43,17 @@ def function_named(fn, name): ...@@ -43,7 +43,17 @@ def function_named(fn, name):
""" """
fn.__name__ = name fn.__name__ = name
return fn return fn
try:
from functools import partial
except:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
return newfunc
if py24: if py24:
def exception_name(exc): def exception_name(exc):
try: try:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment