From f35e28154f6ee9269beec6864136dbb9f230af7d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 6 May 2008 16:04:10 +0200 Subject: [PATCH] some documentation improvements, jinja escapes " and ' now, both into charpoints and no named entities for html 3.2 support ;-) --HG-- branch : trunk --- docs/_static/print.css | 3 +- docs/_static/style.css | 21 ++++++--- docs/_static/watermark.png | Bin 3990 -> 4530 bytes docs/_templates/layout.html | 3 +- docs/api.rst | 2 +- docs/templates.rst | 89 ++++++++++++++++++++++++++++++++++++ jinja2/_speedups.c | 42 ++++++++--------- jinja2/compiler.py | 2 +- jinja2/environment.py | 2 +- jinja2/runtime.py | 37 ++++++++++++--- jinja2/utils.py | 13 +++++- tests/test_filters.py | 2 +- 12 files changed, 174 insertions(+), 42 deletions(-) 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 a5a56e1a11c28b7b48d7fabe2542e348fefa13ed..cc5eb33dd591f99fcd63ee81b11647eb50062525 100644 GIT binary patch literal 4530 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 literal 3990 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 Xs(o~nvz0C^qX9@udsv0}tw;X{F;=
-

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): -- 2.26.2