some documentation improvements, jinja escapes " and ' now, both into charpoints...
authorArmin Ronacher <armin.ronacher@active-4.com>
Tue, 6 May 2008 14:04:10 +0000 (16:04 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Tue, 6 May 2008 14:04:10 +0000 (16:04 +0200)
--HG--
branch : trunk

12 files changed:
docs/_static/print.css
docs/_static/style.css
docs/_static/watermark.png
docs/_templates/layout.html
docs/api.rst
docs/templates.rst
jinja2/_speedups.c
jinja2/compiler.py
jinja2/environment.py
jinja2/runtime.py
jinja2/utils.py
tests/test_filters.py

index 2fea4d631f76e8f4fb8ab7cc286ee9c85841f362..fb633d879c1d2ed02fa24e45109f03ac5a901662 100644 (file)
@@ -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; }
index 1068a335cd5272450ea6ddac71622c991e1a912a..7bddacc2b4a6e167fc62ff0f2620de8337e49fa1 100644 (file)
@@ -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;
 }
index a5a56e1a11c28b7b48d7fabe2542e348fefa13ed..cc5eb33dd591f99fcd63ee81b11647eb50062525 100644 (file)
Binary files a/docs/_static/watermark.png and b/docs/_static/watermark.png differ
index b0e3488050dac42c10538283f95212f82460c52d..0e803083477ce71c1008a1208ed290309c0ef008 100644 (file)
@@ -45,7 +45,8 @@
   <body>
     <div id="content">
       <div class="header">
-        <h1 class="heading"><a href="{{ pathto('index') }}"><span>Jinja</span></a></h1>
+        <h1 class="heading"><a href="{{ pathto('index') }}"
+          title="back to the documentation overview"><span>Jinja</span></a></h1>
       </div>
       <div class="relnav">
         {%- if prev %}
index fa601ddfe2b5b224e64b7745ec683de099e2920c..0b33a43ff1b6fa6864f0be237955aaadd1918a20 100644 (file)
@@ -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
 
index 8d582132d8b850ffc6928ba004966c86a5565726..615884a2d553aba7eb8e558d9b2457ee83f6b0d7 100644 (file)
@@ -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 %}
+        <ul>
+        {% for item in seq %}
+            <li>{{ item }}</li>
+        {% endfor %}
+        </ul>
+    {% 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::
+
+    <ul>
+    # for item in seq
+        <li>{{ item }}</li>
+    # endfor
+    </ul>
+
+    <ul>
+    {% for item in seq %}
+        <li>{{ item }}</li>
+    {% endfor %}
+    </ul>
+
+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
index 3c74bbae60ddbe0869e7194a35757eda06f4776f..dcf72483f1de3979fed2a7f0cdbf25f4352c37f4 100644 (file)
@@ -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("&quot;");
-
-       escaped_chars_delta_len['&'] = 4;
+       /* happing of characters to replace */
+       escaped_chars_repl['"'] = UNICHR("&#34;");
+       escaped_chars_repl['\''] = UNICHR("&#39;");
        escaped_chars_repl['&'] = UNICHR("&amp;");
-       
-       escaped_chars_delta_len['<'] = 3;
        escaped_chars_repl['<'] = UNICHR("&lt;");
-       
-       escaped_chars_delta_len['>'] = 3;
        escaped_chars_repl['>'] = UNICHR("&gt;");
+
+       /* 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"
index 8b4abf6e655947032424343fe04f578807373a81..d90d4acc76afdb5fc2e1eb7f295360a2161de21e 100644 (file)
@@ -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."""
index a129c387dc23fdeba285b8a7d40bbf5a6b0480eb..35bbb15fe6daf1d68ad4c12bbab56bc47f16201e 100644 (file)
@@ -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
index 4b9ce6d635f1b8370cd4ea5cb01b140c4d263fd8..829de41df2a68bf05acd980f43070b57c047d0a7 100644 (file)
@@ -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>' % (
index 13b67f9499abbc6bc81c7b029315977c060a2227..cc6021187c219da5e4225f07fc0910da4c791fb1 100644 (file)
@@ -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('&', '&amp;')
             .replace('>', '&gt;')
             .replace('<', '&lt;')
+            .replace("'", '&#39;')
             .replace('"', '&quot;')
         )
 
index da3d2000dc2c139fb80b213f1da550dac4316563..824346e876a33a2cbefb4571100e20995ab67a00 100644 (file)
@@ -110,7 +110,7 @@ def test_slice(env):
 def test_escape(env):
     tmpl = env.from_string(ESCAPE)
     out = tmpl.render()
-    assert out == '&lt;&quot;&gt;&amp;'
+    assert out == '&lt;&#34;&gt;&amp;'
 
 
 def test_striptags(env):