[svn] add first part of jinja documentation
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 2 Mar 2007 19:42:18 +0000 (20:42 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 2 Mar 2007 19:42:18 +0000 (20:42 +0100)
--HG--
branch : trunk

20 files changed:
artwork/jinjalogo.svg [new file with mode: 0644]
docs/build/jinjabanner.png [new file with mode: 0644]
docs/build/jinjalogo.png [new file with mode: 0644]
docs/build/style.css [new file with mode: 0644]
docs/build/watermark.png [new file with mode: 0644]
docs/generate.py [new file with mode: 0755]
docs/src/designerdoc.txt [new file with mode: 0644]
docs/src/devintro.txt [new file with mode: 0644]
docs/src/fromdjango.txt [new file with mode: 0644]
docs/src/index.txt [new file with mode: 0644]
docs/src/loaders.txt [new file with mode: 0644]
jinja/__init__.py
jinja/datastructure.py
jinja/environment.py
jinja/filters.py
jinja/loaders.py
jinja/nodes.py
jinja/tests.py
jinja/translators/python.py
jinja/utils.py

diff --git a/artwork/jinjalogo.svg b/artwork/jinjalogo.svg
new file mode 100644 (file)
index 0000000..3eb485f
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="300"
+   height="120"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.44"
+   version="1.0"
+   sodipodi:docbase="/home/blackbird/Development/jinja/trunk/artwork"
+   sodipodi:docname="jinjalogo.svg"
+   inkscape:export-filename="/home/blackbird/Development/jinja/trunk/docs/jinjalogo.png"
+   inkscape:export-xdpi="60"
+   inkscape:export-ydpi="60">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient6558">
+      <stop
+         style="stop-color:#585858;stop-opacity:1;"
+         offset="0"
+         id="stop6560" />
+      <stop
+         style="stop-color:#4a4a4a;stop-opacity:1;"
+         offset="1"
+         id="stop6562" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="radialGradient6564"
+       cx="61.297766"
+       cy="60.910986"
+       fx="61.297766"
+       fy="60.910986"
+       r="44.688254"
+       gradientTransform="matrix(1,0,0,0.945104,0,3.343747)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="radialGradient6580"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.945104,0.355158,3.334402)"
+       cx="61.297766"
+       cy="60.910986"
+       fx="61.297766"
+       fy="60.910986"
+       r="44.688254" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.959798"
+     inkscape:cx="145.89672"
+     inkscape:cy="59.43346"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="300px"
+     height="120px"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1400"
+     inkscape:window-height="975"
+     inkscape:window-x="0"
+     inkscape:window-y="24" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="font-size:12px;font-style:normal;font-weight:normal;fill:#dcdcdc;fill-opacity:1;stroke:#cbcbcb;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-family:Bitstream Vera Sans;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 165.36463,80.874808 L 165.36463,80.874808 L 153.32556,80.874808 L 153.32556,81.8344 L 147.64994,81.8344 L 147.64994,36.035583 L 165.36463,36.035583 L 165.36463,20.333129 C 170.58154,21.031083 173.07533,22.077914 172.84609,23.473621 C 172.78871,24.055258 172.21545,24.549594 171.12624,24.956624 L 171.12624,36.035583 L 189.09895,36.035583 L 189.09895,82.532286 L 183.33733,82.532286 L 183.33733,80.874808 L 171.12624,80.874808 L 171.12624,102.94548 L 165.36463,102.94548 L 165.36463,80.874808 M 153.32556,55.489173 L 153.32556,55.489173 L 165.36463,55.489173 L 165.36463,41.793146 L 153.32556,41.793146 L 153.32556,55.489173 M 171.12624,55.489173 L 171.12624,55.489173 L 183.33733,55.489173 L 183.33733,41.793146 L 171.12624,41.793146 L 171.12624,55.489173 M 183.33733,61.333977 L 183.33733,61.333977 L 171.12624,61.333977 L 171.12624,75.030006 L 183.33733,75.030006 L 183.33733,61.333977 M 165.36463,61.333977 L 165.36463,61.333977 L 153.32556,61.333977 L 153.32556,75.030006 L 165.36463,75.030006 L 165.36463,61.333977 M 132.85897,59.414792 C 137.33069,63.136883 140.99969,67.934848 143.86618,73.808701 L 139.13654,77.385372 C 137.24467,72.965445 134.6362,69.12707 131.31114,65.87024 L 131.31114,102.94548 L 125.63554,102.94548 L 125.63554,68.57455 C 122.31042,71.947693 118.52671,74.913707 114.28436,77.47261 L 109.64069,73.372526 C 121.50782,67.091566 130.62312,55.489212 136.98668,38.565417 L 116.26221,38.565417 L 116.26221,32.720615 L 125.80754,32.720615 L 125.80754,20.333129 C 130.85245,21.031083 133.31761,22.048838 133.20299,23.386383 C 133.14561,24.026183 132.57235,24.549594 131.48307,24.956624 L 131.48307,32.720615 L 140.77043,32.720615 L 143.60824,36.733469 C 140.68444,45.51526 137.10137,53.075692 132.85897,59.414792 M 254.11016,49.469901 L 254.11016,49.469901 L 254.11016,20.333129 C 259.21243,21.031083 261.67755,22.048838 261.50562,23.386383 C 261.44823,23.909869 261.04699,24.346044 260.30172,24.694917 C 260.30164,24.694986 260.30164,24.694986 260.30172,24.694917 L 260.30172,24.694917 L 259.78578,24.956624 L 259.78578,49.469901 L 277.15652,49.469901 L 277.15652,55.227471 L 259.78578,55.227471 L 259.78578,93.785712 L 281.45616,93.785712 L 281.45616,99.63051 L 232.35378,99.63051 L 232.35378,93.785712 L 254.11016,93.785712 L 254.11016,55.227471 L 236.22346,55.227471 L 236.22346,49.469901 L 254.11016,49.469901 M 225.5603,59.327554 C 231.12111,63.107798 235.62145,67.876693 239.06127,73.634235 L 234.76157,77.647079 C 231.60845,72.180322 227.82475,67.934848 223.41044,64.910648 L 223.41044,102.94548 L 217.73484,102.94548 L 217.73484,67.44049 C 212.91919,71.627831 207.70222,75.030021 202.084,77.647079 L 197.87027,73.198053 C 212.66118,66.917101 224.01239,55.372897 231.92377,38.565417 L 205.35172,38.565417 L 205.35172,32.720615 L 217.99283,32.720615 L 217.99283,20.333129 C 223.03774,21.031083 225.50291,22.048838 225.38829,23.386383 C 225.33089,24.026183 224.75765,24.549594 223.66837,24.956624 L 223.66837,32.720615 L 236.22346,32.720615 L 238.80326,36.733469 C 235.13421,45.51526 230.71987,53.046611 225.5603,59.327554"
+       id="text4761" />
+    <path
+       style="font-size:44.09793472px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#b41717;fill-opacity:1;stroke:#7f2828;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Candara;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 149.14708,37.774469 C 148.97807,41.117899 148.84526,44.824225 148.74871,48.893456 C 148.67626,52.962754 148.3818,70.641328 148.38184,75.524422 C 148.3818,79.065795 148.05588,81.991266 147.40406,84.300835 C 146.75219,86.610422 145.72612,88.557071 144.32585,90.140779 C 142.94969,91.724494 141.17522,92.901283 139.00239,93.671139 C 136.82953,94.440996 134.22211,94.825935 131.18014,94.825935 C 128.83828,94.825935 126.73787,94.59498 124.87889,94.133049 L 125.4221,89.31593 C 127.13623,90.0418 128.92278,90.404734 130.78177,90.404733 C 132.85805,90.404734 134.66875,90.140782 136.2139,89.612876 C 137.78315,89.062981 139.02651,88.216133 139.94396,87.072335 C 140.8855,85.928548 141.54942,84.520804 141.93572,82.8491 C 142.34613,81.177412 142.55134,78.988811 142.55136,76.283285 C 142.55134,66.297119 142.62852,44.659257 142.26641,37.774469 L 149.14708,37.774469 M 166.38498,80.732697 L 159.83024,80.732697 C 160.16821,76.333498 160.33723,71.307412 160.33723,65.654424 C 160.33723,59.2976 159.91471,53.963567 159.06973,49.652319 L 166.31257,48.761483 C 166.02284,53.358679 165.87799,58.98965 165.87799,65.654424 C 165.87799,70.933479 166.04699,75.959565 166.38498,80.732697 M 167.90601,39.490159 C 167.90598,40.611994 167.5076,41.590815 166.7109,42.42662 C 165.91418,43.240515 164.79155,43.647442 163.343,43.647399 C 162.11172,43.647442 161.146,43.295504 160.44588,42.591595 C 159.76988,41.865769 159.43188,40.996927 159.43188,39.98507 C 159.43188,38.885304 159.84231,37.928485 160.66315,37.114591 C 161.48399,36.30078 162.61869,35.893853 164.06727,35.893811 C 165.25023,35.893853 166.17975,36.256783 166.85575,36.982609 C 167.55588,37.686526 167.90598,38.522373 167.90601,39.490159 M 206.72748,80.732697 L 200.13651,80.732697 C 200.66763,74.947749 200.93319,68.634899 200.9332,61.794122 C 200.93319,58.406756 200.1727,56.097177 198.65174,54.865371 C 197.15487,53.61163 195.00619,52.984747 192.20564,52.984714 C 188.77731,52.984747 185.61465,54.117535 182.71753,56.383099 C 182.71753,63.883761 182.76583,72.000287 182.86238,80.732697 L 176.27142,80.732697 C 176.68182,73.254058 176.88707,67.843042 176.88707,64.499632 C 176.88707,59.352589 176.3559,54.359493 175.29363,49.520339 L 181.66734,48.695493 L 182.35539,52.720761 L 182.64511,52.720761 C 186.21823,49.773323 190.04483,48.299592 194.12499,48.299567 C 198.13265,48.299592 201.23499,49.113454 203.43201,50.741118 C 205.62895,52.346863 206.72747,55.217334 206.72748,59.352563 C 206.72747,59.770507 206.70331,60.595362 206.65507,61.827118 C 206.60675,63.058915 206.5826,63.883761 206.58262,64.30167 C 206.5826,67.975018 206.63088,73.452022 206.72748,80.732697 M 222.69791,48.695493 C 222.28747,55.514282 222.08225,62.355041 222.08225,69.21778 C 222.08225,71.043461 222.14262,73.463019 222.26332,76.476468 C 222.40822,79.467925 222.4806,81.502559 222.48063,82.580363 C 222.4806,89.685068 219.51105,93.996287 213.57195,95.514024 L 211.76124,93.006484 C 213.90995,91.356766 215.2378,89.597085 215.74478,87.727431 C 216.49321,85.043912 216.86743,79.324953 216.86743,70.570535 C 216.86743,61.178248 216.3846,54.16153 215.41887,49.520339 L 222.69791,48.695493 M 224.2551,39.490159 C 224.2551,40.611994 223.85673,41.590815 223.06006,42.42662 C 222.26332,43.240515 221.14069,43.647442 219.69213,43.647399 C 218.46084,43.647442 217.49515,43.295504 216.795,42.591595 C 216.119,41.865769 215.781,40.996927 215.781,39.98507 C 215.781,38.885304 216.19144,37.928485 217.01231,37.114591 C 217.83316,36.30078 218.96785,35.893853 220.4164,35.893811 C 221.5994,35.893853 222.52889,36.256783 223.20492,36.982609 C 223.90503,37.686526 224.2551,38.522373 224.2551,39.490159 M 259.60008,80.732697 L 253.91446,80.930661 C 253.62473,79.852857 253.47987,78.830045 253.4799,77.862216 L 253.11774,77.862216 C 250.14817,80.325772 246.10427,81.557546 240.98606,81.557547 C 238.20962,81.557546 235.8195,80.820682 233.81563,79.346948 C 231.81178,77.851221 230.80988,75.728607 230.80988,72.979099 C 230.80988,69.591724 232.37914,66.875216 235.51769,64.829574 C 238.65625,62.761967 244.48667,61.67316 253.00913,61.563165 C 253.08155,61.035275 253.11772,60.430386 253.11774,59.748497 C 253.11772,57.043003 252.32104,55.239336 250.72765,54.337474 C 249.15832,53.435661 246.76819,52.984747 243.55721,52.984714 C 239.76681,52.984747 236.03678,53.413668 232.3671,54.271484 L 232.9827,49.718301 C 236.60411,48.77251 240.76873,48.299592 245.47658,48.299567 C 249.77395,48.299592 253.09359,49.113454 255.43545,50.741118 C 257.77728,52.346863 258.94819,55.096363 258.94824,58.989625 C 258.94819,60.023469 258.88785,61.904117 258.76715,64.631608 C 258.67054,67.337133 258.62228,69.140806 258.6223,70.042632 C 258.62228,74.045913 258.94819,77.609265 259.60008,80.732697 M 253.19019,74.331856 C 253.06945,70.988469 253.00909,67.986016 253.00913,65.324484 C 248.47027,65.324498 245.01786,65.632443 242.65187,66.248318 C 238.69248,67.348131 236.71278,69.448748 236.71278,72.550177 C 236.71278,75.541643 239.03044,77.037371 243.66588,77.037366 C 247.64942,77.037371 250.82416,76.135534 253.19019,74.331856"
+       id="text3736"
+       sodipodi:nodetypes="ccsscssccscccsccccsccsccsscsssccccscscccsccccscsssccscscccscccsscsssccccccscsccscsccscscsccccssc" />
+    <path
+       style="fill:url(#radialGradient6564);fill-opacity:1.0;fill-rule:evenodd;stroke:#323232;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 105.45673,18.675923 C 105.45673,18.675923 88.211949,26.918461 74.172834,28.737898 C 60.133727,30.557333 33.360434,32.377571 28.045622,31.093256 C 22.730818,29.808941 18.915645,28.309196 18.915645,28.309196 L 20.021441,32.056583 L 16.609513,35.052471 L 17.2144,36.121726 L 18.61792,36.22764 L 22.92773,36.762252 L 23.532621,38.688909 L 25.937975,38.905784 L 27.143021,42.970927 C 27.143021,42.970927 32.254764,43.399628 33.758953,43.399628 C 35.263142,43.399628 38.271966,43.187802 38.271966,43.187802 L 38.371202,44.791657 L 39.477002,45.003495 L 39.477002,46.824227 L 37.066917,48.967759 L 37.671807,49.073671 L 37.671807,49.820127 C 37.671807,49.820127 32.255457,50.252157 30.049301,49.93109 C 27.843157,49.610006 27.440747,49.608286 27.440747,49.608286 L 27.242258,49.820127 L 27.143021,50.783455 L 27.643946,50.783455 L 27.84242,54.959544 L 38.976091,54.530844 L 38.172728,68.980747 L 38.073481,70.796442 L 28.645781,70.261816 L 28.546544,66.408513 L 30.649462,66.408513 L 30.852673,64.910557 L 32.757107,64.481857 L 33.059555,64.058192 L 25.937975,62.343374 L 20.522364,63.947229 L 21.42496,64.698732 L 22.327572,64.698732 L 22.426809,65.984848 L 24.331254,66.09076 L 24.331254,69.838147 L 22.228335,70.372777 L 22.630009,71.225146 L 23.130934,71.547931 L 23.130934,74.437917 L 24.435218,74.437917 L 24.435218,87.813529 L 22.327572,88.13632 L 22.630009,91.989617 L 23.929569,92.206492 L 23.731093,100.98236 L 29.449141,101.08826 L 28.244105,92.418334 L 36.868446,92.206492 L 36.268285,96.912181 L 35.464925,100.23086 L 44.188501,100.33677 L 44.287739,91.777793 L 50.303506,91.243181 L 50.005786,96.700351 L 49.802585,99.90807 L 54.920484,99.90807 L 54.717274,91.132217 L 55.421397,91.243181 L 55.619882,87.067076 L 54.816521,87.067076 L 54.518798,85.352258 L 54.017874,80.429702 L 54.216359,74.760706 L 55.31743,74.760706 L 55.31743,71.336105 L 53.913913,71.442015 L 54.117112,67.402096 L 55.747469,67.240708 L 55.823083,65.929374 L 56.749319,65.793192 L 57.699176,65.071956 L 51.985842,63.896802 L 46.31977,65.15265 L 46.872668,66.060507 L 47.47283,66.010066 L 48.172228,65.984848 L 48.299828,67.639144 L 49.878196,67.563497 L 49.906548,71.144447 L 43.111042,70.988097 L 43.337879,67.160002 L 43.559978,63.679927 L 43.559978,59.105378 L 43.763188,54.288748 L 57.373101,53.592733 L 73.567955,52.659674 L 73.71917,55.736265 L 73.142647,63.120082 L 72.892183,69.9945 L 66.928387,69.888585 L 66.900039,65.071956 L 69.106918,64.991267 L 69.206169,63.629486 L 70.108765,63.493308 L 70.061506,63.226006 L 70.964116,63.175568 L 71.465028,62.504773 L 64.721507,60.926122 L 58.001612,62.368592 L 58.4789,63.200785 L 59.230285,63.1453 L 59.230285,63.523577 L 60.156518,63.523577 L 60.156518,65.046738 L 62.136575,65.071956 L 62.112937,69.298485 L 60.109259,69.298485 L 60.080907,70.261816 L 60.785031,70.342507 L 60.70942,74.009202 L 62.188552,74.089909 L 62.013701,88.620507 L 60.057282,89.018952 L 60.080907,89.714967 L 60.761406,89.714967 L 60.761406,93.437137 L 61.886113,93.437137 L 61.588391,98.52109 L 61.210343,102.95945 L 68.331912,103.14605 L 68.105084,99.29275 L 67.580538,96.085028 L 67.476575,93.300955 L 73.520696,93.195041 L 73.345845,97.502272 L 73.317494,102.05159 L 76.729426,102.3189 L 81.3653,102.1323 L 82.820807,101.70358 L 82.017437,99.26753 L 81.818959,95.439438 L 81.440912,92.710853 L 87.206218,92.499027 L 86.955759,95.842931 L 86.932133,101.08826 L 89.238253,101.30009 L 91.520751,101.24965 L 92.621828,100.90165 L 91.969693,95.923633 L 91.747577,92.176239 L 92.725793,92.070324 L 92.749427,88.726422 L 93.02352,88.670945 L 92.976244,87.949712 L 91.846823,87.949712 L 91.619996,85.488427 L 91.520751,74.811143 L 92.371377,74.785924 L 92.371377,71.280616 L 92.725793,71.336105 L 92.725793,70.640088 L 91.468773,70.529127 L 91.497126,66.463987 L 93.600043,66.277382 L 93.477182,64.910557 L 94.403419,64.829863 L 94.351424,64.562549 L 95.580099,63.947229 L 89.337489,62.69138 L 82.995657,63.977495 L 83.39733,64.723951 L 84.375543,64.643256 L 84.427528,64.966046 L 85.254515,64.966046 L 85.301775,66.569901 L 87.357445,66.544681 L 87.532293,70.478688 L 80.264217,70.423216 L 79.413593,64.512124 L 78.733106,61.380041 L 78.184923,55.761484 L 78.510996,52.473053 L 92.999878,51.373557 L 93.047136,46.476221 L 93.774891,46.289613 L 93.727651,45.543159 L 93.174743,45.220372 C 93.174629,45.220372 85.252181,46.395266 82.745197,46.66284 C 82.0389,46.738209 82.09239,46.733258 81.516524,46.79397 L 81.440912,45.886118 L 78.444837,44.317564 L 78.482644,42.491786 L 79.512842,42.461518 L 79.588444,39.949808 C 79.588444,39.949808 85.728225,39.546834 88.009582,39.0117 C 90.290937,38.476559 93.524432,37.942456 93.524432,37.942456 L 95.055545,33.79662 L 98.089437,32.913987 L 98.339888,32.217972 L 105.20628,30.316548 L 105.98602,29.676006 L 103.37744,23.976741 L 103.62792,22.690624 L 104.95584,21.994611 L 105.91041,19.079404 L 105.45673,18.675923 z M 72.466874,40.403728 L 72.429067,42.476654 L 73.983813,42.542211 L 73.884576,44.509221 L 70.836515,46.506487 L 70.647496,47.081457 L 71.876167,47.091543 L 71.866712,47.575729 L 62.552432,48.029652 L 62.613863,46.652742 L 63.039175,45.966809 L 63.067524,45.528025 L 63.07698,44.579832 L 63.341609,43.949374 L 63.440849,43.439982 L 63.440849,43.076841 L 63.842533,41.47297 L 72.466874,40.403728 z M 52.987688,42.168984 L 52.760853,43.561027 L 53.488599,44.418431 L 53.441349,45.916386 L 54.117112,46.960408 L 53.942262,48.191039 L 54.443185,48.912273 L 44.939872,49.2855 L 44.916247,48.967759 L 46.017333,48.831579 L 46.069307,48.428097 L 43.66394,47.121797 L 43.536351,45.03375 L 44.689411,44.978276 L 44.788661,42.72883 L 52.987688,42.168984 z M 67.051262,74.276518 L 72.81657,74.649742 L 72.618099,82.411833 L 73.36947,88.776857 L 67.254465,88.565018 L 67.051262,74.276518 z M 28.44258,74.599304 L 37.671807,75.078442 L 36.868446,80.429702 L 36.868446,84.928593 L 37.520583,87.440302 L 28.494569,87.869006 L 28.44258,74.599304 z M 87.508658,74.649742 L 87.508658,87.924488 L 81.644113,88.353194 L 81.440912,81.342592 L 80.788764,74.811143 L 87.508658,74.649742 z M 43.087416,74.947312 L 49.906548,74.972531 L 49.977434,87.278902 L 43.611966,87.389863 L 43.285891,83.400379 L 43.262266,79.441156 L 43.087416,74.947312 z "
+       id="path4735" />
+  </g>
+</svg>
diff --git a/docs/build/jinjabanner.png b/docs/build/jinjabanner.png
new file mode 100644 (file)
index 0000000..c672118
Binary files /dev/null and b/docs/build/jinjabanner.png differ
diff --git a/docs/build/jinjalogo.png b/docs/build/jinjalogo.png
new file mode 100644 (file)
index 0000000..17d6dc3
Binary files /dev/null and b/docs/build/jinjalogo.png differ
diff --git a/docs/build/style.css b/docs/build/style.css
new file mode 100644 (file)
index 0000000..64cba24
--- /dev/null
@@ -0,0 +1,196 @@
+body {
+    background-color: #333;
+    margin: 0;
+    padding: 0;
+    font-family: 'Georgia', serif;
+    font-size: 15px;
+    color: #111;
+}
+
+#content {
+    background-color: white;
+    background-image: url(watermark.png);
+    padding: 10px;
+    margin: 25px;
+    border: 4px solid #ddd;
+}
+
+h1 {
+    margin: 0;
+    padding: 0;
+    height: 80px;
+    background-image: url(jinjabanner.png);
+    background-repeat: no-repeat;
+}
+
+h1 span {
+    display: none;
+}
+
+h2.subheading {
+    margin: -55px 0 35px 200px;
+    font-weight: normal;
+    font-size: 30px;
+    color: #444;
+}
+
+h2.plain {
+    margin: 0;
+}
+
+#jinjalogo {
+    background-image: url(jinjalogo.png);
+    background-repeat: no-repeat;
+    width: 400px;
+    height: 160px;
+}
+
+#contentwrapper {
+    max-width: 700px;
+    padding: 0 0 20px 18px;
+}
+
+#contentwrapper h3,
+#contentwrapper h3 a {
+    color: #b41717;
+    font-size: 26px;
+    margin: 20px 0 0 -5px;
+}
+
+#contentwrapper h4,
+#contentwrapper h4 a {
+    color: #b41717;
+    font-size: 20px;
+    margin: 20px 0 0 0;
+}
+
+table.docutils {
+    border-collapse: collapse;
+    border: 2px solid #aaa;
+    margin: 0.5em 1.5em 0.5em 1.5em;
+}
+
+table.docutils td {
+    padding: 2px;
+    border: 1px solid #ddd;
+}
+
+p, li, dd, dt, blockquote {
+    color: #333;
+}
+
+p {
+    line-height: 150%;
+    margin-bottom: 0;
+    margin-top: 10px;
+    text-align: justify;
+}
+
+hr {
+    border-top: 1px solid #ccc;
+    border-bottom: 0;
+    border-right: 0;
+    border-left: 0;
+    margin-bottom: 10px;
+    margin-top: 20px;
+}
+
+dl {
+    margin-left: 10px;
+}
+
+li, dt {
+    margin-top: 5px;
+}
+
+dt {
+    font-weight: bold;
+}
+
+th {
+    text-align: left;
+    padding: 3px;
+    background-color: #f2f2f2;
+}
+
+a {
+    color: #b41717;
+}
+
+a:hover {
+    color: #444;
+}
+
+pre {
+    background-color: #f9f9f9;
+    border-top: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+    padding: 5px;
+    font-size: 13px;
+    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
+}
+
+tt {
+    font-size: 13px;
+    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
+    color: black;
+    padding: 1px 2px 1px 2px;
+    background-color: #f0f0f0;
+}
+
+cite {
+    /* abusing <cite>, it's generated by ReST for `x` */
+    font-size: 13px;
+    font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
+    font-weight: bold;
+    font-style: normal;
+}
+
+div.admonition {
+    margin: 10px 0 10px 0;
+    padding: 10px;
+    border: 1px solid #ccc;
+    background-color: #f8f8f8;
+}
+
+div.admonition p.admonition-title {
+    margin: -3px 0 5px 0;
+    font-weight: bold;
+    color: #b41717;
+    font-size: 16px;
+}
+
+div.admonition p {
+    margin: 0 0 0 40px;
+}
+
+#toc {
+    margin: 0 -10px 10px 15px;
+    padding: 10px;
+    width: 200px;
+    float: right;
+    background-color: #f8f8f8;
+    border: 1px solid #ccc;
+    border-right: none;
+}
+
+#toc h2 {
+    font-size: 20px;
+    margin: 0 0 10px 0;
+    padding: 0;
+    color: #444;
+}
+
+#toc ul {
+    margin: 0 0 0 30px;
+    padding: 0;
+}
+
+#toc ul + h2 {
+    margin-top: 10px;
+}
+
+#toc ul li {
+    padding: 0;
+    margin: 2px 0 2px 0;
+}
diff --git a/docs/build/watermark.png b/docs/build/watermark.png
new file mode 100644 (file)
index 0000000..297d899
Binary files /dev/null and b/docs/build/watermark.png differ
diff --git a/docs/generate.py b/docs/generate.py
new file mode 100755 (executable)
index 0000000..0c155c0
--- /dev/null
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+    Generate Jinja Documentation
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Generates a bunch of html files containing the documentation.
+
+    :copyright: 2006-2007 by Armin Ronacher, Georg Brandl.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+import re
+import inspect
+from datetime import datetime
+from cgi import escape
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from docutils.core import publish_parts
+from docutils.writers import html4css1
+
+from jinja import Environment
+
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+
+def generate_list_of_filters():
+    from jinja.filters import FILTERS
+    result = []
+
+    filters = {}
+    for name, f in FILTERS.iteritems():
+        if not f in filters:
+            filters[f] = ([name], inspect.getdoc(f))
+        else:
+            filters[f][0].append(name)
+    for names, _ in filters.itervalues():
+        names.sort(key=lambda x: -len(x))
+
+    for names, doc in sorted(filters.values(), key=lambda x: x[0][0].lower()):
+        name = names[0]
+        if len(names) > 1:
+            aliases = '\n\n    :Aliases: %s\n' % ', '.join(names[1:])
+        else:
+            aliases = ''
+
+        doclines = []
+        for line in doc.splitlines():
+            doclines.append('    ' + line)
+        doc = '\n'.join(doclines)
+        result.append('`%s`\n%s%s' % (name, doc, aliases))
+
+    return '\n'.join(result)
+
+def generate_list_of_tests():
+    from jinja.tests import TESTS
+    result = []
+
+    tests = {}
+    for name, f in TESTS.iteritems():
+        if not f in tests:
+            tests[f] = ([name], inspect.getdoc(f))
+        else:
+            tests[f][0].append(name)
+    for names, _ in tests.itervalues():
+        names.sort(key=lambda x: -len(x))
+
+    for names, doc in sorted(tests.values(), key=lambda x: x[0][0].lower()):
+        name = names[0]
+        if len(names) > 1:
+            aliases = '\n\n    :Aliases: %s\n' % ', '.join(names[1:])
+        else:
+            aliases = ''
+
+        doclines = []
+        for line in doc.splitlines():
+            doclines.append('    ' + line)
+        doc = '\n'.join(doclines)
+        result.append('`%s`\n%s%s' % (name, doc, aliases))
+
+    return '\n'.join(result)
+
+def generate_list_of_loaders():
+    from jinja import loaders as loader_module
+
+    result = []
+    loaders = []
+    for item in loader_module.__all__:
+        loaders.append(getattr(loader_module, item))
+    loaders.sort(key=lambda x: x.__name__.lower())
+
+    for loader in loaders:
+        doclines = []
+        for line in inspect.getdoc(loader).splitlines():
+            doclines.append('    ' + line)
+        result.append('`%s`\n%s' % (loader.__name__, '\n'.join(doclines)))
+
+    return '\n\n'.join(result)
+
+e = Environment()
+
+PYGMENTS_FORMATTER = HtmlFormatter(style='pastie', cssclass='syntax')
+
+LIST_OF_FILTERS = generate_list_of_filters()
+LIST_OF_TESTS = generate_list_of_tests()
+LIST_OF_LOADERS = generate_list_of_loaders()
+
+TEMPLATE = e.from_string('''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+   "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+  <title>{{ title }} &mdash; Jinja Documentation</title>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <link rel="stylesheet" href="style.css" type="text/css">
+  <style type="text/css">
+    {{ style|e }}
+  </style>
+</head>
+<body>
+  <div id="content">
+    {% if file_id == 'index' %}
+      <div id="jinjalogo"></div>
+      <h2 class="subheading plain">{{ title }}</h2>
+    {% else %}
+      <h1 class="heading"><span>Jinja</span></h1>
+      <h2 class="subheading">{{ title }}</h2>
+    {% endif %}
+    {% if file_id != 'index' or toc %}
+    <div id="toc">
+      <h2>Navigation</h2>
+      <ul>
+        <li><a href="index.html">back to index</a></li>
+      </ul>
+      {% if toc %}
+        <h2>Contents</h2>
+        <ul class="contents">
+        {% for key, value in toc %}
+          <li><a href="{{ key }}">{{ value }}</a></li>
+        {% endfor %}
+        </ul>
+      {% endif %}
+    </div>
+    {% endif %}
+    <div id="contentwrapper">
+      {{ body }}
+    </div>
+  </div>
+</body>
+<!-- generated on: {{ generation_date }}
+     file id: {{ file_id }} -->
+</html>\
+''')
+
+def pygments_directive(name, arguments, options, content, lineno,
+                      content_offset, block_text, state, state_machine):
+    try:
+        lexer = get_lexer_by_name(arguments[0])
+    except ValueError:
+        # no lexer found
+        lexer = get_lexer_by_name('text')
+    parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER)
+    return [nodes.raw('', parsed, format="html")]
+pygments_directive.arguments = (1, 0, 1)
+pygments_directive.content = 1
+directives.register_directive('sourcecode', pygments_directive)
+
+
+def create_translator(link_style):
+    class Translator(html4css1.HTMLTranslator):
+        def visit_reference(self, node):
+            refuri = node.get('refuri')
+            if refuri is not None and '/' not in refuri and refuri.endswith('.txt'):
+                node['refuri'] = link_style(refuri[:-4])
+            html4css1.HTMLTranslator.visit_reference(self, node)
+    return Translator
+
+
+class DocumentationWriter(html4css1.Writer):
+
+    def __init__(self, link_style):
+        html4css1.Writer.__init__(self)
+        self.translator_class = create_translator(link_style)
+
+    def translate(self):
+        html4css1.Writer.translate(self)
+        # generate table of contents
+        contents = self.build_contents(self.document)
+        contents_doc = self.document.copy()
+        contents_doc.children = contents
+        contents_visitor = self.translator_class(contents_doc)
+        contents_doc.walkabout(contents_visitor)
+        self.parts['toc'] = self._generated_toc
+
+    def build_contents(self, node, level=0):
+        sections = []
+        i = len(node) - 1
+        while i >= 0 and isinstance(node[i], nodes.section):
+            sections.append(node[i])
+            i -= 1
+        sections.reverse()
+        toc = []
+        for section in sections:
+            try:
+                reference = nodes.reference('', '', refid=section['ids'][0], *section[0])
+            except IndexError:
+                continue
+            ref_id = reference['refid']
+            text = escape(reference.astext().encode('utf-8'))
+            toc.append((ref_id, text))
+
+        self._generated_toc = [('#%s' % href, caption) for href, caption in toc]
+        # no further processing
+        return []
+
+
+def generate_documentation(data, link_style):
+    writer = DocumentationWriter(link_style)
+    data = data.replace('[[list_of_filters]]', LIST_OF_FILTERS)\
+               .replace('[[list_of_tests]]', LIST_OF_TESTS)\
+               .replace('[[list_of_loaders]]', LIST_OF_LOADERS)
+    parts = publish_parts(
+        data,
+        writer=writer,
+        settings_overrides={
+            'initial_header_level': 3,
+            'field_name_limit': 50,
+        }
+    )
+    return {
+        'title':        parts['title'].encode('utf-8'),
+        'body':         parts['body'].encode('utf-8'),
+        'toc':          parts['toc']
+    }
+
+
+def handle_file(filename, fp, dst):
+    now = datetime.now()
+    title = os.path.basename(filename)[:-4]
+    content = fp.read()
+    parts = generate_documentation(content, (lambda x: './%s.html' % x))
+    result = file(os.path.join(dst, title + '.html'), 'w')
+    c = dict(parts)
+    c['style'] = PYGMENTS_FORMATTER.get_style_defs('.syntax')
+    c['generation_date'] = now
+    c['file_id'] = title
+    result.write(TEMPLATE.render(c).encode('utf-8'))
+    result.close()
+
+
+def run(dst, sources=()):
+    path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
+    if not sources:
+        sources = [os.path.join(path, fn) for fn in os.listdir(path)]
+    for fn in sources:
+        if not os.path.isfile(fn):
+            continue
+        print 'Processing %s' % fn
+        f = open(fn)
+        try:
+            handle_file(fn, f, dst)
+        finally:
+            f.close()
+
+
+def main(dst='build/', *sources):
+    return run(os.path.realpath(dst), sources)
+
+
+if __name__ == '__main__':
+    main(*sys.argv[1:])
diff --git a/docs/src/designerdoc.txt b/docs/src/designerdoc.txt
new file mode 100644 (file)
index 0000000..c3db752
--- /dev/null
@@ -0,0 +1,432 @@
+======================
+Designer Documentation
+======================
+
+This part of the Jinja documentaton is meant for template designers.
+
+Basics
+======
+
+The Jinja template language is designed to strike a balance between content
+and application logic. Nevertheless you can use a python like statement
+language. You don't have to know how Python works to create Jinja templates,
+but if you know it you can use some additional statements you may know from
+Python.
+
+Here is a small example template:
+
+.. sourcecode:: html+jinja
+
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+    <head>
+        <title>My Webpage</title>
+    </head>
+    <body>
+        <ul id="navigation">
+        {% for item in navigation %}
+            <li><a href="{{ item.href|e }}">{{ item.caption|e }}</a></li>
+        {% endfor %}
+        </ul>
+
+        <h1>My Webpage</h1>
+        {{ variable }}
+    </body>
+    </html>
+
+This covers the default settings. The application developer might have changed
+the syntax from ``{% foo %}`` to ``<% foo %>`` or something similar. This
+documentation just covers the default values.
+
+A variable looks like ``{{ foobar }}`` where foobar is the variable name. Inside
+of statements (``{% some content here %}``) variables are just normal names
+without the braces around it. In fact ``{{ foobar }}`` is just an alias for
+the statement ``{% print foobar %}``.
+
+Variables are coming from the context provided by the application. Normally there
+should be a documentation regarding the context contents but if you want to know
+the content of the current context you can add this to your template:
+
+.. sourcecode:: html+jinja
+
+    <pre>{{ debug()|e }}</pre>
+
+A context isn't flat which means that each variable can has subvariables, as long
+as it is representable as python data structure. You can access attributes of
+a variable by using the dot and brace operators. The following examples show
+this:
+
+.. sourcecode:: jinja
+
+    {{ user.username }}
+        is the same as
+    {{ user['username'] }}
+        you can also use a variable to access an attribute:
+    {{ users[current_user].username }}
+        If you have numerical indices you have to use the [] syntax:
+    {{ users[0].username }}
+
+Filters
+=======
+
+In the examples above you might have noticed the pipe symbols. Pipe symbols tell
+the engine that it has to apply a filter on the variable. Here a small example:
+
+.. sourcecode:: jinja
+
+    {{ variable|replace('foo', 'bar')|escape }}
+
+If you like you can also put whitespace between the filters.
+
+This will look for a variable variable, passes it to the filter replace with the
+arguments ``'foo'`` and ``'bar'``, and passes the result to the filter `escape`
+that automatically xml escapes the value. The ``e`` filter is an alias for
+``escape``. Here the complete list of supported filters:
+
+[[list_of_filters]]
+
+Tests
+=====
+
+You can use the `is`-operator to perform tests on a value:
+
+.. sourcecode:: jinja
+
+    {{ 42 is numeric }} -> true
+    {{ "foobar" is numeric }} -> false
+    {{ 'FOO' is upper }} -> true
+
+Those tests are especially useful if used in `if`-conditions.
+
+[[list_of_tests]]
+
+Loops
+=====
+
+To iterate over a sequence you can use the `for`-loop. If basically looks like a
+normal python for loop and works pretty much the same:
+
+.. sourcecode:: html+jinja
+
+    <h1>Members</h1>
+    <ul>
+    {% for user in users %}
+      <li>{{ loop.index }} / {{ loop.length }} - {{ user.username|escape }}</li>
+    {% else %}
+      <li><em>no users found</em></li>
+    {% endfor %}
+    </ul>
+
+The optional ``else`` block is only executed if the template did not iterate
+because the sequence was empty.
+
+Inside of a for loop block you can access some special variables:
+
++----------------------+----------------------------------------+
+| Variable             | Description                            |
++======================+========================================+
+| ``loop.index``       | The current iteration of the loop.     |
++----------------------+----------------------------------------+
+| ``loop.index0``      | The current iteration of the loop,     |
+|                      | starting counting by 0.                |
++----------------------+----------------------------------------+
+| ``loop.revindex``    | The number of iterations from the end  |
+|                      | of the loop.                           |
++----------------------+----------------------------------------+
+| ``loop.revindex0``   | The number of iterations from the end  |
+|                      | of the loop, starting counting by 0.   |
++----------------------+----------------------------------------+
+| ``loop.first``       | True if first iteration.               |
++----------------------+----------------------------------------+
+| ``loop.last``        | True if last iteration.                |
++----------------------+----------------------------------------+
+| ``loop.even``        | True if current iteration is even.     |
++----------------------+----------------------------------------+
+| ``loop.odd``         | True if current iteration is odd.      |
++----------------------+----------------------------------------+
+| ``loop.length``      | Total number of items in the sequence. |
++----------------------+----------------------------------------+
+| ``loop.parent``      | The context of the parent loop.        |
++----------------------+----------------------------------------+
+
+Loops also support recursion. For example you have a sitemap where each item
+might have a number of child items. Such a template could look like this:
+
+.. sourcecode:: html+jinja
+
+    <h1>Sitemap
+    <ul id="sitemap">
+    {% for item in sitemap recursive %}
+      <li><a href="{{ item.url|e }}">{{ item.title|e }}</a>
+      {% if item.children %}<ul>{{ loop(item.children) }}</ul>{% endif %}</li>
+    {% endfor %}
+    </ul>
+
+Now. What happens here? Basically the first thing that is different to a normal
+loop is the additional ``recursive`` modifier in the `for`-loop declaration.
+It tells the template engine that we want recursion. If recursion is enabled
+the special loop variable is callable. If you call it with a sequence it will
+automatically render that loop at that position with the new sequence as argument.
+
+Cycling
+=======
+
+Sometimes you might want to have different classes for each row in a list. For
+example to have alternating row colors. You can easily do this by using the
+``{% cycle %}`` tag:
+
+.. sourcecode:: html+jinja
+
+    <ul id="messages">
+    {% for message in messages %}
+      <li class="{% cycle 'row1', 'row2' %}">{{ message|e }}</li>
+    {% endfor %}
+    </ul>
+
+Each time Jinja encounters a cycle tag it will evaluate cycle through the list
+of given items and return the next one. If you pass it one item jinja assumes
+that this item is a sequence from the context and uses this:
+
+.. sourcecode:: html+jinja
+
+    <li style="color: {% cycle rowcolors %}">...</li>
+
+Conditions
+==========
+
+Jinja supports python like ``if`` / ``elif`` / ``else`` constructs:
+
+.. sourcecode:: jinja
+
+    {% if user.active %}
+        user {{ user.name|e }} is active.
+    {% elif user.deleted %}
+        user {{ user.name|e }} was deleted some time ago.
+    {% else %}
+        i don't know what's wrong with {{ user.username|e }}
+    {% endif %}
+
+If the user is active the first block is rendered. If not and the user was
+deleted the second one, in all other cases the third one.
+
+You can also use comparison operators:
+
+.. sourcecode:: html+jinja
+
+    {% if amount < 0 %}
+        <span style="color: red">{{ amount }}</span>
+    {% else %}
+        <span style="color: black">{{ amount }}</span>
+    {% endif %}
+
+.. admonition:: Note
+
+    Of course you can use `or` / `and` and parenthesis to create more complex
+    conditions but usually the logic is already handled in the application and
+    you don't have to create such complex constucts in the template code. However
+    in some situations it might be a good thing to have the abilities to create
+    them.
+
+Operators
+=========
+
+Inside ``{{ variable }}`` blocks, `if`-conditions and many other parts you can
+can use Expressions. In expressions you can use any of the following operators:
+
+    ======= ===================================================================
+    ``+``   add the right operand to the left one.
+            ``{{ 1 + 2 }}`` would return three.
+    ``-``   substract the right operand from the left one.
+            ``{{ 1 - 1 }}`` would return zero.
+    ``/``   divide the right from the left operand.
+            ``{{ 1 / 2 }}`` would return 0.5
+    ``*``   multiply the left operand with the right.
+            ``{{ 2 * 2}}`` would return 4
+    ``**``  raise the left operand to the power of the right
+            operand. ``{{ 2**3 }}`` would return 8
+    ``is``  perform a test on the value. See the section about
+            tests for more information.
+    ``|``   apply a filter on the value. See the section about
+            filters for more information.
+    ``and`` return true if the left and the right operand is true.
+    ``or``  return true if the left or the right operand is true.
+    ``()``  call a callable. ``{{ user.get_username() }}``. Inside of the
+            parenthesis you can use variables: ``{{ user.get('username') }}``.
+    ======= ===================================================================
+
+Note that there is no support for any bit operation or something similar.
+
+Macros
+======
+
+If you want to use a partial template on more than one place you might want to
+create a macro out of it:
+
+.. sourcecode:: html+jinja
+
+    {% macro show_user user %}
+      <h1>{{ user.name|e }}</h1>
+      <div class="test">
+        {{ user.description }}
+      </div>
+    {% endmacro %}
+
+Now you can use it from everywhere in the code by passing it an item:
+
+.. sourcecode:: jinja
+    
+    {% for user in users %}
+        {{ show_user(user) }}
+    {% endfor %}
+
+You can also specify more then one value:
+
+.. sourcecode:: html+jinja
+
+    {% macro show_dialog title, text %}
+      <div class="dialog">
+        <h1>{{ title|e }}</h1>
+        <div class="test">{{ text|e }}</div>
+      </div>
+    {% endmacro %}
+
+    {{ show_dialog('Warning', 'something went wrong i guess') }}
+
+Inheritance
+===========
+
+The most powerful part of Jinja is template inheritance. Template inheritance
+allows you to build a base "skeleton" template that contains all the common
+elements of your site and defines **blocks** or **markers** that child
+templates can override.
+
+Sounds complicated but is very basic. It's easiest to understand it by starting
+with an example.
+
+Base Template
+-------------
+
+This template, which we'll call ``base.html``, defines a simple HTML skeleton
+document that you might use for a simple two-column page. It's the job of
+"child" templates to fill the empty blocks with content:
+
+.. sourcecode:: html+jinja
+
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+    <html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+      <link rel="stylesheet" href="style.css" />
+      <title>{% block title %}{% endblock %} - My Webpage</title>
+      {% block html_head %}{% endblock %}
+    </head>
+    <body>
+      <div id="content">
+        {% block content %}{% endblock %}
+      </div>
+
+      <div id="footer">
+        {% block "footer" %}
+        &copy; Copyright 2006 by <a href="http://mydomain.tld">myself</a>.
+        {% endblock %}
+      </div>
+    </body>
+
+In this example, the ``{% block %}`` tags define four blocks that child templates
+can fill in. All the ``block`` tag does is to tell the template engine that a
+child template may override those portions of the template.
+
+Child Template
+--------------
+
+A child template might look like this:
+
+.. sourcecode:: html+jinja
+
+    {% extends "base.html" %}
+    {% block title %}Index{% endblock %}
+
+    {% block html_head %}
+      <style type="text/css">
+        .important {
+          color: #336699;
+        }
+      </style>
+    {% endblock %}
+    
+    {% block content %}
+        <h1>Index</h1>
+        <p class="important">
+          Welcome on my awsome homepage.
+        </p>
+    {% endblock %}
+
+The ``{% extends %}`` tag is the key here. It tells the template engine that
+this template "extends" another template. When the template system evaluates
+this template, first it locates the parent.
+
+The filename of the template depends on the template loader. For example the
+``FileSystemLoader`` allows you to access other templates by giving the
+filename. You can access subdirectory with an slash:
+
+.. sourcecode:: jinja
+
+    {% extends "layout/default.html" %}
+
+But this behavior can depend on the application using Jinja.
+
+Note that since the child template didn't define the ``footer`` block, the
+value from the parent template is used instead.
+
+.. admonition:: Note
+
+    You can't define multiple ``{% block %}`` tags with the same name in the
+    same template. This limitation exists because a block tag works in "both"
+    directions. That is, a block tag doesn't just provide a hole to fill - it
+    also defines the content that fills the hole in the *parent*. If there were
+    two similarly-named ``{% block %}`` tags in a template, that template's
+    parent wouldn't know which one of the blocks' content to use.
+
+Template Inclusion
+==================
+
+You can load another template at a given posiiton using ``{% include %}``.
+Usually it's a better idea to use inheritance but if you for example want to
+load macros ``include`` works better than ``extends``:
+
+.. sourcecode:: jinja
+
+    {% include "myhelpers.html" %}
+    {{ my_helper("foo") }}
+
+If you define a macro called ``my_helper`` in ``myhelpers.html`` you can now
+use it from the template as shown above.
+
+Filtering Blocks
+================
+
+Sometimes it could be a good idea to filter a complete block. For example if
+you want to escape some html code:
+
+.. sourcecode:: jinja
+
+    {% filter escape %}
+        <html>
+          <code>goes here</code>
+        </html>
+    {% endfilter %}
+
+Of course you can chain filters too.
+
+Defining Variables
+==================
+
+You can also define variables in the namespace using the ``{% set %}`` tag:
+
+.. sourcecode:: jinja
+
+    {% set foo = 'foobar' %}
+    {{ foo }}
+
+This should ouputput ``foobar``.
diff --git a/docs/src/devintro.txt b/docs/src/devintro.txt
new file mode 100644 (file)
index 0000000..157301c
--- /dev/null
@@ -0,0 +1,139 @@
+====================
+Developer Quickstart
+====================
+
+This part of the documentation shows you how to embedd Jinja into your
+application.
+
+Starting Up
+===========
+
+Here the quickest way to create a template from a string and render it:
+
+.. sourcecode:: python
+
+    from jinja import Environment
+    env = Environment()
+    tmpl = env.from_string('Hello {{ name }}!')
+    print tmpl.render(name='John Doe')
+
+This example should output the following string after execution::
+
+    Hello John Doe!
+
+If you receive an error check if you have a typo in your code. If not have
+a look at the `installation`_ page for troubleshooting.
+
+The Environment
+===============
+
+The core component of Jinja is the `Environment`. It helds important shared
+variables like configuration, filters, tests, globals and other stuff.
+
+Here the possible initialisation parameters:
+
+=========================== ==================================================
+``block_start_string`` *    the string marking the begin of a block. this
+                            defaults to ``'{%'``.
+``block_end_string`` *      the string marking the end of a block. defaults
+                            to ``'%}'``.
+``variable_start_string`` * the string marking the begin of a print
+                            statement. defaults to ``'{{'``.
+``comment_start_string`` *  the string marking the begin of a
+                            comment. defaults to ``'{#'``.
+``comment_end_string`` *    the string marking the end of a comment.
+                            defaults to ``'#}'``.
+``trim_blocks`` *           If this is set to ``True`` the first newline
+                            after a block is removed (block, not
+                            variable tag!). Defaults to ``False``.
+``auto_escape``             If this is set to ``True`` Jinja will
+                            automatically escape all variables using xml
+                            escaping methods. If you don't want to escape a
+                            string you have to wrap it in a ``Markup``
+                            object from the ``jinja.datastructure`` module.
+``template_charset``        The charset of the templates. Defaults
+                            to ``'utf-8'``.
+``charset``                 Charset of all string input data. Defaults
+                            to ``'utf-8'``.
+``namespace``               Global namespace for all templates.
+``loader``                  Specify a template loader.
+``filters``                 dict of filters or the default filters if not
+                            defined.
+``tests``                   dict of tests of the default tests if not defined.
+=========================== ==================================================
+
+All of this variables except those marked with a star(*) are modifyable after
+environment initialisation.
+
+The environment provides the following useful functions and properties
+additional to the initialisation values:
+
+=========================== ==================================================
+``parse(source, filename)`` Parse the sourcecode and return the abstract
+                            syntax tree. This tree of nodes is used by the
+                            `translators`_ to convert the template into
+                            executable source- or bytecode.
+``from_string(source)``     Load and parse a template source and translate it
+                            into evaluable python code. This code is wrapped
+                            with in a `Template` class that allows you to
+                            render it.
+``get_template(name)``      load a template from a loader. If the template
+                            does not exist you will get a `TemplateNotFound`
+                            exception.
+=========================== ==================================================
+
+There are also some internal functions on the environment used by the template
+evaluation code to keep it sandboxed.
+
+Loading Templates From Files
+============================
+
+Loading templates from a string is always a bad idea. It doesn't allow template
+inheritance and is also slow since it parses and compiles the template again
+and again whereas loaders can cache the template code.
+
+All you have to do is to define a loader and use the `get_template` function.
+
+.. sourcecode:: python
+
+    from jinja import Environment, FileSystemLoader
+    env = Environment(loader=FileSystemLoader('templates'))
+    tmpl = env.get_template('index.html')
+    print tmpl.render(name='John Doe')
+
+This tells jinja to look for templates in the ``templates`` folder. It's a
+better idea to use an absolute path here though. For a list of supported
+loaders or how to write your own, head over to the `loader`_ documentation.
+
+Adding Filters
+==============
+
+If you want to add additional filters to the environment the best way is to
+modify the ``filters`` attribute and not to pass a dict to the environment.
+If you pass it a dict it will not include the default filters!
+
+.. sourcecode:: python
+
+    from mylib import my_cool_filter
+    env.filters['mycoolfilter'] = my_cool_filter
+
+Writing filter functions is explained in the `filter development`_ section.
+
+Adding Tests
+============
+
+Adding additional tests works analog to filters:
+
+.. sourcecode:: python
+
+    from mylib import my_cool_test
+    env.tests['mycooltest'] = my_cool_test
+
+Writing tests is explained in the `test development`_ section.
+
+
+.. _installation: installation.txt
+.. _translators: translators.txt
+.. _loader: loaders.txt
+.. _filter development: filters.txt
+.. _test development: tests.txt
diff --git a/docs/src/fromdjango.txt b/docs/src/fromdjango.txt
new file mode 100644 (file)
index 0000000..71b2068
--- /dev/null
@@ -0,0 +1,108 @@
+===============================
+Differences To Django Templates
+===============================
+
+If you have previously worked with Django templates you should feel very
+familiar. In fact most of the syntax elements look and work the same.
+
+However Jinja provides some more syntax elements covered in the documentation
+and some work a bit different.
+
+Method Calls
+============
+
+In Django method calls work implicit. With Jinja you have to tell it that you
+want to call it. Thus this Django code:
+
+.. sourcecode:: django
+
+    {% for page in user.get_created_pages %}
+      ...
+    {% endfor %}
+
+will look like this in Jinja:
+
+.. sourcecode:: jinja
+
+    {% for page in user.get_created_pages() %}
+      ...
+    {% endfor %}
+
+This allows you to pass variables to the function which is also used for
+macros and loop recursion, both features that don't exist in Django.
+
+Conditions
+==========
+
+In Django you can use the following constructs to check for equality:
+
+.. sourcecode:: django
+
+    {% ifequals foo "bar" %}
+        ...
+    {% else %}
+        ...
+    {% endifequals %}
+
+In Jinja you can use the normal ``if`` statement in combination with
+operators:
+
+.. sourcecode:: jinja
+
+    {% if foo == 'bar' %}
+        ...
+    {% else %}
+        ...
+    {% endif %}
+
+You can also have multiple ``elif`` branches in your template:
+
+.. sourcecode:: jinja
+
+    {% if something %}
+        ...
+    {% elif otherthing %}
+        ...
+    {% elif foothing %}
+        ...
+    {% else %}
+        ...
+    {% endif %}
+
+Filter Arguments
+================
+
+Jinja provides more than one argument for a filter. Also the syntax for argument
+passing is different. A template that looks like this in Django:
+
+.. sourcecode:: django
+
+    {{ items|join:", " }}
+
+looks like this in jinja:
+
+.. sourcecode:: jinja
+
+    {{ items|join(', ') }}
+
+In fact it's a bit more to write but it allows different type of arguments including
+variables and more then one of them.
+
+Tests
+=====
+
+Additionally to filters there also exists tests you can perform using the `is`
+operator. Here some examples:
+
+.. sourcecode:: jinja
+
+    {% if user.user_id is odd %}
+        {{ user.username|e }} is odd
+    {% else %}
+        hmm. {{ user.username|e }} looks pretty normal
+    {% endif %}
+
+For a list of supported tests head over to the `syntax reference`_.
+
+
+.. _syntax reference: designerdoc.txt
diff --git a/docs/src/index.txt b/docs/src/index.txt
new file mode 100644 (file)
index 0000000..250a5a3
--- /dev/null
@@ -0,0 +1,25 @@
+======================
+Documentation Overview
+======================
+
+Welcome in the Jinja documentation.
+
+- `Installing Jinja <installation.txt>`_
+
+- Application Developer Documentation:
+
+  - `Quickstart <devintro.txt>`_
+
+  - `Template Loaders <loaders.txt>`_
+
+  - `Filter Functions <filters.txt>`_
+
+  - `Test Functions <tests.txt>`_
+
+  - `Translators <translators.txt>`_
+
+- Template Designer Documentation:
+
+  - `Syntax Reference <designerdoc.txt>`_
+
+  - `Differences To Django <fromdjango.txt>`_
diff --git a/docs/src/loaders.txt b/docs/src/loaders.txt
new file mode 100644 (file)
index 0000000..dbe4df3
--- /dev/null
@@ -0,0 +1,10 @@
+================
+Template Loaders
+================
+
+This part of the documentation explains how to use and write a template loader.
+
+Builtin Loaders
+===============
+
+[[list_of_loaders]]
index f65931c287a745d8aa1a7891b6d1324dfdd6b332..be142ee7761769aacc31ea2cacde4e5a7cd73d6e 100644 (file)
@@ -7,6 +7,4 @@
     :license: BSD, see LICENSE for more details.
 """
 from jinja.environment import Environment
-from jinja.loaders import FileSystemLoader
-
-__all__ = ['Environment', 'FileSystemLoader']
+from jinja.loaders import *
index fed5cfacc16e5c4bbc492a4e55e0bfce7e207546..d0b6d447daec053eea006b9b33adde1aac792a81 100644 (file)
@@ -89,6 +89,9 @@ class Markup(unicode):
     auto_escape option values marked as `Markup` aren't escaped.
     """
 
