Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
  • Loading branch information...
commit 9bb7e4779182490abc6e1784b0ee63d22b91b11e 1 parent d71fff0
@mitsuhiko authored
View
BIN  docs/_static/implementation.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  docs/_static/note.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
22 docs/_static/style.css
@@ -209,20 +209,25 @@ cite {
div.admonition {
margin: 10px 0 10px 0;
- padding: 10px;
+ padding: 10px 10px 10px 60px;
border: 1px solid #ccc;
- background-color: #f8f8f8;
}
div.admonition p.admonition-title {
- margin: -3px 0 5px 0;
+ background-color: #b41717;
+ color: white;
+ margin: -10px -10px 10px -60px;
+ padding: 4px 10px 4px 10px;
font-weight: bold;
- color: #b41717;
- font-size: 16px;
+ font-size: 15px;
+}
+
+div.admonition-note {
+ background: url(note.png) no-repeat 10px 40px;
}
-div.admonition p {
- margin: 0 0 0 40px;
+div.admonition-implementation {
+ background: url(implementation.png) no-repeat 10px 40px;
}
#toc {
@@ -296,7 +301,8 @@ table.indextable dl dd a {
dl.function dt,
dl.class dt,
dl.exception dt,
-dl.method dt {
+dl.method dt,
+dl.attribute dt {
font-weight: normal;
}
View
92 docs/api.rst
@@ -95,6 +95,11 @@ For more details about unicode in Python have a look at the excellent
High Level API
--------------
+The high-level API is the API you will use in the application to load and
+render Jinja2 templates. The :ref:`low-level-api` on the other side is only
+useful if you want to dig deeper into Jinja2 or :ref:`develop extensions
+<jinja-extensions>`.
+
.. autoclass:: Environment([options])
:members: from_string, get_template, join_path, extend
@@ -171,7 +176,7 @@ High Level API
possibility to enhance the error message.
.. autoclass:: Template
- :members: make_module, module, new_context
+ :members: module, make_module
.. attribute:: globals
@@ -233,12 +238,62 @@ disallows all operations beside testing if it's an undefined object.
.. autoclass:: jinja2.runtime.Undefined()
+ .. attribute:: _undefined_hint
+
+ Either `None` or an unicode string with the error message for
+ the undefined object.
+
+ .. attribute:: _undefined_obj
+
+ Either `None` or the owner object that caused the undefined object
+ to be created (for example because an attribute does not exist).
+
+ .. attribute:: _undefined_name
+
+ The name for the undefined variable / attribute or just `None`
+ if no such information exists.
+
+ .. attribute:: _undefined_exception
+
+ The exception that the undefined object wants to raise. This
+ is usually one of :exc:`UndefinedError` or :exc:`SecurityError`.
+
+ .. method:: _fail_with_undefined_error(\*args, \**kwargs)
+
+ When called with any arguments this method raises
+ :attr:`_undefined_exception` with an error message generated
+ from the undefined hints stored on the undefined object.
+
.. autoclass:: jinja2.runtime.DebugUndefined()
.. autoclass:: jinja2.runtime.StrictUndefined()
Undefined objects are created by calling :attr:`undefined`.
+.. admonition:: Implementation
+
+ :class:`Undefined` objects are implemented by overriding the special
+ `__underscore__` methods. For example the default :class:`Undefined`
+ class implements `__unicode__` in a way that it returns an empty
+ string, however `__int__` and others still fail with an exception. To
+ allow conversion to int by returning ``0`` you can implement your own::
+
+ class NullUndefined(Undefined):
+ def __int__(self):
+ return 0
+ def __float__(self):
+ return 0.0
+
+ To disallow a method, just override it and raise
+ :attr:`_undefined_exception`. Because this is a very common idom in
+ undefined objects there is the helper method
+ :meth:`_fail_with_undefined_error`. That does that automatically. Here
+ a class that works like the regular :class:`UndefinedError` but chokes
+ on iteration::
+
+ class NonIterableUndefined(Undefined):
+ __iter__ = Undefined._fail_with_undefined_error
+
The Context
-----------
@@ -283,6 +338,20 @@ The Context
blocks registered. The last item in each list is the current active
block (latest in the inheritance chain).
+ .. automethod:: jinja2.runtime.Context.call(callable, \*args, \**kwargs)
+
+
+.. admonition:: Implementation
+
+ Context is immutable for the same reason Python's frame locals are
+ immutable inside functions. Both Jinja2 and Python are not using the
+ context / frame locals as data storage for variables but only as primary
+ data source.
+
+ When a template accesses a variable the template does not define, Jinja2
+ looks up the variable in the context, after that the variable is treated
+ as if it was defined in the template.
+
.. _loaders:
@@ -330,13 +399,17 @@ functions to a Jinja2 environment.
.. function:: escape(s)
- Convert the characters &, <, >, and " in string s to HTML-safe sequences.
- Use this if you need to display text that might contain such characters
- in HTML. This function will not escaped objects that do have an HTML
- representation such as already escaped data.
+ Convert the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in string `s`
+ to HTML-safe sequences. Use this if you need to display text that might
+ contain such characters in HTML. This function will not escaped objects
+ that do have an HTML representation such as already escaped data.
+
+ The return value is a :class:`Markup` string.
.. autofunction:: jinja2.utils.clear_caches
+.. autofunction:: jinja2.utils.is_undefined
+
.. autoclass:: jinja2.utils.Markup
@@ -482,6 +555,8 @@ exist that are variables available to a specific template that are available
to all :meth:`~Template.render` calls.
+.. _low-level-api:
+
Low Level API
-------------
@@ -518,3 +593,10 @@ don't recommend using any of those.
This attribute is `False` if there is a newer version of the template
available, otherwise `True`.
+
+.. admonition:: Note
+
+ The low-level API is fragile. Future Jinja2 versions will not change it
+ in a backwards incompatible way but modifications in the Jinja core may
+ shine through. For example if Jinja2 introduces a new AST node in later
+ versions that may be returned by :meth:`~Environment.parse`.
View
15 docs/sandbox.rst
@@ -29,3 +29,18 @@ SecurityError: access to attribute 'func_code' of 'function' object is unsafe.
.. autofunction:: is_internal_attribute
.. autofunction:: modifies_known_mutable
+
+.. admonition:: Note
+
+ The Jinja2 sandbox alone is no solution for perfect security. Especially
+ for web applications you have to keep in mind that users may create
+ templates with arbitrary HTML in so it's crucial to ensure that (if you
+ are running multiple users on the same server) they can't harm each other
+ via JavaScript insertions and much more.
+
+ Also the sandbox is only as good as the configuration. We stronly
+ recommend only passing non-shared resources to the template and use
+ some sort of whitelisting for attributes.
+
+ Also keep in mind that templates may raise runtime or compile time errors,
+ so make sure to catch them.
View
7 docs/switching.rst
@@ -174,6 +174,13 @@ operator. Here are some examples::
hmm. {{ user.username|e }} looks pretty normal
{% endif %}
+Loops
+~~~~~
+
+For loops work very similar to Django, the only incompatibility is that in
+Jinja2 the special variable for the loop context is called `loop` and not
+`forloop` like in Django.
+
Mako
----
View
36 docs/templates.rst
@@ -147,19 +147,21 @@ that block::
This will yield all elements without whitespace between them. If `seq` was
a list of numbers from ``1`` to ``9`` the output would be ``123456789``.
-Note that you must not use a whitespace between the tag and the minus sign:
+If :ref:`line-statements` are enabled they strip leading whitespace
+automatically up to the beginning of the line.
+
+.. admonition:: Note
- valid::
+ You must not use a whitespace between the tag and the minus sign.
+
+ **valid**::
{%- if foo -%}...{% endif %}
- invalid::
+ **invalid**::
{% - if foo - %}...{% endif %}
-If :ref:`line-statements` are enabled they strip leading whitespace
-automatically up to the beginning of the line.
-
Escaping
--------
@@ -797,8 +799,16 @@ for Python objects such as strings and numbers. The following literals exist:
filter.
true / false:
- true is always true and false is always false. Keep in mind that those
- literals are lowercase!
+ true is always true and false is always false.
+
+.. admonition:: Note
+
+ The special constants `true`, `false` and `none` are indeed lowercase.
+ Because that caused confusion in the past, when writing `True` expands
+ to an undefined variable that is considered false, all three of them can
+ be written in title case too (`True`, `False`, and `None`). However for
+ consistency (all Jinja identifiers are lowercase) you should use the
+ lowercase versions.
Math
~~~~
@@ -854,12 +864,12 @@ not
(expr)
group an expression.
-Note that there is no support for any bit operations or something similar.
+.. admonition:: Note
-- special note regarding ``not``: The ``is`` and ``in`` operators support
- negation using an infix notation too: ``foo is not bar`` and
- ``foo not in bar`` instead of ``not foo is bar`` and ``not foo in bar``.
- All other expressions require a prefix notation: ``not (foo and bar).``
+ The ``is`` and ``in`` operators support negation using an infix notation
+ too: ``foo is not bar`` and ``foo not in bar`` instead of ``not foo is bar``
+ and ``not foo in bar``. All other expressions require a prefix notation:
+ ``not (foo and bar).``
Other Operators
View
6 ext/jinja.el
@@ -43,12 +43,12 @@
'("\\({{ ?\\)\\([^|]*?\\)\\(|.*?\\)? ?}}" . (1 font-lock-variable-name-face))
;; keywords and builtins
(cons (rx word-start
- (or "in" "as" "reversed" "recursive" "not" "and" "or"
- "if" "else" "import" "with" "without" "context")
+ (or "in" "as" "recursive" "not" "and" "or" "if" "else"
+ "import" "with" "without" "context")
word-end)
font-lock-keyword-face)
(cons (rx word-start
- (or "true" "false" "null" "loop" "block" "super" "forloop")
+ (or "true" "false" "none" "loop" "self" "super")
word-end)
font-lock-builtin-face)
;; tests
View
4 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'
]
View
2  jinja2/_speedups.c
@@ -187,7 +187,7 @@ tb_set_next(PyObject *self, PyObject *args)
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,
View
11 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 contextfilter(f):
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 @@ def do_groupby(environment, value, attribute):
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)))
View
3  jinja2/nodes.py
@@ -400,7 +400,8 @@ class Name(Expr):
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):
View
7 jinja2/parser.py
@@ -454,9 +454,10 @@ def parse_unary(self):
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)
View
66 jinja2/runtime.py
@@ -117,6 +117,11 @@ def get_all(self):
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 @@ def __repr__(self):
)
+# 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 __repr__(self):
)
-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 @@ def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
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 __iter__(self):
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 @@ class StrictUndefined(Undefined):
"""
__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
View
44 jinja2/utils.py
@@ -63,23 +63,47 @@ def concat(gen):
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 @@ def __reversed__(self):
__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:
View
8 tests/test_syntax.py
@@ -163,3 +163,11 @@ def test_trailing_comma(env):
def test_block_end_name(env):
env.from_string('{% block foo %}...{% endblock foo %}')
raises(TemplateSyntaxError, env.from_string, '{% block x %}{% endblock y %}')
+
+
+def test_contant_casing(env):
+ for const in True, False, None:
+ tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
+ str(const), str(const).lower(), str(const).upper()
+ ))
+ assert tmpl.render() == '%s|%s|' % (const, const)
Please sign in to comment.
Something went wrong with that request. Please try again.