Mako's Filtering and Buffering talks all about filters and how to use them. But is it possible to access the method used by a filter itself?
I want to properly escape a value in an attribute that will conditionally be rendered. Essentially I want to do this:
<element ${'attribute="${value | h}"' if value else ''}>
Which is obviously invalid because you can't nest expressions in strings. So I tried:
<element ${'attribute="{}"'.format(h(value)) if value else ''}>
However, this fails with a NameError: 'h' is not defined
. I would like to avoid doing:
<element
% if value:
attribute="${value | h}"
% endif
>
Or:
<element ${'attribute="' if value else ''}${value or '' | h}${'"' if value else ''}>
I know I could use one of the multiple different methods available for escaping HTML attributes: cgi.escape(value, True)
, xml.sax.saxutils.quoteattr(value)
(except it determines the quotes for you), or markupsafe.escape(value)
(third-party). I would prefer to use the method used by Mako if at all possible. Is there a way to access Mako's build-in h
using a lookup facility of some sort?
The h
escape filter is mapped to mako.filters.html_escape()
. E.g.,
from mako.filters import html_escape as h
The escape filters are mapped to the dotted names of functions in mako.filters.DEFAULT_ESCAPES
:
DEFAULT_ESCAPES = {
'x': 'filters.xml_escape',
'h': 'filters.html_escape',
'u': 'filters.url_escape',
'trim': 'filters.trim',
'entity': 'filters.html_entities_escape',
'unicode': 'unicode', # str in python 3
'decode': 'decode',
'str': 'str',
'n': 'n'
}
The n
and decode
filters are special conditions and do not map to a specific function. The others are either built-in functions or functions from mako.filters
. These can be evaluated in 2 ways:
The potentially dangerous way:
def lookup_escape(name):
from mako import filters
return eval(filters.DEFAULT_ESCAPES[name])
The safer way:
import functools
try:
import builtins
except ImportError:
import __builtin__ as builtins
import mako.filters
def lookup_escape(name):
path_parts = mako.filters.DEFAULT_ESCAPES[name].split('.')
for module in (mako, builtins):
try:
return functools.reduce(getattr, path_parts, module)
except AttributeError:
pass
raise LookupError(name)
Finally, the desired escape filter can be gotten with:
h = lookup_escape('h')