From 083ae16362d96aa9469394cbe74f06379371960f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 27 Apr 2008 21:42:57 +0200 Subject: [PATCH] bootstrapped new documentation --HG-- branch : trunk --- docs/Makefile | 70 +++++ docs/conf.py | 141 ++++++++++ docs/generate.py | 325 ---------------------- docs/html/jinjabanner.png | Bin 10539 -> 0 bytes docs/html/jinjalogo.png | Bin 21383 -> 0 bytes docs/html/style.css | 196 ------------- docs/html/watermark.png | Bin 9521 -> 0 bytes docs/index.rst | 19 ++ docs/src/altsyntax.txt | 109 -------- docs/src/api.txt | 315 --------------------- docs/src/builtins.txt | 228 --------------- docs/src/changelog.txt | 1 - docs/src/debugging.txt | 32 --- docs/src/designerdoc.txt | 565 -------------------------------------- docs/src/devintro.txt | 190 ------------- docs/src/devrecipies.txt | 139 ---------- docs/src/escaping.txt | 71 ----- docs/src/filters.txt | 73 ----- docs/src/frameworks.txt | 247 ----------------- docs/src/fromdjango.txt | 108 -------- docs/src/i18n.txt | 113 -------- docs/src/index.txt | 74 ----- docs/src/inheritance.txt | 186 ------------- docs/src/installation.txt | 89 ------ docs/src/loaders.txt | 245 ----------------- docs/src/objects.txt | 154 ----------- docs/src/recipies.txt | 184 ------------- docs/src/scopes.txt | 186 ------------- docs/src/streaming.txt | 74 ----- docs/src/templatei18n.txt | 83 ------ docs/src/tests.txt | 30 -- docs/src/translators.txt | 10 - 32 files changed, 230 insertions(+), 4027 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/conf.py delete mode 100755 docs/generate.py delete mode 100644 docs/html/jinjabanner.png delete mode 100644 docs/html/jinjalogo.png delete mode 100644 docs/html/style.css delete mode 100644 docs/html/watermark.png create mode 100644 docs/index.rst delete mode 100644 docs/src/altsyntax.txt delete mode 100644 docs/src/api.txt delete mode 100644 docs/src/builtins.txt delete mode 100644 docs/src/changelog.txt delete mode 100644 docs/src/debugging.txt delete mode 100644 docs/src/designerdoc.txt delete mode 100644 docs/src/devintro.txt delete mode 100644 docs/src/devrecipies.txt delete mode 100644 docs/src/escaping.txt delete mode 100644 docs/src/filters.txt delete mode 100644 docs/src/frameworks.txt delete mode 100644 docs/src/fromdjango.txt delete mode 100644 docs/src/i18n.txt delete mode 100644 docs/src/index.txt delete mode 100644 docs/src/inheritance.txt delete mode 100644 docs/src/installation.txt delete mode 100644 docs/src/loaders.txt delete mode 100644 docs/src/objects.txt delete mode 100644 docs/src/recipies.txt delete mode 100644 docs/src/scopes.txt delete mode 100644 docs/src/streaming.txt delete mode 100644 docs/src/templatei18n.txt delete mode 100644 docs/src/tests.txt delete mode 100644 docs/src/translators.txt diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..e9c11d0 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,70 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html web pickle htmlhelp latex changes linkcheck + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pickle to make pickle files (usable by e.g. sphinx-web)" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview over all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + +clean: + -rm -rf _build/* + +html: + mkdir -p _build/html _build/doctrees + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + @echo + @echo "Build finished. The HTML pages are in _build/html." + +pickle: + mkdir -p _build/pickle _build/doctrees + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + @echo + @echo "Build finished; now you can process the pickle files or run" + @echo " sphinx-web _build/pickle" + @echo "to start the sphinx-web server." + +web: pickle + +htmlhelp: + mkdir -p _build/htmlhelp _build/doctrees + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in _build/htmlhelp." + +latex: + mkdir -p _build/latex _build/doctrees + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo + @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + mkdir -p _build/changes _build/doctrees + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + @echo + @echo "The overview file is in _build/changes." + +linkcheck: + mkdir -p _build/linkcheck _build/doctrees + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in _build/linkcheck/output.txt." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..b81ae0a --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +# +# Jinja2 documentation build configuration file, created by +# sphinx-quickstart on Sun Apr 27 21:42:41 2008. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default value; values that are commented out +# serve to show the default value. + +import sys, os + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.append(os.path.abspath('some/directory')) + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +#extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General substitutions. +project = 'Jinja2' +copyright = '2008, Armin Ronacher' + +# The default replacements for |version| and |release|, also used in various +# other places throughout the built documents. +# +# The short X.Y version. +version = '2.0' +# The full version, including alpha/beta/rc tags. +release = '2.0' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# Options for HTML output +# ----------------------- + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. +#html_use_opensearch = False + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Jinja2doc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('index', 'Jinja2.tex', 'Jinja2 Documentation', 'Armin Ronacher', 'manual'), +] + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/docs/generate.py b/docs/generate.py deleted file mode 100755 index 8d9f3a0..0000000 --- a/docs/generate.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/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) - -def generate_list_of_baseloaders(): - from jinja import loaders as loader_module - - result = [] - loaders = [] - for item in dir(loader_module): - obj = getattr(loader_module, item) - try: - if issubclass(obj, loader_module.BaseLoader) and \ - obj.__name__ != 'BaseLoader' and \ - obj.__name__ not in loader_module.__all__: - loaders.append(obj) - except TypeError: - pass - 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) - -def generate_environment_doc(): - from jinja.environment import Environment - return '%s\n\n%s' % ( - inspect.getdoc(Environment), - inspect.getdoc(Environment.__init__) - ) - -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() -LIST_OF_BASELOADERS = generate_list_of_baseloaders() -ENVIRONMENT_DOC = generate_environment_doc() -CHANGELOG = file(os.path.join(os.path.dirname(__file__), os.pardir, 'CHANGES'))\ - .read().decode('utf-8') - -FULL_TEMPLATE = e.from_string('''\ - - - - {{ title }} — Jinja Documentation - - - - - -
- {% if file_id == 'index' %} - -

{{ title }}

- {% else %} -

Jinja

-

{{ title }}

- {% endif %} - {% if file_id != 'index' or toc %} -
-

Navigation

- - {% if toc %} -

Contents

-
    - {% for key, value in toc %} -
  • {{ value }}
  • - {% endfor %} -
