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 2fea4d63..fb633d87 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 1068a335..7bddacc2 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 zc-oa$XIN89w+74c(}Q#okVGIsiArx$6%t7zO{oS1M4EttCIK|Gg9?dA7lMSM1Q4YO z0YVL2fg^+#0!S#*jv)j?QL6MC@2~sg{Q2g2XV02h?|x_2nrEKb`^61w_{kHpCjQe?XH7 zt{odW`o~Gp6A^pyd=)Vm5@u@ucx*9eoRWW8NZ@dE&%wbW_~9LQM~HCS->Kg5d5!)7 zp>ya|QQ1+-C8vm~BoTo+ZJi7O0jA);LKGnIulVoi?{;^0ABu?rmgEg6AcL=0SDKgW zr3u1L*N&WxjeeiVeds{Pd;HozSZkRv(Z2mO0LCjWZaygIZ5w(mH!Qum;?7$CwiS;4 zDGIor8OFFKRa}gV|5o=atW90s02-sDjZOMh9wGcDKvKEUyV=?vj#Zw2SsFk#kl%JC1n~YqD;=J@}p86 zE$dZG0WhOG6Mu_*d(W2RFW_>V7wUX%NbI!`7+xrqPaO=@oZpOuodfd4vMWm6yTbu| z%Np1l<00Jxeav63^3@eT`&!8od48x0secZ~it2kswM;Ge!m$@7WmIQKYKAJ!+3^L7M~viQzLpv1Nqs00M9TFdEj1~UpzZJ zd2zy9jCKoL|0OubWd#Xp^tl%v8^P|(odGWHM6xy8S0h`ZkZa*4EeGxWn-LGy>gHwU zSKQbN_73Sh$+l-KUV8BJXzuhm&vB~zR$?-(xEMZ8{kzm7?Dp9^+{lryn1pg3xp1&= z)Z&8ud_!WO4#Dk@u*P2Tj-7Eiy15l@33X;@_TL)SSq9-3 z(s~qe#Cw71?XpOy7hwzO6*cGcgitB9E zM*9P$DA-!v=j+LP3x?gv0H#A5n+6+nROq?w7;w=i!9H(wNP4tvwpriHw}ht&{sx@k z%?{31^;-ttL-%QVSELmDqMOX0E-};6mV!Wbo90k(=S> zdhvJ4tKNln_i7VG4gY(eD}fl3n_F(DvU-wVjij zH0RPZJfMIWp^7TE(g-atXe{Nf0z5VCg=IdYK)!KS=iw9C5U;801e8U*aox9fuPx$K z&1_ow`=7>*bk3vB;jo+$t%2C`6YL8mG#AHxG?|g_Kd3$B76d<;G&fg~E*X5+J7|34 zrh~)41O~R5^%96_d)yc8iT~uH=RUPb53(#-&|s<0jqL--VG*J9l47(C78NA>L@hwE z`*1t&9;a+G$nULKRbT{Gt)UR#lb$i7D#pZnJZ_o_+OtzNC3nl7gidzv`W690oJ}ki zbb3eMUHD=g&6NS+;?&j%AKJnRS=*nh38!s08sST{WleH@+>hE);1A-rxw*NYkuCY+ z*YipaVP=CDgK(DGFCW<7@f(pAKVBP4SHS#AWBOja$vrjN@yIBLEpEBLzvptl zwfq*-xQ|0I0oyLcr+N~opTp?$7Ps-Q7uD36rk>DTv@tORkNO?a5`v1uqti$&-96g8m-yH+FPR^M`k!Z zt*1c^8?mHGHc4SPbpZmdwFXq{O@@>DH)#!0#xV;{pKX^Ja>lmk2VO4XYUb@UR-(!! zF=FIr+twS2S}Wmo?0cUdbAyEdYo9dra- z@e=Fra~VL=WDFDnYV4Wmcog5|%AOX=^08KH@EK`3jjA@Qc@Q=dcsZcacaig|kT;)M zXE7QMg}IZQ)05k~QE>NmIb$~G18@819OF#^Q!AZK#FHHQYFIzw4JIl0+YDQ;j4%h_ zKo9d&inFYUe{L~^bmVj3lP~ewMoMq?b33vrex$ZJ24imZw}N7qz0wGxa6)YL*WD97 z)C=#bkE`$^dB${4@c`0om}RQ(vQ}97I__Mz_UiIT?j~F@Jp>M3iYtzH5vR+V(!7&W zzNQ|n1;1fyE$f8-h|_eV1mM5vn@a)*a~N~4N}H&4;v*ljD=qBYRtsM1*U!F8K;}9) zoLYOh>KwV+8S4_bn+*uKd)6P~C>l2&dLFY-xKJ_+ku|hTNZI8=<+MLSPQ_@jMzkQc zL(PV;kgZ0gp@O2GuBnv*DZl%oXKE6Y!*RF(u9duNP*Eyy;s?_Xo%A$Y%VF8-S^AJc zftzp8lJb+jLx1-2Z1QWV5fqrZ{-LhzOqHk(m*VE-{D4j+y_Ci}pJt)(p$nhNV4KZ? zv%WEtf02S$UVAq*ltDcAYPg#sP3D=Q)nXYbMVPlD_tN%m)qyn>X zu+&IbE_e%qt?=*Nt+pO$L-N*7v7;APrsIq?x3|K}P1Z8vP)&zmeK`+$zZ|v6A!PQg zvzps)&^=#iXy_GmRt^8SYZ0=z)a-{W32}OBWO?_Mqh{Kh{U#l;S;qeEZa99Ac+9!a zd9yn%(yMV=WfZDJP%%x|YFbrz#0s7ov?+SgT?0Uq@ZCn0xR`GlB6!@fsqlAMCgm2}g+J1#f>)+!{ESGH<2QVPpf!^{%cah7yocoQ1w( z6pdb)z9}Wno=F-ra=n@P&%OW=Bi2WudUaDnrnPM0hp@3&QDqONV((9XcE`{j4<43;a;~`K~U+&dyBQnH?yBi_bVRyfx-s*Virnpo50`NFEsj#hIibb5M zhOf{uN;6AeImv$~tGprw-{&nUtI>_AsY{E|)a$Cpv_6Yh)i&`A3yhS!Zmg;lw=lZ40NnF zRSVs{$F+M>t@m8#xRjP2%Y5WX`3&L0r+i`i5#01_xrt|DFOsj97unaa5G z5x#sCcC6-Mr2~p6ca^*Q7}rqR-CM;BIEc4XgW_2|U$x>BOBXPnHg6OV0 za)eGf+UptIb^08V1_IYwLWv`WhXUl_oB5g1gj`OmwiU%Z(RM;NhY^4uBw9AJG{+~K zZht>okO}K^^ZScyx;kkr=!xeMwr$X@#8uqY=e=xq{FFcJ3KNv#4R`pv`h#DnRi+ge7r0EQBY+ ztz7kd$nqTvhESNadKA!ZHJtx9$YD=W-C9cC@E0iNV}PHFdU4iU?aeNHK? zf?*MwaM!RK1w^9|oaoav*tq!CI&Lw<#QQ%<+>BOPkDA%SLGQ+)g=_ccGpU=AzdxN# zEuVh@4>aL<3ZUegV=p;0btm|m1p+AH;ce1%zA+S>F>y!1A#|1TK4SWDww;KeNT8}I zDX)Q5VISM+QdTtE3I2#sWg1n-ypsI;IrcBq({?#A8iG#C5)Zt#kX-*z*+S=V-ujv= z>zWndbqNS}Gu4G;_uTq02l_QxXL0&WUb$0=0Ii{xe|l|n-ONvdInoX8ktV=~2Uvr4 zSMe+6D>QZ%Rf0F*o826131=sp(UDhW11IEGgjOG+&a=RkMjNlxX!!oE>J33mEGShJ zz|5cP8%AiKmhZ@1k~*$K0qJAb{`r~q6{DJMFc2H*vylhdR;svO@9;AuZ9&83KC}o@ zn(}){?wqof3>Bf$=zw0Nc#Kn-9XFyyye2%=-YT9clj-{GQ*Vb@3T`%%$5m2nYJ&pz z>a+D)9MD#(W|%c?d0fisx`(u5ey(?;U%68CR<;qgKvU@OF*(I;W5t~TQ6~_p(`{J` z896i)_ZPRwV;(G-;6QZW@C`pLmI@{O>|W$a1g$xE2o{=HR%o0R=$B{R$h zt_v7MdY78_B>J`I+h{^0-6PzZem3a`+5zmFDE#7bG1b-0)2hE8FPJL9oGD1P@pi)* zgZY0{u$BIT5pCu>eZd2PR!X%?-oAl+8Dxll2n4qF`Wcu%y~1XO#0~dys|KWPkn|tIx2|H91xwi44`Ep4 zBa6b9$JD3J5LwA`qJTbvk4$Ot1@JMLnwV9B5ga5)K5dCe^O+YC-BV17c#x2T5GF(_ zFf*v7)`koL`R$|LA9*AR`{}6H~G%9S@ZtY$SFr;B3fP>ufV{!K mgM9d3@n6#a{~Osa4+M6MNWt&D4)+T13-X#ZtlaF*)BgqjIj(#F literal 3990 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
-

Jinja

+

Jinja

{%- if prev %} diff --git a/docs/api.rst b/docs/api.rst index fa601ddf..0b33a43f 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 8d582132..615884a2 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 3c74bbae..dcf72483 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 8b4abf6e..d90d4acc 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 a129c387..35bbb15f 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 4b9ce6d6..829de41d 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 13b67f94..cc602118 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 da3d2000..824346e8 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.47.2