+    def __repr__(self):
+        return 'Markup(%s)' % unicode.__repr__(self)
+
 
 safe_types = set([Markup, int, long, float])
 
index ffce6b380d7b710e5bb5891a1f3d4710df4cefb8..4277f3433326487bc646a4fb77394eb935f2711c 100644 (file)
@@ -68,11 +68,11 @@ class Environment(object):
         Get or set the template loader.
         """
         self._loader = LoaderWrapper(self, value)
-    loader = property(lambda s: s._loader, loader, loader.__doc__)
+    loader = property(lambda s: s._loader.loader, loader, loader.__doc__)
 
-    def parse(self, source):
+    def parse(self, source, filename=None):
         """Function that creates a new parser and parses the source."""
-        parser = Parser(self, source)
+        parser = Parser(self, source, filename)
         return parser.parse()
 
     def from_string(self, source):
index e45849d7a5f25f983ef19a6312f1fdb5880ab095..d1ecc5d11ff0e3a65b239c2c50ec8c923943191f 100644 (file)
@@ -11,6 +11,7 @@
 from random import choice
 from urllib import urlencode, quote
 from jinja.utils import escape
+from jinja.datastructure import Undefined
 
 
 try:
@@ -40,16 +41,29 @@ def stringfilter(f):
                     nargs[idx] = env.to_unicode(var)
             return f(env.to_unicode(value), *nargs)
         return wrapped
+    try:
+        decorator.__doc__ = f.__doc__
+        decorator.__name__ = f.__name__
+    except:
+        pass
     return decorator
 
 
 def do_replace(s, old, new, count=None):
     """
-    {{ s|replace(old, new, count=None) }}
+    Return a copy of the value with all occurrences of a substring
+    replaced with a new one. The first argument is the substring
+    that should be replaced, the second is the replacement string.
+    If the optional third argument ``count`` is given, only the first
+    ``count`` occurrences are replaced:
+
+    .. sourcecode:: jinja
 
-    Return a copy of s with all occurrences of substring
-    old replaced by new. If the optional argument count is
-    given, only the first count occurrences are replaced.
+        {{ "Hello World"|replace("Hello", "Goodbye") }}
+            -> Goodbye World
+
+        {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
+            -> d'oh, d'oh, aaargh
     """
     if count is None:
         return s.replace(old, new)
@@ -59,9 +73,7 @@ do_replace = stringfilter(do_replace)
 
 def do_upper(s):
     """
-    {{ s|upper }}
-
-    Return a copy of s converted to uppercase.
+    Convert a value to uppercase.
     """
     return s.upper()
 do_upper = stringfilter(do_upper)
@@ -69,9 +81,7 @@ do_upper = stringfilter(do_upper)
 
 def do_lower(s):
     """
-    {{ s|lower }}
-
-    Return a copy of s converted to lowercase.
+    Convert a value to lowercase.
     """
     return s.lower()
 do_lower = stringfilter(do_lower)
@@ -79,10 +89,12 @@ do_lower = stringfilter(do_lower)
 
 def do_escape(s, attribute=False):
     """
-    {{ s|escape(attribute) }}
+    XML escape ``&``, ``<``, and ``>`` in a string of data. If the
+    optional parameter is `true` this filter will also convert
+    ``"`` to ``&quot;``. This filter is just used if the environment
+    was configured with disabled `auto_escape`.
 
-    XML escape &, <, and > in a string of data. If attribute is
-    True it also converts ``"`` to ``&quot;``
+    This method will have no effect it the value is already escaped.
     """
     return escape(s, attribute)
 do_escape = stringfilter(do_escape)
@@ -90,9 +102,9 @@ do_escape = stringfilter(do_escape)
 
 def do_addslashes(s):
     """
-    {{ s|addslashes }}
-
-    Adds slashes to s.
+    Add backslashes in front of special characters to s. This method
+    might be useful if you try to fill javascript strings. Also have
+    a look at the `jsonencode` filter.
     """
     return s.encode('utf-8').encode('string-escape').decode('utf-8')
 do_addslashes = stringfilter(do_addslashes)
@@ -100,10 +112,8 @@ do_addslashes = stringfilter(do_addslashes)
 
 def do_capitalize(s):
     """
-    {{ s|capitalize }}
-
-    Return a copy of the string s with only its first character
-    capitalized.
+    Capitalize a value. The first character will be uppercase, all others
+    lowercase.
     """
     return s.capitalize()
 do_capitalize = stringfilter(do_capitalize)
@@ -111,33 +121,52 @@ do_capitalize = stringfilter(do_capitalize)
 
 def do_title(s):
     """
-    {{ s|title }}
-
-    Return a titlecased version of s, i.e. words start with uppercase
-    characters, all remaining cased characters have lowercase.
+    Return a titlecased version of the value. I.e. words will start with
+    uppercase letters, all remaining characters are lowercase.
     """
     return s.title()
 do_title = stringfilter(do_title)
 
 
-def do_default(default_value=u''):
+def do_default(default_value=u'', boolean=False):
     """
-    {{ s|default(default_value='') }}
+    If the value is undefined it will return the passed default value,
+    otherwise the value of the variable:
+
+    .. sourcecode:: jinja
+
+        {{ my_variable|default('my_variable is not defined') }}
 
-    In case of s isn't set or True default will return default_value
-    which is '' per default.
+    This will output the value of ``my_variable`` if the variable was
+    defined, otherwise ``'my_variable is not defined'``. If you want
+    to use default with variables that evaluate to false you have to
+    set the second parameter to `true`:
+
+    .. sourcecode:: jinja
+
+        {{ ''|default('the string was empty', true) }}
     """
-    return lambda e, c, v: v or default_value
+    def wrapped(env, context, value):
+        if (boolean and not v) or v in (Undefined, None):
+            return default_value
+        return v
+    return wrapped
 do_default = stringfilter(do_default)
 
 
 def do_join(d=u''):
     """
-    {{ sequence|join(d='') }}
-
     Return a string which is the concatenation of the strings in the
-    sequence. The separator between elements is d which is an empty
-    string per default.
+    sequence. The separator between elements is an empty string per
+    default, you can define ith with the optional parameter:
+
+    .. sourcecode:: jinja
+
+        {{ [1, 2, 3]|join('|') }}
+            -> 1|2|3
+
+        {{ [1, 2, 3]|join }}
+            -> 123
     """
     def wrapped(env, context, value):
         d = env.to_unicode(d)
@@ -147,12 +176,9 @@ def do_join(d=u''):
 
 def do_count():
     """
-    {{ var|count }}
-
-    Return the length of var. In case if getting an integer or float
+    Return the length of the value. In case if getting an integer or float
     it will convert it into a string an return the length of the new
-    string.
-    If the object doesn't provide a __len__ function it will return zero
+    string. If the object has no length it will of corse return 0.
     """
     def wrapped(env, context, value):
         try:
@@ -164,29 +190,16 @@ def do_count():
     return wrapped
 
 
-def do_odd():
-    """
-    {{ var|odd }}
-
-    Return true if the variable is odd.
-    """
-    return lambda e, c, v: v % 2 == 1
-
-
-def do_even():
-    """
-    {{ var|even }}
-
-    Return true of the variable is even.
-    """
-    return lambda e, c, v: v % 2 == 0
-
-
 def do_reversed():
     """
-    {{ var|reversed }}
+    Return a reversed list of the sequence filtered. You can use this
+    for example for reverse iteration:
+
+    .. sourcecode:: jinja
 
-    Return a reversed list of the iterable filtered.
+        {% for item in seq|reversed %}
+            {{ item|e }}
+        {% endfor %}
     """
     def wrapped(env, context, value):
         try:
@@ -200,83 +213,59 @@ def do_reversed():
 
 def do_center(value, width=80):
     """
-    {{ var|center(80) }}
-
     Centers the value in a field of a given width.
     """
     return value.center(width)
 do_center = stringfilter(do_center)
 
 
-def do_title(value):
-    """
-    {{ var|title }}
-
-    Capitalize the first character of all words.
-    """
-    return value.title()
-do_title = stringfilter(do_title)
-
-
-def do_capitalize(value):
-    """
-    {{ var|capitalize }}
-
-    Capitalize the first character of the string.
-    """
-    return value.capitalize()
-do_capitalize = stringfilter(do_capitalize)
-
-
 def do_first():
     """
-    {{ var|first }}
-
-    Return the frist item of a sequence or None.
+    Return the frist item of a sequence.
     """
     def wrapped(env, context, seq):
         try:
             return iter(seq).next()
         except StopIteration:
-            return
+            return Undefined
     return wrapped
 
 
 def do_last():
     """
-    {{ var|last }}
-
     Return the last item of a sequence.
     """
     def wrapped(env, context, seq):
         try:
             return iter(_reversed(seq)).next()
         except (TypeError, StopIteration):
-            return
+            return Undefined
     return wrapped
 
 
 def do_random():
     """
-    {{ var|random }}
-
     Return a random item from the sequence.
     """
     def wrapped(env, context, seq):
         try:
             return choice(seq)
         except:
-            return
+            return Undefined
     return wrapped
 
 
 def do_urlencode():
     """
-    {{ var|urlencode }}
+    urlencode a string or directory.
 
-    {{ {'foo': 'bar'}|urlencode }}
+    .. sourcecode:: jinja
 
-    urlencode a string or directory.
+        {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }}
+            -> foo=bar&blub=blah
+
+        {{ 'Hello World' }}
+            -> Hello%20World
     """
     def wrapped(env, context, value):
         if isinstance(value, dict):
@@ -291,9 +280,12 @@ def do_urlencode():
 
 def do_jsonencode():
     """
-    {{ var|jsonencode }}
-
     JSON dump a variable. just works if simplejson is installed.
+
+    .. sourcecode:: jinja
+
+        {{ 'Hello World'|jsonencode }}
+            -> "Hello World"
     """
     global simplejson
     try:
@@ -315,8 +307,6 @@ FILTERS = {
     'default':              do_default,
     'join':                 do_join,
     'count':                do_count,
-    'odd':                  do_odd,
-    'even':                 do_even,
     'reversed':             do_reversed,
     'center':               do_center,
     'title':                do_title,
index a7a1221dd4d17753e1130c1a5e50f5621ad2cc84..98c4729033a835a3c83187f22d795d2d1472a936 100644 (file)
@@ -16,6 +16,9 @@ from jinja.translators.python import PythonTranslator
 from jinja.exceptions import TemplateNotFound
 
 
+__all__ = ['FileSystemLoader']
+
+
 def get_template_filename(searchpath, name):
     """
     Return the filesystem filename wanted.
@@ -68,10 +71,28 @@ class LoaderWrapper(object):
 
 class FileSystemLoader(object):
     """
-    Loads templates from the filesystem::
+    Loads templates from the filesystem:
+
+    .. sourcecode:: python
 
         from jinja import Environment, FileSystemLoader
         e = Environment(loader=FileSystemLoader('templates/'))
+
+    You can pass the following keyword arguments to the loader on
+    initialisation:
+
+    =================== =================================================
+    ``searchpath``      String with the path to the templates on the
+                        filesystem.
+    ``use_cache``       Set this to ``True`` to enable memory caching.
+                        This is usually a good idea in production mode,
+                        but disable it during development since it won't
+                        reload template changes automatically.
+                        This only works in persistent environments like
+                        FastCGI.
+    ``cache_size``      Number of template instance you want to cache.
+                        Defaults to ``40``.
+    =================== =================================================
     """
 
     def __init__(self, searchpath, use_cache=False, cache_size=40):
index 256bcbd36909b1156c4436d4a8d89875adb581c5..400e4e233c1baf3bd083a3b0896e5c91381f8b58 100644 (file)
@@ -133,9 +133,9 @@ class IfCondition(Node):
 
     def get_items(self):
         result = []
-        for test in tests:
+        for test in self.tests:
             result.extend(test)
-        result.append(self._else)
+        result.append(self.else_)
         return result
 
     def __repr__(self):
index a8692eb2e3759752ca6d1262713afcff54c24287..95220e5a6c7b148ec0dc52307dc9c5df9c10ea2d 100644 (file)
@@ -19,54 +19,52 @@ regex_type = type(number_re)
 
 def test_odd():
     """
-    {{ var is odd }}
-
-    Return True if the variable is odd.
+    Return true if the variable is odd.
     """
     return lambda e, c, v: v % 2 == 1
 
 
 def test_even():
     """
-    {{ var is even }}
-
-    Return True of the variable is even.
+    Return true of the variable is even.
     """
     return lambda e, c, v: v % 2 == 0
 
 
 def test_defined():
     """
-    {{ var is defined }}
+    Return true if the variable is defined:
+
+    .. sourcecode:: jinja
 
-    Return True if the variable is defined.
+        {% if variable is defined %}
+            value of variable: {{ variable }}
+        {% else %}
+            variable is not defined
+        {% endif %}
+
+    See also the ``default`` filter.
     """
     return lambda e, c, v: v is not Undefined
 
 
 def test_lower():
     """
-    {{ var is lower }}
-
-    Return True if the variable is lowercase.
+    Return true if the variable is lowercase.
     """
     return lambda e, c, v: isinstance(v, basestring) and v.islower()
 
 
 def test_upper():
     """
-    {{ var is upper }}
-
-    Return True if the variable is uppercase.
+    Return true if the variable is uppercase.
     """
     return lambda e, c, v: isinstance(v, basestring) and v.isupper()
 
 
 def test_numeric():
     """
-    {{ var is numeric }}
-
-    Return True if the variable is numeric.
+    Return true if the variable is numeric.
     """
     return lambda e, c, v: isinstance(v, (int, long, float)) or (
                            isinstance(v, basestring) and
@@ -75,9 +73,8 @@ def test_numeric():
 
 def test_sequence():
     """
-    {{ var is sequence }}
-
-    Return True if the variable is a sequence.
+    Return true if the variable is a sequence. Sequences are variables
+    that are iterable.
     """
     def wrapped(environment, context, value):
         try:
@@ -91,12 +88,18 @@ def test_sequence():
 
 def test_matching(regex):
     """
-    {{ var is matching('\d+$') }}
-
     Test if the variable matches the regular expression
     given. If the regular expression is a string additional
     slashes are automatically added, if it's a compiled regex
-    it's used without any modifications.
+    it's used without any modifications:
+
+    .. sourcecode:: jinja
+
+        {% if var is matching('\d+$') %}
+            var looks like a number
+        {% else %}
+            var doesn't really look like a number
+        {% endif %}
     """
     if isinstance(regex, unicode):
         regex = re.compile(regex.encode('unicode-escape'), re.U)
index 31c4be7308cee30dd5c4bbfae4a4d688963df6e5..b410b59b83ae5798cc0564bf83e943ef2c68b2a0 100644 (file)
@@ -674,7 +674,7 @@ class PythonTranslator(Translator):
         handle foo or bar.
         """
         return ' or '.join([
-            self.handle_node(n) for n in self.nodse
+            self.handle_node(n) for n in node.nodes
         ])
 
     def handle_not(self, node):
index de3f57047745703c4a4d6e8ae70fd5afb11e4be4..1b0df50d0cd202e43403f5871a7d18ff52eb6c5e 100644 (file)
@@ -9,7 +9,7 @@
     :license: BSD, see LICENSE for more details.
 """
 import re
-from jinja.datastructure import safe_types
+from jinja.datastructure import safe_types, Markup
 
 
 _escape_pairs = {
@@ -30,5 +30,5 @@ def escape(x, attribute=False):
     """
     if type(x) in safe_types:
         return x
-    return _escape_res[not attribute].sub(lambda m: _escape_pairs[m.group()],
-                                          unicode(x))
+    return Markup(_escape_res[not attribute].sub(lambda m:
+                  _escape_pairs[m.group()], unicode(x)))