- {% endif %} -
- {% endif %} -
- {{ body }} -
-
- - -\ -''') - -PREPROC_TEMPLATE = e.from_string('''\ -{{ title }} -{% for key, value in toc %}
  • {{ value }}
  • {% endfor %} -{{ body }}\ -''') - -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)\ - .replace('[[list_of_baseloaders]]', LIST_OF_BASELOADERS)\ - .replace('[[environment_doc]]', ENVIRONMENT_DOC)\ - .replace('[[changelog]]', CHANGELOG) - parts = publish_parts( - data, - writer=writer, - settings_overrides={ - 'initial_header_level': 2, - '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, preproc): - now = datetime.now() - title = os.path.basename(filename)[:-4] - content = fp.read().decode('utf-8') - suffix = not preproc and '.html' or '' - parts = generate_documentation(content, (lambda x: './%s%s' % (x, suffix))) - 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 - if preproc: - tmpl = PREPROC_TEMPLATE - else: - tmpl = FULL_TEMPLATE - result.write(tmpl.render(c).encode('utf-8')) - result.close() - - -def run(dst, preproc, sources=(), handle_file=handle_file): - 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, preproc) - finally: - f.close() - - -def main(dst='html/', preproc=False, *sources): - run(os.path.realpath(dst), str(preproc).lower() == 'true', sources) - - -if __name__ == '__main__': - main(*sys.argv[1:]) diff --git a/docs/html/jinjabanner.png b/docs/html/jinjabanner.png deleted file mode 100644 index c672118065d1caa3bc52d440aecc99441a252fa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10539 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 diff --git a/docs/html/jinjalogo.png b/docs/html/jinjalogo.png deleted file mode 100644 index 17d6dc31eab31a0faacc8cba623fbcce0b726519..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21383 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 F;eR, 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/html/watermark.png b/docs/html/watermark.png deleted file mode 100644 index 297d8990c5a23df6f98f811e183171399904aec2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9521 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 BPksOZ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..34458f2 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,19 @@ +.. Jinja2 documentation master file, created by sphinx-quickstart on Sun Apr 27 21:42:41 2008. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Jinja2's documentation! +================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/src/altsyntax.txt b/docs/src/altsyntax.txt deleted file mode 100644 index 0f7242f..0000000 --- a/docs/src/altsyntax.txt +++ /dev/null @@ -1,109 +0,0 @@ -================== -Alternative Syntax -================== - -Jinja allows some syntax customization for the block delimiters. Depending on -the Jinja release more or less combinations are possible. The idea of an -customizable syntax is that you can update existing templates from other -template engines and programming languages easier and that you can replace -the django like default delimiters which are not everybody's favorite. - - -Configuring The Environment -=========================== - -For syntax configuration there are six arguments in the environment which -are the first six for convenience. Thus those two snippets do the same: - -.. sourcecode:: python - - env = Environment( - block_start_string='{%', - block_end_string='%}', - variable_start_string='{{', - variable_end_string='}}', - comment_start_string='{#', - comment_end_string='#}', - ) - - env = Environment('{%', '%}', '{{', '}}', '{#', '#]') - - -Ruby Like Syntax ----------------- - -Here an example configuration for Ruby-like syntax: - -.. sourcecode:: python - - env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>') - -An example template then looks like this: - -.. sourcecode:: rhtml - - <%# example eruby like configuration for Jinja %> -
      - <% for item in seq %> -
    • <%= item %>
    • - <% end‭for %> -
    - - -SGML Comment Syntax -------------------- - -Here an example configuration that uses SGML comments to hide the -processing instructions. This can be useful if you work with an WYSIWYG -designer: - -.. sourcecode:: python - - env = Environment('', '${', '}', '') - -.. sourcecode:: html - - -
      - -
    • ${item}
    • - -
    - - -Parenthesis Balancing ---------------------- - -Starting with Jinja 1.1 it's possible to use the block delimiter as a token -in the expression. That means that you can use small delimiters like single -braces or parenthesis. So if you want to have your variables to look like -``${expr}`` and you still want to use dicts in such expressions you need -Jinja 1.1 or higher. - - -Block / Variable Tag Unification -================================ - -If variable end and start tags are `None` or look the same as block tags and -you're running Jinja 1.1 or later the parser will switch into the -`no_comment_block` mode. In that mode it will try to match something as block -first and continue treating it as variable block if there is no known -directive for that name. Example: - -.. sourcecode:: python - - env = Environment('{', '}', None, None, '{*', '*}') - -This now allows smarty like templates: - -.. sourcecode:: smarty - - {* example smarty-like configuration for Jinja *} - {if something == 42} - Something is the answer to all questions and stuff like that. - {else} - Something is {something}. - {endif} - -This feature however can cause strange looking templates because there is no -visible difference between blocks and variables. diff --git a/docs/src/api.txt b/docs/src/api.txt deleted file mode 100644 index 5086953..0000000 --- a/docs/src/api.txt +++ /dev/null @@ -1,315 +0,0 @@ -======================= -Context and Environment -======================= - -The two central objects in Jinja are the `Environment` and `Context`. Both -are designed to be subclassed by applications if they need to extend Jinja. - -Environment -=========== - -The initialization parameters are already covered in the `Quickstart`_ thus -not repeated here. - -But beside those configurable instance variables there are some functions used -in the template evaluation code you may want to override: - -**def** `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. - -**def** `lex` *(source, filename)*: - - Tokenize the given sourcecode and return a generator of tuples in the - form ``(lineno, token, value)``. The filename is just used in the - exceptions raised. - - **New in Jinja 1.1** - -**def** `from_string` *(source)*: - - Load and parse a template source and translate it into eval-able Python - code. This code is wrapped within a `Template` class that allows you to - render it. - -**def** `get_template` *(name)*: - - Load a template from a loader. If the template does not exist, you will - get a `jinja.exceptions.TemplateNotFound` exception. - -**def** `to_unicode` *(self, value)*: - - Called to convert variables to unicode. Per default this checks if the - value is already unicode. If not it's converted to unicode using the - charset defined on the environment. - - Also `None` is converted into an empty string per default. - -**def** `get_translator` *(self, context)*: - - Return the translator used for i18n. A translator is an object that - provides the two functions ``gettext(string)`` and - ``ngettext(singular, plural, n)``. Both of those functions have to - behave like the `ugettext` and `nugettext` functions described in the - python `gettext documentation`_. - - If you don't provide a translator a default one is used to switch - between singular and plural forms. - - Have a look at the `i18n`_ section for more information. - -**def** `get_translations` *(self, name)*: - - Get the translations for the template `name`. Only works if a loader - is present. See the `i18n`_ section for more details. - -**def** `get_translations_for_string` *(self, string)*: - - Get the translations for the string `string`. This works also if no - loader is present and can be used to lookup translation strings from - templates that are loaded from dynamic resources like databases. - -**def** `apply_filters` *(self, value, context, filters)*: - - Now this function is a bit tricky and you usually don't have to override - it. It's used to apply filters on a value. The Jinja expression - ``{{ foo|escape|replace('a', 'b') }}`` calls the function with the - value of `foo` as first parameter, the current context as second and - a list of filters as third. The list looks like this: - - .. sourcecode:: python - - [('escape', ()), ('replace', (u'a', u'b'))] - - As you can see the filter `escape` is called without arguments whereas - `replace` is called with the two literal strings ``a`` and ``b``, both - unicode. The filters for the names are stored on ``self.filters`` in a - dict. Missing filters should raise a `FilterNotFound` exception. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -**def** `perform_test` *(self, context, testname, args, value, invert)*: - - Like `apply_filters` you usually don't override this one. It's the - callback function for tests (``foo is bar`` / ``foo is not bar``). - - The first parameter is the current contex, the second the name of - the test to perform. the third a tuple of arguments, the fourth is - the value to test. The last one is `True` if the test was performed - with the `is not` operator, `False` if with the `is` operator. - - Missing tests should raise a `TestNotFound` exception. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -**def** `get_attribute` *(self, obj, attribute)*: - - Get `attribute` from the object provided. The default implementation - performs security tests. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -**def** `get_attributes` *(self, obj, attributes)*: - - Get some attributes from the object. If `attributes` is an empty - sequence the object itself is returned unchanged. - -**def** `call_function` *(self, f, context, args, kwargs, dyn_args, dyn_kwargs)*: - - Call a function `f` with the arguments `args`, `kwargs`, `dyn_args` and - `dyn_kwargs` where `args` is a tuple and `kwargs` a dict. If `dyn_args` - is not `None` you have to add it to the arguments, if `dyn_kwargs` is - not `None` you have to update the `kwargs` with it. - - The default implementation performs some security checks. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -**def** `call_function_simple` *(self, f, context)*: - - Like `call_function` but without arguments. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -**def** `finish_var` *(self, value, ctx)*: - - Postprocess a variable before it's sent to the template. - - **Warning** this is a Jinja internal method. The actual implementation - and function signature might change. - -.. admonition:: Note - - The Enviornment class is defined in `jinja.environment.Environment` - but imported into the `jinja` package because it's often used. - -Context -======= - -Jinja wraps the variables passed to the template in a special class called a -context. This context supports variables on multiple layers and lazy (deferred) -objects. Often your application has a request object, database connection -object or something similar you want to access in filters, functions etc. - -The default context object is defined in `jinja.datastructure`. If you want -to provide your own context object always subclass the default one. This -ensures that the class continues working after Jinja upgrades. - -Beacause of that you can easily subclass a context to add additional variables -or to change the way it behaves. - -**def** `pop` *(self)*: - - Pop the outermost layer and return it. - -**def** `push` *(self, data=None)*: - - Push a dict to the stack or an empty layer. - - Has to return the pushed object. - -**def** `to_dict` *(self)*: - - Flatten the context and convert it into a dict. - -**def** `__getitem__` *(self, name)*: - - Resolve an item. Per default this also resolves `Deferred` objects. - -**def** `__setitem__` *(self, name, value)*: - - Set an item in the outermost layer. - -**def** `__delitem__` *(self, name)*: - - Delete an item in the outermost layer. Do not raise exceptions if - the value does not exist. - -**def** `__contains__` *(self, name)*: - - Return `True` if `name` exists in the context. - -**attribute** `cache`: - - The cache is a dict which can be used by filters, test functions - and global objects to cache data. It's also used by the environment - to cache often used tests and filters. - -**attribute** `translate_func`: - - This property is created on first access and returns a translation - function used by the rendering process to translate strings with the - translator defined on the environment. - -.. admonition:: Note - - The context uses a stack of dicts internally to represent the - layers of variables. It contains at least 3 levels available on - the context with some attributes. Those are: - - `globals`: - - The reference to the global namespace of the environment. - It's the lowest namespace on the stack and thus immutable - - `initial`: - - The initial namespace. Contains the values passed to the - context in the render function. It also contains the resolved - deferred values for bot the `initial` and the `globals` - namespace. - - `current`: - - The reference to the current active namespace. When the - context is initialized this automatically points to an - empty namespace. - - The number of layers on the stack are theoretically unlimited. - Some elements in the template language like loops, blocks, - macros and others push and pop the layer on entering and leaving - the section. - - This is done in order to keep the namespace clean. - - Note that since Jinja 1.1 the context object is a subclass of the - `BaseContext`, a much simpler class that just implements a stack - like namespace for python. If the `_speedups` extension was - compiled for jinja the base class will be - `jinja._speedups.BaseContext` otherwise `jinja._native.BaseContext`. - - Since you cannot reproduce completely the same semantics in python - and the C API there are some things you should keep in mind: - - - The `stack` attribute of the context maps to the real layers - on the stack, thus you can modify the items but the list as - such is meant to be read only. - - - `globals`, `current` and `initial` are read only attributes that - map to layers on the stack which you can of course modify. - - -Exceptions -========== - -During parsing and evaluation Jinja raises a couple of Jinja specific -exceptions. All of those exceptions are defined in the `jinja.exceptions` -module and are subclasses of the `TemplateError` class defined there. - -Here a list of exceptions that could occur: - -`SecurityException`: - - An exception that is raised if the template tried to access something - it should not access. In the default configuration this exception - will get caught in the Jinja rendering process and silenced. - - If however the environment is configured to not silently fail it - could happen that this exception reaches the application. - -`FilterNotFound`: - - Raised if the template tried to apply a filter that does not exist. - Since this exception is a subclass of `KeyError` too you can catch - it this way too. - -`FilterArgumentError`: - - Raised if the filter received an argument that it couldn't handle. - It's a subclass of `TypeError` too so you can catch it this way too. - -`TestNotFound`: - - Raised if the template tried to perform a test that does not exist. - Since this exception is a subclass of `KeyError` too you can catch - it this way too. - -`TestArgumentError`: - - Raised if a test function received an argument that it couldn't handle. - It's a subclass of `TypeError` too so you can catch it this way too. - -`TemplateNotFound`: - - Raised if a template does not exist. Subclass of `IOError` too. - -`TemplateSyntaxError`: - - Subclass of `SyntaxError` and used to indicate an syntax error. - -`TemplateRuntimeError`: - - Generic runtime error exception which can occour at various places. - - -.. _i18n: i18n.txt -.. _translators: translators.txt -.. _Quickstart: devintro.txt -.. _gettext documentation: http://docs.python.org/lib/module-gettext.html diff --git a/docs/src/builtins.txt b/docs/src/builtins.txt deleted file mode 100644 index 0708077..0000000 --- a/docs/src/builtins.txt +++ /dev/null @@ -1,228 +0,0 @@ -=================================== -Filters, Tests and Helper Functions -=================================== - -This part of the documentation lists the filter-, test- and helper functions -you can use in templates. - - -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 is a small example: - -.. sourcecode:: jinja - - {{ variable|replace('foo', 'bar')|escape }} - -If you want, you can also put whitespace between the filters. - -This will look for a variable `variable`, pass it to the filter `replace` -with the arguments ``'foo'`` and ``'bar'``, and pass the result to the filter -`escape` that automatically XML-escapes the value. The `e` filter is an alias for -`escape`. Here is the complete list of supported filters: - -[[list_of_filters]] - -.. admonition:: note - - *Jinja 1.0 and 1.1 notice* - - The filter operator has a pretty low priority in Jinja 1.0 and 1.1. If you - want to add fitered values you have to put them into parentheses. The same - applies if you want to access attributes or return values: - - .. sourcecode:: jinja - - correct: - {{ (foo|filter) + (bar|filter) }} - wrong: - {{ foo|filter + bar|filter }} - - correct: - {{ (foo|filter).attribute }} - wrong: - {{ foo|filter.attribute }} - - This changed in Jinja 1.2, from that version one the filter operator has - the highest priority so you can do ``foo|filter + bar|filter``. - -*new in Jinja 1.1*: - -Because the application can provide additional filters you can get a documentation -of all the provided filters by calling ``debug.filters()``: - -.. sourcecode:: jinja - - {{ debug.filters() }} - -> returns a plain text representation of all the filters - - {{ debug.filters(False) }} - -> same as above but without the builtin ones. - -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 - -These tests are especially useful when used in `if` conditions. - -[[list_of_tests]] - -*new in Jinja 1.1*: - -Because the application can provide additional tests you can get a documentation -of all the provided tests by calling ``debug.tests()``: - -.. sourcecode:: jinja - - {{ debug.tests() }} - -> returns a plain text representation of all the tests - - {{ debug.tests(False) }} - -> same as above but without the builtin ones. - -*new in Jinja 1.2*: - -If a test function expects one or no argument you can leave out the parentheses. -Previously this was only possible for text functions without arguments: - -.. sourcecode:: jinja - - {{ foo is matching @/\s+/ }} - is the same as - {{ foo is matching(@/\s+/) }} - - -Global Functions -================ - -Test functions and filter functions live in their own namespace. Global -functions not. They behave like normal objects in the context. Beside the -functions added by the application or framewhere there are two functions -available per default: - -`range` - - Works like the python `range function`_ just that it doesn't support - ranges greater than ``1000000``. - -`debug` - - Function that outputs the contents of the context. - -`lipsum` - - Generate some paragraphs of random text: - - .. sourcecode:: jinja - - {{ lipsum(5) }} - five html paragraphs - - {{ lipsum(2, False) }} - two ascii only paragraphs - - Per default a paragraph is between 20 and 100 words long. You can - override this default with the third and fourth parameter: - - .. sourcecode:: jinja - - {{ lipsum(10, True, 50, 200) }} - generate 10 HTML paragraphs between 50 and 200 words. - - *new in Jinja 1.1* - -`watchchanges` - - Jinja does not provide an django like ``{% ifchanged %}`` tag. As - replacement for this tag there is a special function in the namespace - called `watchchanges`. - - You can use it like this: - - .. sourcecode:: html+jinja - - {% for changed, article in watchchanges(articles, 'date', 'day') %} - {% if changed %}

    {{ articles.date.day }}

    {% endif %} -

    {{ article.title|e }}

    -

    {{ article.body|e }}

    - {% endif %} - - For each iteration `watchchanges` will check the given attribute. If it - changed to the former iteration the first yielded item (in this example - it's called `changed`) will be `true`, else `false`. - - In this example `articles` is a list of articles for the template with - an attribute called `date.day` which represents the current day. To only - add a new day headline if the day has changed `watchchanges` is now told - to check `articles.date.days`. - - If you want to observe more than one attribute you can provide pairs: - - .. sourcecode:: html+jinja - - {% for changed, item in watchchanges(foo, ('a', 'b'), ('a', 'c')) %} - ... - {% endfor %} - - Note that if you want to watch two first level attributes you have to - either use the list syntax `[]` or add a colon: - - .. sourcecode:: html+jinja - - {% for changed, item in watchchanges(foo, ['a'], ('b',)) %} - ... - {% endfor %} - - otherwise Jinja cannot differ between a pair of parentheses to group - expressions or the sequence syntax. - - If you don't provide any arguments the value of the variable itself - is checked. - - *new in Jinja 1.1* - -`rendertemplate` - - Loads and renders a template with a copy of the current context. This works - in many situations like the ``{% include %}`` tag, just that it does not - include a template and merges it into the template structure but renders - it completely independent and returns the rendered data as string. - - *new in Jinja 1.1* - - -Global Constants -================ - -Jinja provides some special variables which cannot be overridden in templates. -Those are not affected by the variable lookup: - - `_`, `true`, `false`, `none`, `undefined` - -You cannot use those names for your own variables or macros. It's however no -problem to have blocks with that name. - -**Notice** due to a bug in Jinja 1.0 it was possible to override `_`, `true`, -`false`, `none` and `undefined` from the template by either using `{% set %}` -or in a for loop. While overriding `true`, `false` etc just caused nothing -overriding the translation marker `_` would crash the translation interface. -If you want to use `_` for unpacking in for loops (eg. skipping items) you -have to give it a proper name: - -.. sourcecode:: jinja - - {% for key, ignore in mydict|dictsort %} - {{ key|e }} - {% endfor %} - - -.. _range function: http://docs.python.org/tut/node6.html#SECTION006300000000000000000 diff --git a/docs/src/changelog.txt b/docs/src/changelog.txt deleted file mode 100644 index 557c248..0000000 --- a/docs/src/changelog.txt +++ /dev/null @@ -1 +0,0 @@ -[[changelog]] diff --git a/docs/src/debugging.txt b/docs/src/debugging.txt deleted file mode 100644 index e8f432a..0000000 --- a/docs/src/debugging.txt +++ /dev/null @@ -1,32 +0,0 @@ -=================== -Debugging Templates -=================== - -In order to keep templates debuggable you have to do some additional work on -the application side. The traceback module that comes with python currently -does not support the `__loader__` hook which is used by Jinja to provide -templates. Although the import system was implemented three Python versions -ago the default traceback system still doesn't support it. - -However most of the extended web development traceback modules have support -for `__loader__`. Either directly or via the linecache module: - -- `Werkzeug Debugging Middleware`_ -- `Django`_ -- `cgitb`_ -- `EvalException`_ - -To enable debugging you have to use one of those debugging systems or -implement your own one with support for `__loader__`. - -The python implementation of the jinja debugger can only translate the first -exception in a traceback which can lead to problematic results. If you have -the extended debugger c extension compiled (default on unix systems but only -available on Windows if you have a VisualStudio 2003 installation) all frames -in the traceback will point to the correct linenumbers of the templates. - - -.. _Werkzeug Debugging Middleware: http://werkzeug.pocoo.org/ -.. _Django: http://www.djangoproject.com/ -.. _cgitb: http://docs.python.org/lib/module-cgitb.html -.. _EvalException: http://pythonpaste.org/module-paste.evalexception.html diff --git a/docs/src/designerdoc.txt b/docs/src/designerdoc.txt deleted file mode 100644 index cc0cbde..0000000 --- a/docs/src/designerdoc.txt +++ /dev/null @@ -1,565 +0,0 @@ -====================== -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 - - - - - My Webpage - - -
    - -

    My Webpage

    - {{ variable }} - - - -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 - -
    {{ debug()|e }}
    - -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 using the dot and bracket 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 }} - -*new in Jinja 1.2*: You can now use django like attributes for integer -indices. Thus ``{{ foo.0 }}`` is equivalent to ``{{ foo[0] }}``. - - -Loops -===== - -To iterate over a sequence, you can use the `for` loop. It basically looks like a -normal Python `for` loop and works pretty much the same: - -.. sourcecode:: html+jinja - -

    Members

    -
      - {% for user in users %} -
    • {{ loop.index }} / {{ loop.length }} - {{ user.username|escape }}
    • - {% else %} -
    • no users found
    • - {% endfor %} -
    - -*Important* Contrary to Python is the optional `else` block only -executed if there was no iteration 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. Let's assume you have a sitemap where each item -might have a number of child items. A template for that could look like this: - -.. sourcecode:: html+jinja - -

    Sitemap -
      - {% for item in sitemap recursive %} -
    • {{ item.title|e }} - {% if item.children %}
        {{ loop(item.children) }}
      {% endif %}
    • - {% endfor %} -
    - -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 the loop at that position with the new sequence as argument. - -Cycling -======= - -Sometimes you might want to have different text snippets 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 - -
      - {% for message in messages %} -
    • {{ message|e }}
    • - {% endfor %} -
    - -Each time Jinja encounters a `cycle` tag it will 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 - -
  • ...
  • - -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 %} - {{ amount }} - {% else %} - {{ amount }} - {% endif %} - -.. admonition:: Note - - Of course you can use `or` / `and` and parentheses to create more complex - conditions, but usually the logic is already handled in the application and - you don't have to create such complex constructs in the template code. However - in some situations it might be a good thing to have the abilities to create - them. - -Literals -======== - -For most of the builtin python types, literals exist in Jinja. The following -table shows which syntax elements are supported: - - ======================= =================================================== - ``"text" / 'text'`` work like python's unicode literals (u'text'). - ``42`` integer literls. - ``42.0`` float literals (exponents are not supported and - before and after the dot digits must be present) - ``[1, 'two', none]`` list literal - ``(), (1,), (1, 2)`` tuple literals. (tuples work like lists but consume - less memory and are not modifyable.) - ``{'foo': 'bar'}`` dictionary literal - ``@/expr/flags`` regular expression literals. ``@/expr/flags`` is - equivalent to ``re.compile('(?flags)expr')`` in - python. - ``@(1, 2, 3)`` set literal. ``@(1, 2, 3)`` in Jinja is is equal to - ``set([1, 2, 3])`` in python. - ``true / false`` corresponds to `True` and `False` in python. - ``none`` corresponds to `None` in python. - ``undefined`` special Jinja undefined singleton. - ======================= =================================================== - -.. admonition:: Common Pitfalls - - Keep in mind that Jinja literals, keywords and functions are all lowercase. - If you're used to Python you probably tried to write `True` which evaluates - to undefined because it simply does not exist. The correct name for the - true value is just `true` (lowercase). - - -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 ``3``. - ``-`` subtract the right operand from the left one. - ``{{ 1 - 1 }}`` would return ``0``. - ``/`` divide the left operand by the right one. - ``{{ 1 / 2 }}`` would return ``0.5``. - ``//`` divide the left operand by the right one and return a truncated - integer result: ``{{ 20 // 7 }}`` is ``2``. - *added in Jinja 1.1* - ``~`` string concatenate a value with another one. ``{{ foo ~ bar }}`` - is equivalent to ``{{ foo|string + bar|string }}``. *added in - Jinja 1.1* - ``*`` multiply the left operand with the right one. - ``{{ 2 * 2 }}`` would return ``4``. - ``**`` raise the left operand to the power of the right - operand. ``{{ 2**3 }}`` would return ``8``. - ``%`` calculate the remainder of an integer division between the - left and right operand: ``{{ 11 % 7 }}`` is ``4``. - ``in`` perform sequence membership test. ``{{ 1 in [1,2,3] }}`` would - return true. - ``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. - ``not`` negate a statement (see below) - ``()`` call a callable: ``{{ user.get_username() }}``. Inside of the - parentheses you can use variables: ``{{ user.get(username) }}``. - ======= =================================================================== - -Note that there is no support for any bit operations or something similar. - -* special note regarding `not`: The `is` and `in` operators support negation - using an infix notation too: ``foo is not bar`` and ``foo not in bar`` - instead of ``not foo is bar`` and ``not foo in bar``. All other expressions - require a prefix notation: ``not (foo and bar)``. - - -With Jinja 1.2 onwards it's possible to replace basic if/else blocks with the -inline `if` / `else` expression. The following two examples evaluate to the -same: - -.. sourcecode:: jinja - - {{ "something" if expr else "otherthing" }} - - {% if expr %}something{% else %}otherthing{% endif %} - - -Boolean Values -============== - -In If-Conditions Jinja performs a boolean check. All empty values (eg: empty -lists ``[]``, empty dicts ``{}`` etc) evaluate to `false`. Numbers that are -equal to `0`/`0.00` are considered `false` too. The boolean value of other -objects depends on the behavior the application developer gave it. Usually -items are `true`. - -Here some examples that should explain it: - -.. sourcecode:: jinja - - {% if [] %} - will always be false because it's an empty list - - {% if {} %} - false too. - - {% if ['foo'] %} - this is true. Because the list is not empty. - - {% if "foobar" %} - this is also true because the string is not empty. - -Slicing -======= - -Some objects support slicing operations. For example lists: - -.. sourcecode:: jinja - - {% for item in items[:5] %} - This will only iterate over the first 5 items of the list - - {% for item in items[5:10] %} - This will only iterate from item 5 to 10. - - {% for item in items[:10:2] %} - This will only yield items from start to ten and only returing - even items. - -For more informations about slicing have a look at the `slicing chapter`_ -in the "Dive into Python" e-book. - -Macros -====== - -If you want to use a partial template in more than one place, you might want to -create a macro from it: - -.. sourcecode:: html+jinja - - {% macro show_user user %} -

    {{ user.name|e }}

    -
    - {{ user.description }} -
    - {% 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 than one value: - -.. sourcecode:: html+jinja - - {% macro show_dialog title, text %} -
    -

    {{ title|e }}

    -
    {{ text|e }}
    -
    - {% endmacro %} - - {{ show_dialog('Warning', 'something went wrong i guess') }} - -*Improvements in Jinja 1.1*: - - Starting with Jinja 1.1 it's possible to use optional parentheses - around the macro arguments: - - .. sourcecode:: html+jinja - - {% macro foo(a, b) %} - ... - {% endmacro %} - - Additionally extra arguments passed to the macro end up in the - special variable `varargs`. So you can have a macro like this: - - .. sourcecode:: html+jinja - - {% macro make_list() %} - {% if varargs %} -
      - {% for item in varargs %} -
    • {{ item|e }}
    • - {% endfor %} -
    - {% endif %} - {% endmacro %} - - {{ make_list("John", "Jane", "Marcus", "Heinrich") }} - - If a macro parameter is called `varargs` the additional extra - arguments are not accessible. - -For information regarding the visibility of macros have a look at the -`Scopes and Variable Behavior`_ section. - - -Extended Macro Call -=================== - -*new in Jinja 1.1* - -Jinja 1.1 adds a new special tag that you can use to pass some evaluable -template code to a macro. Here an example macro that uses the features of -the ``{% call %}`` tag: - -.. sourcecode:: html+jinja - - {% macro dialog title %} -
    -

    {{ title }}

    -
    - {{ caller() }} -
    -
    - {% endmacro %} - -Called the normal way `caller` will be undefined, but if you call it -using the new `{% call %}` tag you can pass it some data: - -.. sourcecode:: html+jinja - - {% call dialog('Hello World') %} - This is an example dialog - {% endcall %} - -Now the data wrapped will be inserted where you put the `caller` call. - -If you pass `caller()` some keyword arguments those are added to the -namespace of the wrapped template data: - -.. sourcecode:: html+jinja - - {% macro makelist items %} -
      - {%- for item in items %} -
    • {{ caller(item=item) }}
    • - {%- endfor %} -
    - {%- endmacro %} - - {% call makelist([1, 2, 3, 4, 5, 6]) -%} - [[{{ item }}]] - {%- endcall %} - -This will then produce this output: - -.. sourcecode:: html - -
      -
    • [[1]]
    • -
    • [[2]]
    • -
    • [[3]]
    • -
    • [[4]]
    • -
    • [[5]]
    • -
    • [[6]]
    • -
    - - -Template Inclusion -================== - -You can load another template at a given position 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. - -Please keep in mind that include does not render the template indenpendently -but includes the processing instructions into the current template. Thus if the -included template contains a ``{% extends %}`` tag it also affects the current -template. - -This is intended because it makes it possible to include macros from other -templates. - -*new in Jinja 1.1* you can now render an included template to a string that is -evaluated in an indepdendent environment by calling `rendertemplate`. See the -documentation for this function in the `builtins`_ documentation. - - -Filtering Blocks -================ - -Sometimes it could be a good idea to filter a complete block of text. For -example, if you want to escape some html code: - -.. sourcecode:: jinja - - {% filter escape %} - - goes here - - {% endfilter %} - -Of course you can chain filters too: - -.. sourcecode:: jinja - - {% filter lower|escape %} - SOME TEXT - {% endfilter %} - -returns ``"<b>some text</b>"``. - - -Defining Variables -================== - -You can also define variables in the namespace using the ``{% set %}`` tag: - -.. sourcecode:: jinja - - {% set foo = 'foobar' %} - {{ foo }} - -This should ouput ``foobar``. - -For information regarding the visibility of variables have a look at the -`Scopes and Variable Behavior`_ section. - - -Reserved Keywords -================= - -Jinja has some keywords you cannot use a variable names. This limitation -exists to make templates look coherent. Syntax highlighters won't mess things -up and you won't have the situation that some names work depending on the -context. - -The following keywords exist and cannot be used as identifiers: - - `and`, `block`, `cycle`, `elif`, `else`, `endblock`, `endfilter`, - `endfor`, `endif`, `endmacro`, `endraw`, `endtrans`, `extends`, `filter`, - `for`, `if`, `in`, `include`, `is`, `macro`, `not`, `or`, `pluralize`, - `print`, `raw`, `recursive`, `set`, `trans`, `call`, `endcall` - -If you want to use such a name you have to prefix or suffix it or use -alternative names: - -.. sourcecode:: jinja - - {% for macro_ in macros %} - {{ macro_('foo') }} - {% endfor %} - - -.. _slicing chapter: http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice -.. _Scopes and Variable Behavior: scopes.txt -.. _builtins: builtins.txt diff --git a/docs/src/devintro.txt b/docs/src/devintro.txt deleted file mode 100644 index 0b61924..0000000 --- a/docs/src/devintro.txt +++ /dev/null @@ -1,190 +0,0 @@ -==================== -Developer Quickstart -==================== - -This part of the documentation shows you how to embed 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. - -Basically the important method on a template is the `render` method. It -takes either a dict or keyword arguments. All keyword arguments appear -in the template as variables. - -So these two snippets do the same: - -.. sourcecode:: python - - tmpl.render( - knights='we say nih', - spam='and eggs' - ) - -.. sourcecode:: python - - tmpl.render({ - 'knights': 'we say nih', - 'spam': 'and eggs' - }) - -The Environment -=============== - -[[environment_doc]] - -The environment provides the following useful functions and properties in -addition to the initialization 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. -``lex(source, filename)`` Tokenize the given sourcecode and return a - generator of tuples in the form - ``(lineno, token, value)``. The filename is just - used in the exceptions raised. - **New in Jinja 1.1** -``from_string(source)`` Load and parse a template source and translate it - into eval-able Python code. This code is wrapped - within 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. - -Undefined Values -================ - -If a template designer tries to access a not defined value the return value -will be the `undefined_singleton` specified in the environment. The default -one is the `SilentUndefined` which fails in no case. Additionally there is -a special undefined type called the `ComplainingUndefined` which is located -in the `jinja.datastructure` module. It will raise exceptions when compared -with other types, or when rendered. - -Theoretically you can provide your own singleton by subclassing -`AbstractUndefindedType` and creating an instance of it using `make_undefined` -(both located in `jinja.datastructure`) but those two types should cover -the basic use cases. The `Undefined` object in that module exists for -backwards compatibility and is an alias for `SilentUndefined`. - -To create your own undefined singleton do something like this: - -.. sourcecode:: jinja - - from jinja.datastructure import AbstractUndefinedType, make_undefined - - class MyUndefinedType(AbstractUndefindedType): - __slots__ = () - - def __iter__(self): - return iter(int, 0) - - def __reduce__(self): - return 'MyUndefined' - - MyUndefined = make_undefined(MyUndefinedType) - -The only thing you have to do is to override `__reduce__` so that it returns -the name of the singleton instance and create the instance using -`make_undefined`. Everything else is up to you. Note that currently attributes -on undefined objects are available in the Jinja layer too which however -will change in one of the next Jinja versions. So if you put a `foo` attribute -on your undefined singleton you will be able to do ``{{ undefined.foo }}`` -by now but certainly not in the future. - -This limitation currently exists because undefined is treated as normal object -and thus affected by normal getattr calls. - - -Automatic Escaping -================== - -Jinja provides a way for automatic escaping, but we do not recommend using it. -Because Jinja was designed as multi purpose template engine there are some -issues with automatic escaping. For example filters don't deal with markup -data. Also you can easily bypass the automatic escaping so it's not something -you can expect to "just work". Also there is a huge overhead when escaping -everything. - -The best idea is to think about which data already contains html, which will -probably contain (eg: every user input, etc) etc. And live with self escaping. - -That's usually a much better idea. - - -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 analogous 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 -.. _context documentation: contextenv.txt -.. _loader: loaders.txt -.. _translators: translators.txt -.. _filter development: filters.txt -.. _test development: tests.txt diff --git a/docs/src/devrecipies.txt b/docs/src/devrecipies.txt deleted file mode 100644 index dfa2043..0000000 --- a/docs/src/devrecipies.txt +++ /dev/null @@ -1,139 +0,0 @@ -======================= -Recipies For Developers -======================= - -Here some recipies for application developers. - - -Automagic Template Variables -============================ - -Python allows some magic stack manipulation which can be used to -pass variables to templates automatically. It's not a recommended -way to pass variables to templates but it can be useful for some -small generation scripts etc. - -Just subclass the environment and add an `autorender` function like -this: - -.. sourcecode:: python - - import sys - from jinja import Environment - - class AutoEnvironment(Environment): - - def autorender(self, template): - tmpl = self.get_template(template) - return tmpl.render(sys._getframe(1).f_locals) - -You can use it now like this: - -.. sourcecode:: python - - def foo(): - seq = range(10) - foo = "blub" - return env.autorender('foo.html') - -In the template you can now access the local variables `seq` and `foo`. - - -Using Django Filters with Jinja -=============================== - -If you use Jinja in django and want to use some of the filters that -are part of the django core you can use this snippet: - -.. sourcecode:: python - - def convert_django_filter(f): - def filter_factory(*args): - def wrapped(env, ctx, value): - return f(value, *args) - return wrapped - return filter_factory - -You can now convert django filters for jinja using `convert_filter`. *Note*: -Django only supports one filter argument. Because of this limitation you -shouldn't pass it more arguments than it accepts. Because django uses some -introspection to find out if a filter accepts an argument weird things can -happen if you call it with an incompatible argument count. - -You can now register django filters for a jinja environment: - -.. sourcecode:: python - - from django.template.defaultfilters import date - env.filters['date'] = convert_django_filter(date) - -And use it: - -.. sourcecode:: jinja - - {{ entry.pub_date|date }} - -Also keep in mind that Jinja knows about keywords, thus you cannot have a filter -that is called `pluralize` for example. - -Such a conversion function is also part of the djangosupport module which was -introduced in Jinja 1.2. It's explained on the `Framework Integration`_ page. - - -Using Jinja in Django -===================== - -.. admonition:: Note - - This section is more or less obsolete as of Jinja 1.2 which provides a - djangosupport module, covered on the `Framework Integration`_ page. - -This snippet was contributed by Bryan McLemore. It provides a `render_to_response` -function similar to the one shipped with django just that it uses Jinja for -rendering. It applies the context processors on the context and consumes a -`RequestContext`: - -.. sourcecode:: python - - from django.template.context import get_standard_processors - from django.http import HttpResponse - from jinja import Environment, FileSystemLoader, ChoiceLoader - from django.conf import settings - - loaders = [] - for location in settings.TEMPLATE_DIRS: - loaders.append(FileSystemLoader(location)) - env = Environment(loader=ChoiceLoader(loaders)) - - def render_to_response(template, context, request=None): - template = env.get_template(template) - if request: - for processor in get_standard_processors(): - context.update(processor(request)) - return HttpResponse(template.render(context)) - - -If you want to plug Jinja into the Django i18n system you can use this -environment class: - -.. sourcecode:: python - - from jinja import Environment - from django.utils.translation import gettext, ngettext - - class DjangoTranslator(object): - - def __init__(self): - self.gettext = gettext - self.ngettext = ngettext - - class DjangoEnvironment(Environment): - - def get_translator(self, context): - return DjangoTranslator() - -Because Django uses gettext internally we can create just assign the -ngettext and gettext functions directly to the translator class. - - -.. _Framework Integration: frameworks.txt diff --git a/docs/src/escaping.txt b/docs/src/escaping.txt deleted file mode 100644 index 73c0d93..0000000 --- a/docs/src/escaping.txt +++ /dev/null @@ -1,71 +0,0 @@ -======================= -Whitespace and Escaping -======================= - -This section of the documentation explains how to remove unused whitespace -from the templates and embed raw Jinja syntax. - -Escaping -======== - -Sometimes you might want to add Jinja syntax elements into the template -without executing them. In that case you have quite a few possibilities. - -For small parts this might be a good way: - -.. sourcecode:: jinja - - {{ "{{ foo }} is variable syntax and {% foo %} is block syntax" }} - -When you have multiple elements you can use the ``raw`` block: - -.. sourcecode:: jinja - - {% raw %} - Filtering blocks works like this in Jinja: - {% filter escape %} - - goes here - - {% endfilter %} - {% endraw %} - - -Whitespace -========== - -If the Jinja environment was created with ``trim_blocks`` set to true the -first newline after a template tag is removed automatically (php like -processing). - -Starting with `Jinja 1.1` you can also handle some whitespace rules in the -template. If you put an minus sign (`-`) to the start or end of an block -(for example a `for` tag), a comment or variable element you can remove -the whitespaces after or before that block: - -.. sourcecode:: jinja - - {% for item in seq -%} - {{ item }} - {%- endfor %} - -This will yield all elements without whitespace between them because they -are automatically removed. This of course works for other tags too: - -.. sourcecode:: jinja - - {% for user in userlist -%} - {{ user|e -}} - {% if not loop.last %},{% endif %} - {%- endfor %} - -Note that you must not use a whitespace between the tag and the minus sign: - -.. sourcecode:: jinja - - valid: - {%- if foo -%}...{% endif %} - - invalid: - - {% - if foo - %}...{% endif %} diff --git a/docs/src/filters.txt b/docs/src/filters.txt deleted file mode 100644 index df23205..0000000 --- a/docs/src/filters.txt +++ /dev/null @@ -1,73 +0,0 @@ -================ -Filter Functions -================ - -Filters are a powerful feature of Jinja taken from django which probably took -it from UNIX. The idea is that you "pipe" a value through some filters to -do something with it. For example convert it to upper case, escape it or -replace a substring. - -Jinja comes with some builtin filters explained in the `designer documentation`_. - -Writing Filters -=============== - -A filter basically is a factory function, a function that returns another -function. We do this because filters can get an unlimited amount of positional -arguments and aditionally should gain access to the environment, context and -piped value. A simple filter looks like this: - -.. sourcecode:: python - - def do_join(d=u''): - def wrapped(env, context, value): - tmp = [] - for item in value: - tmp.append(env.to_unicode(item)) - return d.join(tmp) - return wrapped - -Now you have to register that filter on an environment: - -.. sourcecode:: python - - env.filters['join'] = do_join - -In fact this filter is already bundled so you won't see any effect. But it -should explain how such a filter looks like. The template designer can just -trigger the outer code (i.e. call `join` with or without arguments). The -returned function is then processed by the jinja template engine once all -filters are created. - -If you want to create filters that just operate on a string (in fact unicode -objects) you can use the `stringfilter` decorator: - -.. sourcecode:: python - - from jinja.filters import stringfilter - - @stringfilter - def do_strip(value): - return value.strip() - -The wrapped function is created internally by the decorator, any positional -arguments are forwarded to the filter function. The first argument is always -the value already converted into a string. - -If you're using Jinja with django and want to use the django filters in Jinja -have a look at the `developer recipies`_ page. - -*new in Jinja 1.1* additionally to the `stringfilter` decorator there is now -a similar decorator that works exactly the same but does not convert values -to unicode: - -.. sourcecode:: python - - from jinja.filters import simplefilter - - @simplefilter - def do_add(value, to_add): - return value + to_add - -.. _designer documentation: builtins.txt -.. _developer recipies: devrecipies.txt diff --git a/docs/src/frameworks.txt b/docs/src/frameworks.txt deleted file mode 100644 index 6511cf7..0000000 --- a/docs/src/frameworks.txt +++ /dev/null @@ -1,247 +0,0 @@ -===================== -Framework Integration -===================== - -Starting with Jinja 1.1 it's possible to embed Jinja into some of the existing -frameworks a lot easier. When speaking of frameworks we only refer to `Pylons`_ -which has a working implementation of the TurboGears template specification. - -Since the whole situation is problematic because of various reasons (kid -specific, uses dotted names for template loading, package name prefix etc.) -we worked around some of the problems by using pylons specific workarounds. - -Jinja also ships an implementation for a hypothetical template abstraction layer -called `General Template Interface`_ which isn't implemented by any existing -framework so far. This specification however tries to solve the problems that -exist in Buffet. - - -Buffet -====== - -The buffet specification proposes that templates are named in dotted names. That -means `foo.bar` and not `foo/bar.html`. The dotted notation has the disadvantage -that you cannot specify the filename extension. In recent pylons versions it's -however possible to load templates with their native path too if you prefix the -template name with a foreslash (`/foo/bar.html`). If you don't specify the -extension it will assume `.html` for the dotted notation. - -Here the list of configuration values: - -======================= ====================================================== -``jinja.extension`` The template extension when templates are loaded using - the dotted notation. Defaults to ``html``. -``jinja.environment`` If this is provided it must be the only configuration - value and it's used as jinja environment. In that - case all other configuration parameters except of - ``jinja.extension`` are ignored. -``jinja.searchpath`` If provided a new file system loader with this - search path is instanciated. -``jinja.package`` Name of the python package containing the - templates. If this and ``package_path`` is - defined a `PackageLoader` is used. -``jinja.package_path`` Path to the templates inside of a package. -``jinja.loader_func`` Function that takes the name of the template to - load. If it returns a string or unicode object - it's used to load a template. If the return - value is None it's considered missing. -``jinja.getmtime_func`` Function used to check if templates requires - reloading. Has to return the UNIX timestamp of - the last template change or 0 if this template - does not exist or requires updates at any cost. -``jinja.use_memcache`` 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. -``jinja.memcache_size`` Number of template instance you want to cache. - Defaults to ``40``. -``jinja.cache_folder`` Set this to an existing directory to enable - caching of templates on the file system. Note - that this only affects templates transformed - into python code. Default is ``None`` which means - that caching is disabled. -``jinja.auto_reload`` Set this to `False` for a slightly better - performance. In that case of `getmtime_func` - not being provided this won't have an effect. -======================= ====================================================== - -All other options that start with `jinja.` are automatically forwarded to the -environment constructor. - -In pylons for example you can use jinja as buffet plugin like this: - -Edit the `yourproject/config/middleware.py` and add this to `config.init_app`: - -.. sourcecode:: python - - config.add_template_engine('jinja', '', { - 'jinja.package': 'yourapplication', - 'jinja.package_path': 'res/templates', - 'jinja.use_memcache': True - }) - -Note that it's a good idea to set the second parameter to an empty string. -It's meant to be used as replacement for the turbogears package name but -Jinja assumes that the name of the template does not include the package -path. - -.. admonition:: Note - - Special note for pylons users. Jinja will be unable to request attributes - from the special `c` object, unless you set `strict_c` to `True` in the - `environment.py`: - - .. sourcecode:: python - - return pylons.config.Config(tmpl_options, map, paths, strict_c=True) - -You can then render the template in the view like this: - -.. sourcecode:: python - - class ExampleController(BaseController): - - def index(self): - c.title = "Your Page" - c.message = 'hi' - return render_response('jinja', 'test_template') - - def download(self): - c.title = "Downloads" - c.downloads = [1, 2, 3] - return render_response('jinja', '/downloads.html') - -With the settings from above rendering the `index` action will result in -rendering the template ``res/templates/test_template.html`` where res is -a folder in the ``yourapplication`` python package. - -The `downloads` action uses the pylons specific leading foreslash notation. - - -General Template Interface -========================== - -Because nobody implemented this specification so far it's not documented here -but in the sourcecode of the `plugin module`_. The specification itself is -explained on the pocoo trac on the `General Template Interface`_ wiki page. - - -Django -====== - -Using Jinja in django is straightforward because django has a pretty low -level response interface. Just have a look at the `developer recipies`_, -there are some examples for django. Starting with Jinja 1.2 there is also -a contrib module that simplifies using Jinja in an unicode enabled django. - -Quickstart ----------- - -To get started execute the following code at the bottom of your settings.py -or in some general application file such as urls.py or a central module. The -only thing that matters is that it's executed right *after* the settings -were set up and *before* `django.contrib.jinja` is imported:: - - from jinja.contrib import djangosupport - djangosupport.configure() - -What this does is setting up a Jinja environment for this django instance -with loaders for `TEMPLATE_DIRS` etc. It also converts a couple of default -django filters such as `date` and `timesince` which are not available in -Jinja per default. If you want to change the list you can provide others -by passing a list with filter import names as `convert_filters` keyword -argument. - -All other keyword arguments are forwarded to the environment. If you want -to provide a loader yourself pass it a loader keyword argument. - -Rendering Templates -------------------- - -To render a template you can use the functions `render_to_string` or -`render_to_response` from the `django.contrib.jinja` module:: - - from django.contrib.jinja import render_to_response - resp = render_to_response('Hello {{ username }}!', { - 'username': req.session['username'] - }, req) - -`render_to_string` and `render_to_response` take at least the name of -the template as argument, then the optional dict which will become the -context. If you also provide a request object as third argument the -context processors will be applied. - -`render_to_response` also takes a forth parameter which can be the -content type which defaults to `DEFAULT_CONTENT_TYPE`. - -Converting Filters ------------------- - -One of the useful objects provided by `django.contrib.jinja` is the -`register` object which can be used to register filters, tests and -global objects. You can also convert any filter django provides in -a Jinja filter using `convert_django_filter`:: - - from django.contrib.jinja import register, convert_django_filter - from django.template.defaultfilters import floatformat - - register.filter(convert_django_filter(floatformat), 'floatformat') - -Available methods on the `register` object: - -``object (obj[, name])`` - Register a new global as name or with the object's name. - Returns the function object unchanged so that you can use - it as decorator if no name is provided. - -``filter (func[, name])`` - Register a function as filter with the name provided or - the object's name as filtername. - Returns the function object unchanged so that you can use - it as decorator if no name is provided. - -``test (func[, name])`` - Register a function as test with the name provided or the - object's name as testname. - Returns the function object unchanged so that you can use - it as decorator if no name is provided. - -``context_inclusion (func, template[, name])`` - Register a function with a name provided or the func object's - name in the global namespace that acts as subrender function. - - func is called with the callers context as dict and the - arguments and keywords argument of the inclusion function. - The function should then process the context and return a - new context or the same context object. Afterwards the - template is rendered with this context. - - Example:: - - def add_author(context, author=None): - if author is not None: - author = Author.objects.get(name=author) - context['author'] = author - return context - - register.context_inclusion(add_author, 'author_details.html', - 'render_author_details') - - You can use it in the template like this then:: - - {{ render_author_details('John Doe') }} - -``clean_inclusion (func, template[, name[, run_processors]])`` - Works like `context_inclusion` but doesn't use the calles - context but an empty context. If `run_processors` is `True` - it will lookup the context for a `request` object and pass - it to the render function to apply context processors. - - - -.. _Pylons: http://www.pylonshq.com/ -.. _General Template Interface: http://trac.pocoo.org/wiki/GeneralTemplateInterface -.. _plugin module: http://trac.pocoo.org/browser/jinja/trunk/jinja/plugin.py -.. _developer recipies: devrecipies.txt diff --git a/docs/src/fromdjango.txt b/docs/src/fromdjango.txt deleted file mode 100644 index 23d0b78..0000000 --- a/docs/src/fromdjango.txt +++ /dev/null @@ -1,108 +0,0 @@ -=============================== -Differences To Django Templates -=============================== - -If you have previously worked with Django templates, you should find Jinja 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 implicitly. With Jinja you have to specify that you -want to call an object. 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 filters. 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 verbose but it allows different types of arguments - including -variables - and more than one of them. - -Tests -===== - -In addition to filters there also are tests you can perform using the `is` operator. -Here are 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/i18n.txt b/docs/src/i18n.txt deleted file mode 100644 index 91af491..0000000 --- a/docs/src/i18n.txt +++ /dev/null @@ -1,113 +0,0 @@ -==================== -Internationalization -==================== - -Jinja includes support for internationalized templates. Because usually the -application includes i18n/l10n code too there is no script to collect -translatable strings and no default translation interface. A simple -implementation wouldn't fit into every application so there are a few things -you have to do. - - -Writing A Translator -==================== - -The most important thing is writing a translator and subclassing the -Environment so that Jinja knows about the translator. Then you have to -think of how to resolve the current language. You probably use Jinja in a -multithreaded environment where each thread (request) might want to have -a different language. The best way is putting the current language into -the context, this can work automatically if you create a helper function -for template rendering. But that's up to you. - -However. For many web applications this might be a way: - -.. sourcecode:: python - - from jinja import Environment - from myapplication import get_translator - - class ApplicationTranslator(object): - - def __init__(self, language): - self.language = language - self.translator = get_translator(language) - - def gettext(self, string): - return self.translator.ugettext(string) - - def ngettext(self, singular, plural, n): - return self.translator.ungettext(singuarl, plural, n) - - - class ApplicationEnvironment(Environment): - - def get_translator(self, context): - return ApplicationTranslator(context['LANGUAGE']) - - - env = ApplicationEnvironment() - tmpl = env.get_template('index.html') - tmpl.render(LANGUAGE='de_DE') - -This example assumes that you use gettext and have a gettext `Translations` -object which is returned by the `get_translator` function. But you don't -have to use gettext. The only thing Jinja requires is an object with to -functions/methods on it that return and accept unicode strings: -``gettext(string)`` that takes a string, translates and returns it, a -``ngettext(singular, plural, count)`` function that returns the correct plural -form for `count` items. Because some languages have no or multiple plural -forms this is necessary. - - -Translator Factory -================== - -With Jinja 1.2 onwards it's possible to use a translator factory -instead of an enviornment subclass to create a translator for a context. -A translator factory is passed a context and has to return a translator. -Because of the way classes work you can also assign a translator class -that takes a context object as only argument as factory. - -Example: - -.. sourcecode:: python - - from jinja import Environment - from myapplication import get_translator - - def translator_factory(context): - return get_translator(context['LANGUAGE']) - - env = ApplicationEnvironment(translator_factory=translator_factory) - tmpl = env.get_template('index.html') - tmpl.render(LANGUAGE='de_DE') - -This example assumes that the translator returned by `get_translator` -already has a gettext and ngettext function that returns unicode strings. - - -Collecting Translations -======================= - -The next step is to collect the translations. Every Jinja environment -provides a function called `get_translations` which collects all -translatable strings from an template. - -Example: - -.. sourcecode:: pycon - - >>> env.get_translations('index.html') - [(1, u'foo', None), (2, u'Foo', None), (3, u'%(count)s Foo', u'%(count)s Foos')] - -The first item in the tuple is the linenumer, the second one is the -singular form and the third is the plural form if given. - -Because Jinja is not bound to gettext you can now use these strings to -create translation files for any translation system. - -*New in Jinja 1.1* You can now extract translations from strings according -to the current envrionment settings too by using the environment method -`get_translations_for_string` which takes a string containing a template -as only argument. The return value is the same as for `get_translations`. diff --git a/docs/src/index.txt b/docs/src/index.txt deleted file mode 100644 index ba1c4eb..0000000 --- a/docs/src/index.txt +++ /dev/null @@ -1,74 +0,0 @@ -====================== -Documentation Overview -====================== - -Welcome in the Jinja documentation. - -- `Installing Jinja `_ - -- **Application Developer Documentation**: - - - `Quickstart `_ - getting started with Jinja - - - `Template Loaders `_ - documentation for the different - loader types and how to write custom ones. - - - `Filter Functions `_ - information about how to write - custom filter functions. - - - `Test Functions `_ - information about how to write - custom test functions. - - - `Global Objects `_ - information about the special global - namespace in Jinja templates. - - - `Streaming Interface `_ - using Jinja for big templates - by streaming the output. - - - `Internationalization `_ - how to internationalize applications - using Jinja templates. - - - `Alternative Syntax `_ - changing the default Jinja - block / variable / comment delimiters. - - - `API Documentation `_ - API documentation for public Jinja - objects like `Environment`. - - - `Translators `_ - explanation about the Jinja template - translation interface. - - - `Framework Integration `_ - integrating Jinja into - python frameworks. - - - `Debugging Support `_ - debugging Jinja templates. - - - `Developer Recipies `_ - tips and tricks for application - developers. - -- **Template Designer Documentation**: - - - `Syntax Reference `_ - quick overview over the Jinja - syntax elements. - - - `Template Inheritance `_ - template inheritance howto. - - - `Filters, Tests and Helper Functions `_ - list of filters - and test/helper functions. - - - `Whitespace and Escaping `_ - how to escape markup and - control whitespace. - - - `Scopes and Variable Behavior `_ - behavior of undefined - variables and scoping rules. - - - `Template Internationalization `_ - how to internationalize - Jinja templates. - - - `Differences To Django `_ - coming from django? Then this - document is for you. - - - `Designer Recipies `_ - various tips and tricks for designers. - -- `Changelog `_ - -There is also support via IRC on the ``#pocoo`` channel on `irc.freenode.net`. diff --git a/docs/src/inheritance.txt b/docs/src/inheritance.txt deleted file mode 100644 index d9d0cdc..0000000 --- a/docs/src/inheritance.txt +++ /dev/null @@ -1,186 +0,0 @@ -==================== -Template 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** 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 - - - - - - {% block title %}{% endblock %} - My Webpage - {% block html_head %}{% endblock %} - - -
    - {% block content %}{% endblock %} -
    - - - - -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 %} - - {% endblock %} - - {% block content %} -

    Index

    -

    - Welcome on my awsome homepage. -

    - {% 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. It must be always the first tag -in a template but whitespace or a comment is allowed before. This was not -enforced with Jinja 1.0 and 1.1, it does however raise a syntax error with -1.2 or later. - -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 templates in subdirectories 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. - - -How Inheritance Works Internally -================================ - -Inheritance in Jinja is straightforward. If a template contains an -``{% extends %}`` tag it's considered being a child template, otherwise it's -a layout template. In a layout template you can place blocks basically -everywhere. In a child template blocks can only be located either at the -top level or inside another block. - -Data outside of a block in a child template is executed before the layout -template is rendered, thus you can use it to propagate data to the whole -inheritance chain. Having a block in an invalid position you will receive -an syntax error. Here some examples: - -**impossible**: - - .. sourcecode:: jinja - - {% extends 'master.html' %} - {% if some_condition %} - {% block body %} - ... - {% endblock %} - {% endif %} - - This can't work because template inheritance works at translation / - compilation time not at template execution. - -**possible**: - - .. sourcecode:: jinja - - {% extends 'master.html' %} - {% block body %} - {% if some_condition %} - {% block myblock %} - ... - {% endblock %} - {% endif %} - {% endblock %} - - This can work although it probably makes no sense in this specific case. - However the condition is handled at runtime because it's in a valid block - and defines a new block subtemplates can override. - - -.. admonition:: Note - - Unlike Python Jinja does not support multiple inheritance. So you can - only have one extends tag with only one constant string argument. - - -Super Blocks -============ - -Starting with `Jinja 1.1` it's possible to render the contents of the parent -block. By calling it you get the results of the parent block back. If you want -to get the data of the parent you can give it an offset: - -.. sourcecode:: jinja - - {{ super() }} - return the parent data - - {{ super(1) }} - the same as above - - {{ super(2) }} - return the data of the second parent block - - -Block Shortcuts -=============== - -With Jinja 1.1 onwards it's possible to have a shortcut syntax for blocks -with few content. The following constructs do the same: - -.. sourcecode:: jinja - - {% block title %}{{ page_title }}{% endblock %} - - {% block title page_title %} - -Note that as soon as you specify a second argument it's threated as -short block and Jinja won't look for an closing tag. diff --git a/docs/src/installation.txt b/docs/src/installation.txt deleted file mode 100644 index 8f7cbad..0000000 --- a/docs/src/installation.txt +++ /dev/null @@ -1,89 +0,0 @@ -============ -Installation -============ - -Jinja requires at least Python 2.3 to work correctly. - - -Installing a released version -============================= - -As a Python egg (via easy_install) ----------------------------------- - -You can install the most recent Jinja version using `easy_install`_:: - - sudo easy_install Jinja - -This will install a Jinja egg in your Python installation's site-packages -directory. - -From the tarball release -------------------------- - -1. Download the most recent tarball from the `download page`_ -2. Unpack the tarball -3. ``sudo python setup.py install`` - -Note that the last command will automatically download and install -`setuptools`_ if you don't already have it installed. This requires a working -internet connection. - -This will install Jinja into your Python installation's site-packages directory. - - -Installing the development version -================================== - -If you want to play around with the code ----------------------------------------- - -1. Install `mercurial`_ -2. ``svn co http://dev.pocoo.org/hg/jinja-main jinja`` -3. ``cd jinja`` -4. ``ln -s jinja /usr/lib/python2.X/site-packages`` - -As an alternative to steps 4 you can also do ``python setup.py develop`` -which will install the package via setuptools in development mode. This also -has the advantage that the c extensions are compiled. - -If you just want the latest features and use them -------------------------------------------------- - -:: - - sudo easy_install Jinja==dev - -This will install a Jinja egg containing the latest Subversion trunk code -in your Python installation's site-packages directory. Every time the command -is run, the sources are updated from Subversion. - - -Windows Installation Information -================================ - -On windows computers without Visual Studio 2003 Jinja is unable to compile the -C extensions. This is usually not a problem because there is a fallback for -native Python implementations of those C libraries. However under certain -conditions the installation may fail with an error message. In that case try -to disable the extensions by passing the following parameters to setup.py:: - - python setup.py install --without-speedups --without-extended-debugger - - -Documentation -============= - -The egg builds include a documentation which is available in the ``docs`` folder -of the egg. If you're running linux you will find the documentation here:: - - file:///usr/lib/python2.X/site-packages/Jinja-Y.Z-py2.X.egg/docs/index.html - -where ``X``, ``Y`` and ``Z`` must be replaced by the python / jinja version -number. - - -.. _download page: http://jinja.pocoo.org/download.html -.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools -.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall -.. _mercurial: http://www.selenic.com/mercurial/ diff --git a/docs/src/loaders.txt b/docs/src/loaders.txt deleted file mode 100644 index f4480ac..0000000 --- a/docs/src/loaders.txt +++ /dev/null @@ -1,245 +0,0 @@ -================ -Template Loaders -================ - -This part of the documentation explains how to use and write a template loader. - -Builtin Loaders -=============== - -This list contains the builtin loaders you can use without further -modification: - -[[list_of_loaders]] - -Loader Baseclasses -================== - -With Jinja 1.1 onwards all the loaders have (except of the uncached) -baseclasses. You can use them to mix your own caching layer in. This technique -is described below. The `BaseLoader` itself is also a loader baseclass but -because it's the baseclass of all loaders it's covered in the "Developing -Loaders" section. - -[[list_of_baseloaders]] - -Developing Loaders -================== - -Template loaders are just normal Python classes that have to provide some -functions used to load and translate templates. Because some of the tasks -a loader has to do are redundant there are some classes that make loader -development easier. - -Here the implementation of a simple loader based on the `BaseLoader` from -`jinja.loaders`: - -.. sourcecode:: python - - import codecs - from os.path import join - from jinja.loaders import BaseLoader - from jinja.exceptions import TemplateNotFound - - class SimpleLoader(BaseLoader): - - def __init__(self, path): - self.path = path - - def get_source(self, environment, name, parent): - filename = join(self.path, name) - if not path.exists(filename): - raise TemplateNotFound(name) - f = codecs.open(filename, 'r', environment.template_charset) - try: - return f.read() - finally: - f.close() - -The functions `load` and `parse` which are a requirement for a loader are -added automatically by the `BaseLoader`. Instead of the normal `BaseLoader` -you can use one of the other base loaders that already come with a proper -`get_source` method for further modification. Those loaders however are -new in Jinja 1.1. - -CachedLoaderMixin ------------------ - -Additionally to the `BaseLoader` there is a mixin class called -`CachedLoaderMixin` that implements memory and disk caching of templates. -Note that you have to give it a higher priority in the MRO than the -`BaseLoader` which means that's the first base class when inheriting from it: - -.. sourcecode:: python - - import codecs - from os.path import join, getmtime, exists - from jinja.loaders import BaseLoaderCachedLoaderMixin - from jinja.exceptions import TemplateNotFound - - class CachedLoader(CachedLoaderMixin, BaseLoader): - - def __init__(self, path): - self.path = path - CachedLoaderMixin.__init__(self, - True, # use memory caching - 40, # for up to 40 templates - '/tmp', # additionally save the compiled templates in /tmp - True, # and reload cached templates automatically if changed - 'foo' # optional salt used to keep templates with the same - # name in the same cache folder, but from different - # loaders. New in Jinja 1.1 and can be omitted. - ) - - def get_source(self, environment, name, parent): - filename = join(self.path, name) - if not path.exists(filename): - raise TemplateNotFound(name) - f = codecs.open(filename, 'r', environment.template_charset) - try: - return f.read() - finally: - f.close() - - def check_source_changed(self, environment, name): - fn = join(self.path, name) - if exists(fn): - return getmtime(fn) - return -1 - -You don't have to provide the `check_source_changed` method. If it doesn't -exist the option `auto_reload` won't have an effect. Also note that the -`check_source_changed` method must not raise an exception if the template -does not exist but return ``-1``. The return value ``-1`` is considered -"always reload" whereas ``0`` means "do not reload". The default return -value for not existing templates should be ``-1``. - -For the default base classes that come with Jinja 1.1 onwards there exist -also concrete implementations that support caching. The implementation -just mixes in the `CachedLoaderMixin`. - -MemcachedLoaderMixin --------------------- - -*New in Jinja 1.1* - -The `MemcachedLoaderMixin` class adds support for `memcached`_ caching. -There is only one builtin loader that mixes it in: The -`MemcachedFileSystemLoader`. If you need other loaders with this mixin -you can easily subclass one of the existing base loaders. Here an example -for the `FunctionLoader`: - -.. sourcecode:: python - - from jinja.loaders import FunctionLoader, MemcachedLoaderMixin - - class MemcachedFunctionLoader(MemcachedLoaderMixin, FunctionLoader): - - def __init__(self, loader_func): - BaseFunctionLoader.__init__(self, loader_func) - MemcachedLoaderMixin.__init__(self, - True, # use memcached - 60 * 60 * 24 * 7, # 7 days expiration - ['127.0.0.1:11211'], # the memcached hosts - 'template/' # string prefix for the cache keys - ) - -This mixin requires the `python-memcached`_ library. - -.. _memcached: http://www.danga.com/memcached/ -.. _python-memcached: http://www.tummy.com/Community/software/python-memcached/ - - -How Mixin Classes Work -====================== - -The idea of the cached loader mixins is that you override the `load` -method of the other base class so that it's only called to get the data -from the loader and put it into a cache and then bypass the original `load`. - -This works because mixin classes, as well as the loaders are so called "new -style classes" with a MRO (method resolution order). So it's possible to -access the parent without actually knowing the name of it. - -Here as small mixin class that stores everything after loading in a -dict: - -.. sourcecode:: python - - class SimpleCacheMixin(object): - - def __init__(self): - self.__cache = {} - - def load(self, environment, name, translator): - if name in self.__cache: - return self.__cache[name] - tmpl = super(SimpleCacheMixin, self).load(environment, name, - translator) - self.__cache[name] = tmpl - return tmpl - -You can then mix the class in like any other mixin class. Note that -all non public attributes **must** be prefixed with two underscores to -enable the name mangling. Otherwise the mixin class could break the -internal structure of the loader. - -The ``super(SimpleCacheMixin, self)`` call returns an object that looks -up all the attributes you request in all the parent classes. The -`SimpleCacheMixin` just has the `object` parent which makes it a new -style class, but as soon as a loader is mixed in it will call the -`load` method of the loader that is the other parent of the resulting -class. Here a full example. - -Combining Everything -==================== - -Here a full example with a custom cache mixin and a custom base loader: - -.. sourcecode:: python - - import codecs - from os.path import join - from jinja.loaders import BaseLoader - from jinja.exceptions import TemplateNotFound - - class SimpleBaseLoader(BaseLoader): - - def __init__(self, path): - self.path = path - - def get_source(self, environment, name, parent): - filename = join(self.path, name) - if not path.exists(filename): - raise TemplateNotFound(name) - f = codecs.open(filename, 'r', environment.template_charset) - try: - return f.read() - finally: - f.close() - - - class SimpleCacheMixin(object): - - def __init__(self): - self.__cache = {} - - def load(self, environment, name, translator): - if name in self.__cache: - return self.__cache[name] - tmpl = super(SimpleCacheMixin, self).load(environment, name, - translator) - self.__cache[name] = tmpl - return tmpl - - - class SimpleLoader(SimpleBaseLoader, SimpleCacheMixin): - - def __init__(self, path): - SimpleBaseLoader.__init__(self, path) - SimpleCacheMixin.__init__() - -You can of course put all the functionallity into the `SimpleLoader` but then -you cannot exchange parts of it without rewriting much code. In the example -above replacing the `SimpleCacheMixin` with a `MemcachedLoaderMixin` is a -matter of 20 seconds. diff --git a/docs/src/objects.txt b/docs/src/objects.txt deleted file mode 100644 index d1551ac..0000000 --- a/docs/src/objects.txt +++ /dev/null @@ -1,154 +0,0 @@ -============== -Global objects -============== - -This section covers the behavior of global objects in the Jinja namespace and -also the behavior of their attributes. - - -Functions -========= - -`Filters`_ and `Tests`_ are special kind of functions you cannot call without -and argument. Global functions are different. Those are really just ordinary -python functions. You can register them on the global namespace easily: - -.. sourcecode:: python - - def my_function(): - return [1, 2, 3] - - env.globals['my_function'] = my_function - -In the template you can not call it like this: - -.. sourcecode:: jinja - - {{ my_function() }} - -Of couse you can pass any argument to it. But what if you want to access the -context or environment? In that case you have to decorate a function or mark -it (both work exactly the same, but the decorator way is probably nicer if you -have python >= 2.4): - -.. sourcecode:: python - - from cgi import escape - from pprint import pformat - from jinja.datastructure import contextcallable - - @contextcallable - def printcontext(env, context): - result = [] - for key, value in context.to_dict(): - result.append('%s%r' % - (escape(unicode(key)), escape(pformat(value)))) - return '%s
    ' % u'\n'.join(result) - -If this function is registered in the environment namespace then and called -from the template it should return the content of the context as simple html -template. Of course you can modify the context too. For more informations about -the context object have a look at the `context object`_ documentation. - -The new ``{% call %}`` tag that exists with Jinja 1.1 onwards can not only -be used with Jinja macros but also with Python functions. If a template -designers uses the ``{% call %}`` tag to call a function provided by the -application Jinja will call this function with a keyword argument called -`caller` which points to a `function`. If you call this function (optionally -with keyword arguments that appear in the context) you get a string back -that was the content of that block. This should explain this: - -.. sourcecode:: python - - def make_dialog(title, caller=None): - body = '' - if caller: - body = caller(title=title) - return '

    %s

    %s
    ' % (title, body) - -This can be used like this in the template now: - -.. sourcecode:: html+jinja - - {% call make_dialog('Dialog Title') %} - This is the body of the dialog entitled "{{ title }}". - {% endcall %} - -Deferred Values -=============== - -You can pass functions to the template that are called to provide values at -first access. Say you want to pass the list of recently written comments to -all templates. But not necessarily every template will render that. In that -situation you can create a function that returns that value and wrap it in an -`Deferred` object. The first time a template accesses the variable then the -`Deferred` object is resolved: - -.. sourcecode:: python - - from jinja.datastructure import Deferred - from yourapplication.models import Comments - - def get_recent_comments(env, context, name): - # do what ever you want here. - return Comments.get_recent(10) - - env.globals['recent_comments'] = Deferred(get_recent_comments) - -The function is always called with the same arguments. The first one is the -current environment, the second the context and the third is the name of the -variable. In this example ``recent_comments``. - -The value is cached until rendering/streaming finished. - -Unsafe Methods / Attributes -=========================== - -Because Jinja is sandboxed it provides several ways to prevent unsafe attribute -access. You can mark both attributes and methods as unsafe: - -.. sourcecode:: python - - from jinja.datastructure import unsafe - - class MyModel(...): - - # just give access to a and b. Default is all - # note that this also disallows the functions from below. - # if you use jinja_allowed_attributes you don't have add the - # code below since methods are treated as attributes too. - jinja_allowed_attributes = ['a', 'b'] - - def __init__(self, ...): - ... - self.a = ... - self.b = ... - - # python2.4 way of marking methods - @unsafe - def save(self): - """Save the model.""" - - # python2.3 way for the same - def delete(self): - """Delete the model.""" - delete.jinja_unsafe_call = True - - -Bypassing Automatic Filtering -============================= - -With Jinja 1.1 it's possible to assign filters to any variable printed. Of -course there are ways to bypass that in order to make sure an object does -not get escaped etc. - -In order to disable automatic filtering for an object you have to make -sure that it's either an subclass of `unicode` or implements a -`__unicode__` method. `__str__` will work too as long as the return value -only contains ASCII values. Additionally you have to add an attribute to -that object named `jinja_no_finalization` and set that to `True`. - - -.. _Filters: filters.txt -.. _Tests: tests.txt -.. _context object: contextenv.txt diff --git a/docs/src/recipies.txt b/docs/src/recipies.txt deleted file mode 100644 index 08ce8bb..0000000 --- a/docs/src/recipies.txt +++ /dev/null @@ -1,184 +0,0 @@ -======== -Recipies -======== - -Here are some typical recipes for the usage of Jinja templates. - -Alternating Rows -================ - -If you want to have different styles for each row of a table or -list you can use the ``cycle`` tag: - -.. sourcecode:: html+jinja - -
      - {% for row in sequence %} -
    • {{ row|e }}
    • - {% endfor %} -
    - -``cycle`` can take an unlimited amount of strings. Each time this -tag is encountered the next item from the list is rendered. -If you pass it just one argument it's meant to be a sequence. - - -Active Menu Item -================ - -Often you want to have a navigation bar with an active navigation -item. This is really simple to achieve. Because ``set`` tags outside -of ``blocks`` are global you can do something like this: - -**layout.html**: - - .. sourcecode:: html+jinja - - {% set navigation_bar = [ - ('/', 'index', 'Index'), - ('/downloads/', 'downloads', 'Downloads'), - ('/about/', 'about', 'About') - ] %} - ... - - ... - -**index.html**: - - .. sourcecode:: jinja - - {% extends "layout.html" %} - {% set active_page = "index" %} - - -Sitemap -======= - -To create a sitemap you can either use the ``for`` tag or a ``macro`` -that calls itself. The datastructures should look like this: - -.. sourcecode:: python - - {'sitemap': [ - dict( - caption='Pages', - children=[ - dict(href='index.html', caption='Index'), - dict(href='downloads.html', caption='Downloads'), - dict( - caption='Users', - children=[ - dict(href='peter.html', - caption='Peter'), - dict(href='max.html', - caption='Max'), - dict(href='suzan.html', - caption='Suzan') - ] - ), - dict( - caption='Files', - children=[ - dict( - caption='Images', - children=[ - dict(href='vienna.html', - caption='Vienna'), - dict(href='roma.html', - caption='Roma'), - dict(href='tokyo.html', - caption='Tokyo') - ] - ), - dict( - caption='Videos', - children=[ - dict(href='party.html', - caption='Party') - ] - ) - ] - ) - ] - ), - dict(caption='Foo', href='foo.html') - dict(caption='About', href='about.html') - ]} - -Now you can create a sitemap using ``for``: - -.. sourcecode:: html+jinja - -
      - {% for item in sitemap recursive %} -
    • {{ item.caption|e }} - {% if item.children %}
        {{ loop(item.children) }}
      {% endif %}
    • - {% endfor %} -
    - -Or by using a ``macro`` that calls itself: - -.. sourcecode:: html+jinja - - {% macro render_sitemap items %} - {% for item in items %} -
  • {{ item.caption|e }} - {% if item.children %}
      {{ render_sitemap(item.children) }}
    {% endif %}
  • - {% endfor %} - {% endmacro %} -
      {{ render_sitemap(sitemap) }}
    - - -Using A Block Multiple Times -============================ - -Blocks have the small disadvantage that they work both ways which is a problem -if you want to render a block two times on a page. Here a nice little -workaround for this limitation: - -.. sourcecode:: html+jinja - - - - {% filter capture('title') %}{% - block title %}{% endblock %}{% - endfilter %} - - -
    {{ title }}
    - - - -Or if you use the `capture` filter in `clean` mode: - -.. sourcecode:: html+jinja - - {% filter capture('title', True)|trim %} - {% block title %}{% endblock %} - {% endfilter %} - - - {{ title }} - - -
    {{ title }}
    - - - - -Vim Syntax Highlighting -======================= - -Because of the similar syntax to django you can use the django highlighting -plugin for jinja too. There is however a Jinja syntax highlighting plugin -too which supports all of the syntax elements. - -You can download it from the vim webpage: `jinja.vim`_ - - -.. _jinja.vim: http://www.vim.org/scripts/script.php?script_id=1856 diff --git a/docs/src/scopes.txt b/docs/src/scopes.txt deleted file mode 100644 index 8dd1e43..0000000 --- a/docs/src/scopes.txt +++ /dev/null @@ -1,186 +0,0 @@ -============================ -Scopes and Variable Behavior -============================ - -This section of the documentation covers the Jinja behavior regarding -variable visibility. - - -Scopes -====== - -Jinja has multiple scopes. A scope is something like a new transparent foil on -a stack of foils. You can only write to the outermost foil but read all of them -since you can look through them. If you remove the top foil all data on that -foil disappears. Some tags in Jinja add a new layer to the stack. Currently -these are `block`, `for`, `macro` and `filter`. This means that variables and -other elements defined inside a macro, loop or some of the other tags listed -above will be only available in that block. Here an example: - -.. sourcecode:: jinja - - {% macro angryhello name %} - {% set angryname = name|upper %} - Hello {{ name }}. Hello {{ name }}! - HELLO {{ angryname }}!!!!!!111 - {% endmacro %} - -The variable ``angryname`` just exists inside the macro, not outside it. - -Defined macros appear on the context as variables. Because of this, they are -affected by the scoping too. A macro defined inside of a macro is just available -in those two macros (the macro itself and the macro it's defined in). - - -Template Globals -================ - -A special threatment exists for template code outside of visible blocks in -child templates. This code will be executed **before** the layout template -code. Thus it can be used to propagate values back to the layout template or -import macros from templates for rendering. - -Such code can output data but it won't appear in the final rendering. So no -additional whitespace will pollute the template. - -Because this code is executed before the actual layout template code it's -possible that the layout code overrides some of those variables. Usually -this is not a problem because of different variable names but it can be -a problem if you plan to specify default values. - -In that case you have to test if the variable is not defined before setting -it: - -.. sourcecode:: jinja - - {% if not page_title %} - {% set page_title = 'Default Page Title' %} - {% endif %} - -You can of course also use the `|default` filter. - -.. admonition:: Explanation - - This template stored as `a.html`: - - .. sourcecode:: html+jinja - - {{ title|default('Untitled') }} - {% block body %}{% endblock %} - - ...and this child template stored as `b.html`: - - .. sourcecode:: html+jinja - - {% extends 'a.html' %} - {% include 'macros.tmpl' %} - {% set title = 'My Page' %} - {% block body %}{{ wrap(42) }}{% endblock %} - - ...and this code in `macros.tmpl`: - - .. sourcecode:: html+jinja - - {% macro wrap(text) %} - [{{ text }}] - {% endmacro %} - - ..will translate to something with the same semantics as this (just - that the value is not stored in a variable): - - .. sourcecode:: html+jinja - - {% filter capture('captured', true) %} - {% macro wrap(text) %} - [{{ text }}] - {% endmacro %} - {% set title='My Page' %} - {% endfilter %} - {{ title|default('Untitled') }} - - {{ wrap(42) }} - - -.. admonition:: Note - - This implementation was improved in Jinja 1.1. In Jinja 1.0 blocks that - were not top-level were not propagated to the layout template. This made - it impossible to use conditional expressions for inclusion in non root - templates. - - -Undefined Variables -=================== - -If you have already worked with python you probably know about the fact that -undefined variables raise an exception. This is different in Jinja. There is a -special value called `undefined` that represents values that do not exist. - -Depending on the configuration it will behave different. - -In order to check if a value is defined you can use the `defined` test: - -.. sourcecode:: jinja - - {{ myvariable is not defined }} - will return true if the variable does not exist. - -`SilentUndefined`: - - The silent `undefined` is the default behavior. The `undefined` object works - complete different from any variables you maybe know. If you print it - using ``{{ variable }}`` it will not appear because it's literally empty. - If you try to iterate over it, it will work. But no items are returned. - - In order to check if a value is defined you can use the `defined` test: - - There are also some additional rules regarding this special value. Any - mathematical operators (``+``, ``-``, ``*``, ``/``) return the operand - as result: - - .. sourcecode:: jinja - - {{ undefined + "foo" }} - returns "foo" - - {{ undefined - 42 }} - returns 42. Note: not -42! - - In any expression `undefined` evaluates to `false`. It has no length, all - attribute calls return undefined, calling too: - - .. sourcecode:: jinja - - {{ undefined.attribute().attribute_too[42] }} - still returns `undefined`. - -`ComplainingUndefined`: - - Starting with Jinja 1.1 it's possible to replace the default undefined - object with different values. The other common undefined object which - comes with Jinja is the `ComplainingUndefined` object. - - It raises exceptions as soon as you either render it or want to iterate - over it or try to access attributes etc. - - -Overriding Variables Of Outer Scopes -==================================== - -*New in Jinja 1.2* - -Normally you cannot override a variable from an outer scope, you can just hide -it. There is however a way to override a variable from an outer scope using the -`set` tag, postfixed with a bang (!): - -.. sourcecode:: jinja - - {% set last_item = none %} - {% for item in seq %} - {% set last_item = item! %} - {% endfor %} - -After the iteration `last_item` will point to the item of the last iteration. - -If `last_item` was not defined in the outer scope it would be defined in the -outermost scope. diff --git a/docs/src/streaming.txt b/docs/src/streaming.txt deleted file mode 100644 index d872dc1..0000000 --- a/docs/src/streaming.txt +++ /dev/null @@ -1,74 +0,0 @@ -=================== -Streaming Interface -=================== - -With Jinja 1.1 onwards it's possible to stream the template output. This is -usually a bad idea because it's slower than `render()` but there are some -situations where it's useful. - -If you for example generate a file with a couple of megabytes you may want -to pass the stream to the WSGI interface in order to keep the amount of -memory used low and deliver the output to the browser as fast as possible. - -The streaming interface is straightforward. Instead of using `render()` you -can call `stream()` which does pretty much the same but doesn't return a -string but a `TemplateStream`: - -.. sourcecode:: pycon - - >>> tmpl = env.from_string("
      {% for item in seq %}\n
    • {{ loop.index " - ... "}} - {{ item }}
    • \n{%- endfor %}
    ") - >>> stream = tmpl.stream(seq=range(4)) - >>> stream.next() - '
      ' - >>> stream.next() - u'\n
    • 1 - 0
    • ' - >>> stream.next() - u'\n
    • 2 - 1
    • ' - >>> stream.next() - u'\n
    • 3 - 2
    • ' - >>> stream.next() - u'\n
    • 4 - 3
    • ' - >>> stream.next() - '
    ' - -As you can see each iteration is threated as template event here. But also -other tags trigger events. Basically every tag yields one event, the -`print` tag too. The only exception is the variable substitution syntax which -is inserted into the template text data. - -Because some protocols like `WSGI` flush after each iteration if passed as -response iterable it's better to buffer some events internally. You can do -this by enable buffering using `enable_buffering` and passing it the buffer -size which must be greater than one: - -.. sourcecode:: pycon - - >>> stream.enable_buffering(size=3) - >>> stream.next() - u'
      \n
    • 1 - 0
    • \n
    • 2 - 1
    • ' - >>> stream.next() - u'\n
    • 3 - 2
    • \n
    • 4 - 3
    ' - -This will buffer 3 events before yielding. Disabling buffering works using the -`disable_buffering` method. You can enable and disable buffering on the fly also -if you have already iterated over that stream. To check if you are in buffered -or unbuffered mode you can use the `.buffered` property: - -.. sourcecode:: pycon - - >>> stream.buffered - False - >>> stream.enable_buffering(20) - >>> stream.buffered - True - >>> stream.disable_buffering() - >>> stream.buffered - False - -.. admonition:: Note - - Jinja uses buffering internally for some constructs like macros. A macro - call is yielded as one event, independently of the internal structure. - - The same applies to recursive for loops and `{% filter %}` tags. diff --git a/docs/src/templatei18n.txt b/docs/src/templatei18n.txt deleted file mode 100644 index debd82b..0000000 --- a/docs/src/templatei18n.txt +++ /dev/null @@ -1,83 +0,0 @@ -============================= -Template Internationalization -============================= - -If the application is configured for i18n, you can define translatable blocks -for translators using the `trans` tag or the special underscore function: - -.. sourcecode:: jinja - - {% trans %} - this is a translatable block - {% endtrans %} - - {% trans "This is a translatable string" %} - - {{ _("This is a translatable string") }} - -The latter one is useful if you want translatable arguments for filters etc. -If you want to use the ``_()`` syntax in an expression and have variables in -the string you can add a substituation marker (``%s``) and use the `|format` -filter to fill the slot: - -.. sourcecode:: jinja - - {{ _('Hello %s!')|format(username) }} - -If you have more than one substitution variable consider using the -``{% trans %}`` tags or the `|dformat` filter, the latter however is new -in Jinja 1.1. - -If you want to have plural forms too, use the `pluralize` block: - -.. sourcecode:: jinja - - {% trans users=users %} - One user found. - {% pluralize %} - {{ users }} users found. - {% endtrans %} - - {% trans first=(users|first).username|escape, user=users|length %} - one user {{ first }} found. - {% pluralize users %} - {{ users }} users found, the first one is called {{ first }}. - {% endtrans %} - -If you have multiple arguments, the first one is assumed to be the indicator (the -number that is used to determine the correct singular or plural form. If you -don't have the indicator variable on position 1 you have to tell the `pluralize` -tag the correct variable name. - -Inside translatable blocks you cannot use blocks or expressions (however you can -still use the ``raw`` block which will work as expected). The variable -print syntax (``{{ variablename }}``) is the only way to insert the variables -defined in the ``trans`` header. Filters must be applied in the header. - -.. admonition:: note - - Please make sure that you always use pluralize blocks where required. - Many languages have more complex plural forms than the English language. - - Never try to workaround that issue by using something like this: - - .. sourcecode:: jinja - - {% if count != 1 %} - {{ count }} users found. - {% else %} - one user found. - {% endif %} - -*New in Jinja 1.1*: It's now possible to use the marker name as implicit -default: - -.. sourcecode:: jinja - - instead of this version: - - {% trans username=username %}Hello {{ username }}!{% endtrans %} - - you can now write this: - - {% trans username %}Hello {{ username }}!{% endtrans %} diff --git a/docs/src/tests.txt b/docs/src/tests.txt deleted file mode 100644 index 21ce64f..0000000 --- a/docs/src/tests.txt +++ /dev/null @@ -1,30 +0,0 @@ -============== -Test Functions -============== - -In addition to filters, Jinja also supports test functions. Test functions -always return either ``True`` or ``False``. Inside the template they are available -using the `is` operator. - -Jinja comes with some builtin tests listed in the `designer documentation`_. - -Writing Test Functions -====================== - -Test functions look exactly like filters mentioned in the `filter documentation`_: - -.. sourcecode:: python - - def is_even(): - def wrapped(env, context, value): - return value % 2 == 0 - return wrapped - -Now you have to register that test on an environment: - -.. sourcecode:: python - - env.tests['even'] = is_even - -.. _designer documentation: builtins.txt -.. _filter documentation: filters.txt diff --git a/docs/src/translators.txt b/docs/src/translators.txt deleted file mode 100644 index dd77746..0000000 --- a/docs/src/translators.txt +++ /dev/null @@ -1,10 +0,0 @@ -=========== -Translators -=========== - -Jinja translates the template sourcecode into executable python code behind -the secenes. This is done by the python translator which is currently the -only shipped translator. Because the interface isn't stable it's also not -recommended yet to write other translators. However for one of the next Jinja -versions a JavaScript translator is planned which allows you to translate -Jinja templates into executable JavaScript code. -- 2.26.2