some more documentation updates and minor code cleanups. Additionally True and true in the template are the same now, same for false/False and none/None.
--HG--
branch : trunk
diff --git a/jinja2/__init__.py b/jinja2/__init__.py
index cd720a6..194390a 100644
--- a/jinja2/__init__.py
+++ b/jinja2/__init__.py
@@ -50,7 +50,7 @@
# decorators and public utilities
from jinja2.filters import environmentfilter, contextfilter
from jinja2.utils import Markup, escape, clear_caches, \
- environmentfunction, contextfunction
+ environmentfunction, contextfunction, is_undefined
__all__ = [
'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
@@ -59,5 +59,5 @@
'TemplateError', 'UndefinedError', 'TemplateNotFound',
'TemplateSyntaxError', 'TemplateAssertionError', 'environmentfilter',
'contextfilter', 'Markup', 'escape', 'environmentfunction',
- 'contextfunction', 'clear_caches'
+ 'contextfunction', 'clear_caches', 'is_undefined'
]
diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c
index f691c78..8a9a108 100644
--- a/jinja2/_speedups.c
+++ b/jinja2/_speedups.c
@@ -187,7 +187,7 @@
static PyMethodDef module_methods[] = {
{"escape", (PyCFunction)escape, METH_O,
"escape(s) -> markup\n\n"
- "Convert the characters &, <, >, and \" in string s to HTML-safe\n"
+ "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
"sequences. Use this if you need to display text that might contain\n"
"such characters in HTML. Marks return value as markup string."},
{"soft_unicode", (PyCFunction)soft_unicode, METH_O,
diff --git a/jinja2/filters.py b/jinja2/filters.py
index 0a32f9a..762b06d 100644
--- a/jinja2/filters.py
+++ b/jinja2/filters.py
@@ -23,8 +23,8 @@
def contextfilter(f):
- """Decorator for marking context dependent filters. The current context
- argument will be passed as first argument.
+ """Decorator for marking context dependent filters. The current
+ :class:`Context` will be passed as first argument.
"""
if getattr(f, 'environmentfilter', False):
raise TypeError('filter already marked as environment filter')
@@ -33,8 +33,8 @@
def environmentfilter(f):
- """Decorator for marking evironment dependent filters. The environment
- used for the template is passed to the filter as first argument.
+ """Decorator for marking evironment dependent filters. The current
+ :class:`Environment` is passed to the filter as first argument.
"""
if getattr(f, 'contextfilter', False):
raise TypeError('filter already marked as context filter')
@@ -578,7 +578,8 @@
class _GroupTuple(tuple):
__slots__ = ()
- grouper, list = (property(itemgetter(x)) for x in xrange(2))
+ grouper = property(itemgetter(0))
+ list = property(itemgetter(1))
def __new__(cls, (key, value)):
return tuple.__new__(cls, (key, list(value)))
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 0cccddf..f4b1f32 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -400,7 +400,8 @@
fields = ('name', 'ctx')
def can_assign(self):
- return self.name not in ('true', 'false', 'none')
+ return self.name not in ('true', 'false', 'none',
+ 'True', 'False', 'None')
class Literal(Expr):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 0ce4a29..7efe79c 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -454,9 +454,10 @@
def parse_primary(self, with_postfix=True):
token = self.stream.current
if token.type is 'name':
- if token.value in ('true', 'false'):
- node = nodes.Const(token.value == 'true', lineno=token.lineno)
- elif token.value == 'none':
+ if token.value in ('true', 'false', 'True', 'False'):
+ node = nodes.Const(token.value in ('true', 'True'),
+ lineno=token.lineno)
+ elif token.value in ('none', 'None'):
node = nodes.Const(None, lineno=token.lineno)
else:
node = nodes.Name(token.value, 'load', lineno=token.lineno)
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index babc3c9..2dbd569 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -117,6 +117,11 @@
return dict(self.parent, **self.vars)
def call(__self, __obj, *args, **kwargs):
+ """Call the callable with the arguments and keyword arguments
+ provided but inject the active context or environment as first
+ argument if the callable is a :func:`contextfunction` or
+ :func:`environmentfunction`.
+ """
if __debug__:
__traceback_hide__ = True
if isinstance(__obj, _context_function_types):
@@ -160,6 +165,14 @@
)
+# register the context as mutable mapping if possible
+try:
+ from collections import MutableMapping
+ MutableMapping.register(Context)
+except ImportError:
+ pass
+
+
class TemplateReference(object):
"""The `self` in templates."""
@@ -321,28 +334,6 @@
)
-def fail_with_undefined_error(self, *args, **kwargs):
- """Regular callback function for undefined objects that raises an
- `UndefinedError` on call.
- """
- if self._undefined_hint is None:
- if self._undefined_obj is None:
- hint = '%r is undefined' % self._undefined_name
- elif not isinstance(self._undefined_name, basestring):
- hint = '%r object has no element %r' % (
- self._undefined_obj.__class__.__name__,
- self._undefined_name
- )
- else:
- hint = '%r object has no attribute %r' % (
- self._undefined_obj.__class__.__name__,
- self._undefined_name
- )
- else:
- hint = self._undefined_hint
- raise self._undefined_exception(hint)
-
-
class Undefined(object):
"""The default undefined type. This undefined type can be printed and
iterated over, but every other access will raise an :exc:`UndefinedError`:
@@ -366,18 +357,36 @@
self._undefined_name = name
self._undefined_exception = exc
+ def _fail_with_undefined_error(self, *args, **kwargs):
+ """Regular callback function for undefined objects that raises an
+ `UndefinedError` on call.
+ """
+ if self._undefined_hint is None:
+ if self._undefined_obj is None:
+ hint = '%r is undefined' % self._undefined_name
+ elif not isinstance(self._undefined_name, basestring):
+ hint = '%r object has no element %r' % (
+ self._undefined_obj.__class__.__name__,
+ self._undefined_name
+ )
+ else:
+ hint = '%r object has no attribute %r' % (
+ self._undefined_obj.__class__.__name__,
+ self._undefined_name
+ )
+ else:
+ hint = self._undefined_hint
+ raise self._undefined_exception(hint)
+
__add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
__realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
__mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
__getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
- fail_with_undefined_error
+ __int__ = __float__ = __complex__ = _fail_with_undefined_error
def __str__(self):
return self.__unicode__().encode('utf-8')
- def __repr__(self):
- return 'Undefined'
-
def __unicode__(self):
return u''
@@ -391,6 +400,9 @@
def __nonzero__(self):
return False
+ def __repr__(self):
+ return 'Undefined'
+
class DebugUndefined(Undefined):
"""An undefined that returns the debug info when printed.
@@ -439,7 +451,7 @@
"""
__slots__ = ()
__iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
- fail_with_undefined_error
+ Undefined._fail_with_undefined_error
# remove remaining slots attributes, after the metaclass did the magic they
diff --git a/jinja2/utils.py b/jinja2/utils.py
index e064a25..6d1c958 100644
--- a/jinja2/utils.py
+++ b/jinja2/utils.py
@@ -63,23 +63,47 @@
def contextfunction(f):
- """This decorator can be used to mark a callable as context callable. A
- context callable is passed the active context as first argument when
- called from the template.
+ """This decorator can be used to mark a function or method context callable.
+ A context callable is passed the active :class:`Context` as first argument when
+ called from the template. This is useful if a function wants to get access
+ to the context or functions provided on the context object. For example
+ a function that returns a sorted list of template variables the current
+ template exports could look like this::
+
+ @contextcallable
+ def get_exported_names(context):
+ return sorted(context.exported_vars)
"""
f.contextfunction = True
return f
def environmentfunction(f):
- """This decorator can be used to mark a callable as environment callable.
- A environment callable is passed the current environment as first argument
- when called from the template.
+ """This decorator can be used to mark a function or method as environment
+ callable. This decorator works exactly like the :func:`contextfunction`
+ decorator just that the first argument is the active :class:`Environment`
+ and not context.
"""
f.environmentfunction = True
return f
+def is_undefined(obj):
+ """Check if the object passed is undefined. This does nothing more than
+ performing an instance check against :class:`Undefined` but looks nicer.
+ This can be used for custom filters or tests that want to react to
+ undefined variables. For example a custom default filter can look like
+ this::
+
+ def default(var, default=''):
+ if is_undefined(var):
+ return default
+ return var
+ """
+ from jinja2.runtime import Undefined
+ return isinstance(obj, Undefined)
+
+
def clear_caches():
"""Jinja2 keeps internal caches for environments and lexers. These are
used so that Jinja2 doesn't have to recreate environments and lexers all
@@ -532,6 +556,14 @@
__copy__ = copy
+# register the LRU cache as mutable mapping if possible
+try:
+ from collections import MutableMapping
+ MutableMapping.register(LRUCache)
+except ImportError:
+ pass
+
+
# we have to import it down here as the speedups module imports the
# markup type which is define above.
try: