From: Armin Ronacher Date: Tue, 6 May 2008 14:04:10 +0000 (+0200) Subject: some documentation improvements, jinja escapes " and ' now, both into charpoints... X-Git-Tag: 2.0rc1~89 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=f35e28154f6ee9269beec6864136dbb9f230af7d;p=jinja2.git some documentation improvements, jinja escapes " and ' now, both into charpoints and no named entities for html 3.2 support ;-) --HG-- branch : trunk --- diff --git a/docs/_static/print.css b/docs/_static/print.css index 2fea4d6..fb633d8 100644 --- a/docs/_static/print.css +++ b/docs/_static/print.css @@ -1,6 +1,5 @@ div.header, div.relnav, #toc { display: none; } #contentwrapper { padding: 0; margin: 0; border: none; } body { color: black; background-color: white; } -div.footer { text-align: right; border-top: 1px solid #888; color: #888; - margin-top: 1cm; } +div.footer { border-top: 1px solid #888; color: #888; margin-top: 1cm; } div.footer a { text-decoration: none; } diff --git a/docs/_static/style.css b/docs/_static/style.css index 1068a33..7bddacc 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -8,10 +8,10 @@ body { } div.footer { - border-top: 1px solid #111; + border-top: 1px solid black; padding: 8px; font-size: 11px; - text-align: center; + text-align: right; } div.footer a { @@ -26,7 +26,7 @@ div.header { div.relnav { border-bottom: 1px solid #ACACAC; background: url(navigation.png); - padding: 2px 20px 0 38px; + padding: 2px 20px 0 28px; line-height: 25px; color: #aaa; font-size: 12px; @@ -56,18 +56,27 @@ h1 { } h1.heading { - margin: 0 0 0 20px; + margin: 0; padding: 0; height: 80px; - background: url(jinjabanner.png) no-repeat; +} + +h1.heading:hover { + background: #222; } h1.heading a { + background: url(jinjabanner.png) no-repeat 20px 0; display: block; - width: 200px; + width: 100%; height: 80px; } +h1.heading a:focus { + -moz-outline: none; + outline: none; +} + h1.heading span { display: none; } diff --git a/docs/_static/watermark.png b/docs/_static/watermark.png index a5a56e1..cc5eb33 100644 Binary files a/docs/_static/watermark.png and b/docs/_static/watermark.png differ diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index b0e3488..0e80308 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -45,7 +45,8 @@
-

Jinja

+

Jinja

{%- if prev %} diff --git a/docs/api.rst b/docs/api.rst index fa601dd..0b33a43 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -124,7 +124,7 @@ The Context ----------- .. autoclass:: jinja2.runtime.Context - :members: super, get, get_exported, get_all + :members: resolve, get_exported, get_all .. attribute:: parent diff --git a/docs/templates.rst b/docs/templates.rst index 8d58213..615884a 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -126,6 +126,95 @@ yourself:: {% endfor %} #} + +Whitespace Control +------------------ + +In the default configuration whitespace is not further modified by the +template engine, so each whitespace (spaces, tabs, newlines etc.) is returned +unchanged. If the application configures Jinja to `trim_blocks` the first +newline after a a template tag is removed automatically (like in PHP). + +But you can also strip whitespace in templates by hand. If you put an minus +sign (``-``) to the start or end of an block (for example a for tag), a +comment or variable expression you can remove the whitespaces after or before +that block:: + + {% for item in seq -%} + {{ item }} + {%- endfor %} + +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: + + valid: + {%- if foo -%}...{% endif %} + + invalid: + + {% - if foo - %}...{% endif %} + +If :ref:`line-statements` are enabled they strip leading whitespace +automatically up to the beginning of the line. + + +Escaping +-------- + +It is sometimes desirable or even necessary to have Jinja ignore parts it +would otherwise handle as variables or blocks. For example if the default +syntax is used and you want to use ``{{`` as raw string in the template and +not start a variable you have to use a trick. + +The easiest way is to output the variable delimiter (``{{``) by using a +variable expression:: + + {{ '{{' }} + +For bigger sections it makes sense to mark a block `raw`. For example to +put Jinja syntax as example into a template you can use this snippet:: + + {% raw %} +
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ {% endraw %} + + +.. _line-statements: + +Line Statements +--------------- + +If line statements are enabled by the application it's possible to mark a +line as a statement. For example if the line statement prefix is configured +to ``#`` the following two examples are equivalent:: + +
    + # for item in seq +
  • {{ item }}
  • + # endfor +
+ +
    + {% for item in seq %} +
  • {{ item }}
  • + {% endfor %} +
+ +The line statement prefix can appear anywhere on the line as long as no text +precedes it. For better readability statements that start a block (such as +`for`, `if`, `elif` etc.) may end with a colon:: + + # for item in seq: + ... + # endif + + .. _template-inheritance: Template Inheritance diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c index 3c74bba..dcf7248 100644 --- a/jinja2/_speedups.c +++ b/jinja2/_speedups.c @@ -23,20 +23,20 @@ static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; static int init_constants(void) { - memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); - - escaped_chars_delta_len['"'] = 5; - escaped_chars_repl['"'] = UNICHR("""); - - escaped_chars_delta_len['&'] = 4; + /* happing of characters to replace */ + escaped_chars_repl['"'] = UNICHR("""); + escaped_chars_repl['\''] = UNICHR("'"); escaped_chars_repl['&'] = UNICHR("&"); - - escaped_chars_delta_len['<'] = 3; escaped_chars_repl['<'] = UNICHR("<"); - - escaped_chars_delta_len['>'] = 3; escaped_chars_repl['>'] = UNICHR(">"); + + /* lengths of those characters when replaced - 1 */ + memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); + escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ + escaped_chars_delta_len['&'] = 4; + escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; + /* import markup type so that we can mark the return value */ PyObject *module = PyImport_ImportModule("jinja2.utils"); if (!module) return 0; @@ -108,16 +108,6 @@ escape_unicode(PyUnicodeObject *in) } -static PyObject* -soft_unicode(PyObject *self, PyObject *s) -{ - if (!PyUnicode_Check(s)) - return PyObject_Unicode(s); - Py_INCREF(s); - return s; -} - - static PyObject* escape(PyObject *self, PyObject *text) { @@ -156,6 +146,16 @@ escape(PyObject *self, PyObject *text) } +static PyObject* +soft_unicode(PyObject *self, PyObject *s) +{ + if (!PyUnicode_Check(s)) + return PyObject_Unicode(s); + Py_INCREF(s); + return s; +} + + static PyObject * tb_set_next(PyObject *self, PyObject *args) { @@ -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" - "sequences. Use this if you need to display text that might contain\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, "soft_unicode(object) -> string\n\n" diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 8b4abf6..d90d4ac 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -461,7 +461,7 @@ class CodeGenerator(NodeVisitor): def pull_locals(self, frame): """Pull all the references identifiers into the local scope.""" for name in frame.identifiers.undeclared: - self.writeline('l_%s = context[%r]' % (name, name)) + self.writeline('l_%s = context.resolve(%r)' % (name, name)) def pull_dependencies(self, nodes): """Pull all the dependencies.""" diff --git a/jinja2/environment.py b/jinja2/environment.py index a129c38..35bbb15 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -103,7 +103,7 @@ class Environment(object): `line_statement_prefix` If given and a string, this will be used as prefix for line based - statements. + statements. See also :ref:`line-statements`. `trim_blocks` If this is set to ``True`` the first newline after a block is diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 4b9ce6d..829de41 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -51,8 +51,10 @@ class Context(object): and are allowed to access the context read-only. The template context supports read only dict operations (`get`, - `__getitem__`, `__contains__`) however `__getitem__` doesn't fail with - a `KeyError` but returns an :attr:`Undefined` object. + `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, + `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` + method that doesn't fail with a `KeyError` but returns an + :class:`Undefined` object for missing variables. """ def __init__(self, environment, parent, name, blocks): @@ -95,11 +97,20 @@ class Context(object): """Returns an item from the template context, if it doesn't exist `default` is returned. """ + try: + return self[key] + except KeyError: + return default + + def resolve(self, key): + """Looks up a variable like `__getitem__` or `get` but returns an + :class:`Undefined` object with the name of the name looked up. + """ if key in self.vars: return self.vars[key] if key in self.parent: return self.parent[key] - return default + return self.environment.undefined(name=key) def get_exported(self): """Get a new dict with the exported variables.""" @@ -111,15 +122,29 @@ class Context(object): """ return dict(self.parent, **self.vars) + def _all(meth): + def proxy(self): + return getattr(self.get_all(), meth)() + proxy.__doc__ = getattr(dict, meth).__doc__ + proxy.__name__ = meth + return proxy + + keys = _all('keys') + values = _all('values') + items = _all('items') + iterkeys = _all('iterkeys') + itervalues = _all('itervalues') + iteritems = _all('iteritems') + del _all + def __contains__(self, name): return name in self.vars or name in self.parent def __getitem__(self, key): + """Lookup a variable or raise `KeyError`.""" if key in self.vars: return self.vars[key] - if key in self.parent: - return self.parent[key] - return self.environment.undefined(name=key) + return self.parent[key] def __repr__(self): return '<%s %s of %r>' % ( diff --git a/jinja2/utils.py b/jinja2/utils.py index 13b67f9..cc60211 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -303,6 +303,14 @@ class Markup(unicode): stripped = u' '.join(_striptags_re.sub('', self).split()) return Markup(stripped).unescape() + @classmethod + def escape(cls, s): + """Escape the string. Works like :func:`escape`.""" + rv = escape(s) + if rv.__class__ is not cls: + return cls(rv) + return rv + def make_wrapper(name): orig = getattr(unicode, name) def func(self, *args, **kwargs): @@ -478,8 +486,8 @@ try: from jinja2._speedups import escape, soft_unicode except ImportError: def escape(s): - """Convert the characters &, <, >, and " in string s to HTML-safe - sequences. Use this if you need to display text that might contain + """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. Marks return value as markup string. """ if hasattr(s, '__html__'): @@ -488,6 +496,7 @@ except ImportError: .replace('&', '&') .replace('>', '>') .replace('<', '<') + .replace("'", ''') .replace('"', '"') ) diff --git a/tests/test_filters.py b/tests/test_filters.py index da3d200..824346e 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -110,7 +110,7 @@ def test_slice(env): def test_escape(env): tmpl = env.from_string(ESCAPE) out = tmpl.render() - assert out == '<">&' + assert out == '<">&' def test_striptags(env):