diff --git a/src/jinja2/__init__.py b/__init__.py similarity index 100% rename from src/jinja2/__init__.py rename to __init__.py diff --git a/src/jinja2/_identifier.py b/_identifier.py similarity index 100% rename from src/jinja2/_identifier.py rename to _identifier.py diff --git a/artwork/jinjalogo.svg b/artwork/jinjalogo.svg deleted file mode 100644 index 0bc9ea4..0000000 --- a/artwork/jinjalogo.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/src/jinja2/async_utils.py b/async_utils.py similarity index 100% rename from src/jinja2/async_utils.py rename to async_utils.py diff --git a/src/jinja2/bccache.py b/bccache.py similarity index 100% rename from src/jinja2/bccache.py rename to bccache.py diff --git a/src/jinja2/compiler.py b/compiler.py similarity index 100% rename from src/jinja2/compiler.py rename to compiler.py diff --git a/src/jinja2/constants.py b/constants.py similarity index 100% rename from src/jinja2/constants.py rename to constants.py diff --git a/src/jinja2/debug.py b/debug.py similarity index 100% rename from src/jinja2/debug.py rename to debug.py diff --git a/src/jinja2/defaults.py b/defaults.py similarity index 100% rename from src/jinja2/defaults.py rename to defaults.py diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 5128596..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/jinja-logo-sidebar.png b/docs/_static/jinja-logo-sidebar.png deleted file mode 100644 index 455b4c3..0000000 Binary files a/docs/_static/jinja-logo-sidebar.png and /dev/null differ diff --git a/docs/_static/jinja-logo.png b/docs/_static/jinja-logo.png deleted file mode 100644 index 7f8ca5b..0000000 Binary files a/docs/_static/jinja-logo.png and /dev/null differ diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index b2db537..0000000 --- a/docs/api.rst +++ /dev/null @@ -1,922 +0,0 @@ -API -=== - -.. module:: jinja2 - :noindex: - :synopsis: public Jinja API - -This document describes the API to Jinja and not the template language -(for that, see :doc:`/templates`). It will be most useful as reference -to those implementing the template interface to the application and not -those who are creating Jinja templates. - -Basics ------- - -Jinja uses a central object called the template :class:`Environment`. -Instances of this class are used to store the configuration and global objects, -and are used to load templates from the file system or other locations. -Even if you are creating templates from strings by using the constructor of -:class:`Template` class, an environment is created automatically for you, -albeit a shared one. - -Most applications will create one :class:`Environment` object on application -initialization and use that to load templates. In some cases however, it's -useful to have multiple environments side by side, if different configurations -are in use. - -The simplest way to configure Jinja to load templates for your -application is to use :class:`~loaders.PackageLoader`. - -.. code-block:: python - - from jinja2 import Environment, PackageLoader, select_autoescape - env = Environment( - loader=PackageLoader("yourapp"), - autoescape=select_autoescape() - ) - -This will create a template environment with a loader that looks up -templates in the ``templates`` folder inside the ``yourapp`` Python -package (or next to the ``yourapp.py`` Python module). It also enables -autoescaping for HTML files. This loader only requires that ``yourapp`` -is importable, it figures out the absolute path to the folder for you. - -Different loaders are available to load templates in other ways or from -other locations. They're listed in the `Loaders`_ section below. You can -also write your own if you want to load templates from a source that's -more specialized to your project. - -To load a template from this environment, call the :meth:`get_template` -method, which returns the loaded :class:`Template`. - -.. code-block:: python - - template = env.get_template("mytemplate.html") - -To render it with some variables, call the :meth:`render` method. - -.. code-block:: python - - print(template.render(the="variables", go="here")) - -Using a template loader rather than passing strings to :class:`Template` -or :meth:`Environment.from_string` has multiple advantages. Besides being -a lot easier to use it also enables template inheritance. - -.. admonition:: Notes on Autoescaping - - In future versions of Jinja we might enable autoescaping by default - for security reasons. As such you are encouraged to explicitly - configure autoescaping now instead of relying on the default. - - -High Level API --------------- - -The high-level API is the API you will use in the application to load and -render Jinja templates. The :ref:`low-level-api` on the other side is only -useful if you want to dig deeper into Jinja or :ref:`develop extensions -`. - -.. autoclass:: Environment([options]) - :members: from_string, get_template, select_template, - get_or_select_template, join_path, extend, compile_expression, - compile_templates, list_templates, add_extension - - .. attribute:: shared - - If a template was created by using the :class:`Template` constructor - an environment is created automatically. These environments are - created as shared environments which means that multiple templates - may have the same anonymous environment. For all shared environments - this attribute is `True`, else `False`. - - .. attribute:: sandboxed - - If the environment is sandboxed this attribute is `True`. For the - sandbox mode have a look at the documentation for the - :class:`~jinja2.sandbox.SandboxedEnvironment`. - - .. attribute:: filters - - A dict of filters for this environment. As long as no template was - loaded it's safe to add new filters or remove old. For custom filters - see :ref:`writing-filters`. For valid filter names have a look at - :ref:`identifier-naming`. - - .. attribute:: tests - - A dict of test functions for this environment. As long as no - template was loaded it's safe to modify this dict. For custom tests - see :ref:`writing-tests`. For valid test names have a look at - :ref:`identifier-naming`. - - .. attribute:: globals - - A dict of variables that are available in every template loaded - by the environment. As long as no template was loaded it's safe - to modify this. For more details see :ref:`global-namespace`. - For valid object names see :ref:`identifier-naming`. - - .. attribute:: policies - - A dictionary with :ref:`policies`. These can be reconfigured to - change the runtime behavior or certain template features. Usually - these are security related. - - .. attribute:: code_generator_class - - The class used for code generation. This should not be changed - in most cases, unless you need to modify the Python code a - template compiles to. - - .. attribute:: context_class - - The context used for templates. This should not be changed - in most cases, unless you need to modify internals of how - template variables are handled. For details, see - :class:`~jinja2.runtime.Context`. - - .. automethod:: overlay([options]) - - .. method:: undefined([hint, obj, name, exc]) - - Creates a new :class:`Undefined` object for `name`. This is useful - for filters or functions that may return undefined objects for - some operations. All parameters except of `hint` should be provided - as keyword parameters for better readability. The `hint` is used as - error message for the exception if provided, otherwise the error - message will be generated from `obj` and `name` automatically. The exception - provided as `exc` is raised if something with the generated undefined - object is done that the undefined object does not allow. The default - exception is :exc:`UndefinedError`. If a `hint` is provided the - `name` may be omitted. - - The most common way to create an undefined object is by providing - a name only:: - - return environment.undefined(name='some_name') - - This means that the name `some_name` is not defined. If the name - was from an attribute of an object it makes sense to tell the - undefined object the holder object to improve the error message:: - - if not hasattr(obj, 'attr'): - return environment.undefined(obj=obj, name='attr') - - For a more complex example you can provide a hint. For example - the :func:`first` filter creates an undefined object that way:: - - return environment.undefined('no first item, sequence was empty') - - If it the `name` or `obj` is known (for example because an attribute - was accessed) it should be passed to the undefined object, even if - a custom `hint` is provided. This gives undefined objects the - possibility to enhance the error message. - -.. autoclass:: Template - :members: module, make_module - - .. attribute:: globals - - A dict of variables that are available every time the template - is rendered, without needing to pass them during render. This - should not be modified, as depending on how the template was - loaded it may be shared with the environment and other - templates. - - Defaults to :attr:`Environment.globals` unless extra values are - passed to :meth:`Environment.get_template`. - - Globals are only intended for data that is common to every - render of the template. Specific data should be passed to - :meth:`render`. - - See :ref:`global-namespace`. - - .. attribute:: name - - The loading name of the template. If the template was loaded from a - string this is `None`. - - .. attribute:: filename - - The filename of the template on the file system if it was loaded from - there. Otherwise this is `None`. - - .. automethod:: render([context]) - - .. automethod:: generate([context]) - - .. automethod:: stream([context]) - - .. automethod:: render_async([context]) - - .. automethod:: generate_async([context]) - - -.. autoclass:: jinja2.environment.TemplateStream() - :members: disable_buffering, enable_buffering, dump - - -Autoescaping ------------- - -.. versionchanged:: 2.4 - -Jinja now comes with autoescaping support. As of Jinja 2.9 the -autoescape extension is removed and built-in. However autoescaping is -not yet enabled by default though this will most likely change in the -future. It's recommended to configure a sensible default for -autoescaping. This makes it possible to enable and disable autoescaping -on a per-template basis (HTML versus text for instance). - -.. autofunction:: jinja2.select_autoescape - -Here a recommended setup that enables autoescaping for templates ending -in ``'.html'``, ``'.htm'`` and ``'.xml'`` and disabling it by default -for all other extensions. You can use the :func:`~jinja2.select_autoescape` -function for this:: - - from jinja2 import Environment, PackageLoader, select_autoescape - env = Environment(autoescape=select_autoescape(['html', 'htm', 'xml']), - loader=PackageLoader('mypackage')) - -The :func:`~jinja.select_autoescape` function returns a function that -works roughly like this:: - - def autoescape(template_name): - if template_name is None: - return False - if template_name.endswith(('.html', '.htm', '.xml')) - -When implementing a guessing autoescape function, make sure you also -accept `None` as valid template name. This will be passed when generating -templates from strings. You should always configure autoescaping as -defaults in the future might change. - -Inside the templates the behaviour can be temporarily changed by using -the `autoescape` block (see :ref:`autoescape-overrides`). - - -.. _identifier-naming: - -Notes on Identifiers --------------------- - -Jinja uses Python naming rules. Valid identifiers can be any combination -of characters accepted by Python. - -Filters and tests are looked up in separate namespaces and have slightly -modified identifier syntax. Filters and tests may contain dots to group -filters and tests by topic. For example it's perfectly valid to add a -function into the filter dict and call it `to.str`. The regular -expression for filter and test identifiers is -``[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*``. - - -Undefined Types ---------------- - -These classes can be used as undefined types. The :class:`Environment` -constructor takes an `undefined` parameter that can be one of those classes -or a custom subclass of :class:`Undefined`. Whenever the template engine is -unable to look up a name or access an attribute one of those objects is -created and returned. Some operations on undefined values are then allowed, -others fail. - -The closest to regular Python behavior is the :class:`StrictUndefined` which -disallows all operations beside testing if it's an undefined object. - -.. autoclass:: jinja2.Undefined() - - .. attribute:: _undefined_hint - - Either `None` or a string with the error message for the - undefined object. - - .. attribute:: _undefined_obj - - Either `None` or the owner object that caused the undefined object - to be created (for example because an attribute does not exist). - - .. attribute:: _undefined_name - - The name for the undefined variable / attribute or just `None` - if no such information exists. - - .. attribute:: _undefined_exception - - The exception that the undefined object wants to raise. This - is usually one of :exc:`UndefinedError` or :exc:`SecurityError`. - - .. method:: _fail_with_undefined_error(\*args, \**kwargs) - - When called with any arguments this method raises - :attr:`_undefined_exception` with an error message generated - from the undefined hints stored on the undefined object. - -.. autoclass:: jinja2.ChainableUndefined() - -.. autoclass:: jinja2.DebugUndefined() - -.. autoclass:: jinja2.StrictUndefined() - -There is also a factory function that can decorate undefined objects to -implement logging on failures: - -.. autofunction:: jinja2.make_logging_undefined - -Undefined objects are created by calling :attr:`undefined`. - -.. admonition:: Implementation - - :class:`Undefined` is implemented by overriding the special - ``__underscore__`` methods. For example the default - :class:`Undefined` class implements ``__str__`` to returns an empty - string, while ``__int__`` and others fail with an exception. To - allow conversion to int by returning ``0`` you can implement your - own subclass. - - .. code-block:: python - - class NullUndefined(Undefined): - def __int__(self): - return 0 - - def __float__(self): - return 0.0 - - To disallow a method, override it and raise - :attr:`~Undefined._undefined_exception`. Because this is very - common there is the helper method - :meth:`~Undefined._fail_with_undefined_error` that raises the error - with the correct information. Here's a class that works like the - regular :class:`Undefined` but fails on iteration:: - - class NonIterableUndefined(Undefined): - def __iter__(self): - self._fail_with_undefined_error() - - -The Context ------------ - -.. autoclass:: jinja2.runtime.Context() - :members: get, resolve, resolve_or_missing, get_exported, get_all - - .. attribute:: parent - - A dict of read only, global variables the template looks up. These - can either come from another :class:`Context`, from the - :attr:`Environment.globals` or :attr:`Template.globals` or points - to a dict created by combining the globals with the variables - passed to the render function. It must not be altered. - - .. attribute:: vars - - The template local variables. This list contains environment and - context functions from the :attr:`parent` scope as well as local - modifications and exported variables from the template. The template - will modify this dict during template evaluation but filters and - context functions are not allowed to modify it. - - .. attribute:: environment - - The environment that loaded the template. - - .. attribute:: exported_vars - - This set contains all the names the template exports. The values for - the names are in the :attr:`vars` dict. In order to get a copy of the - exported variables as dict, :meth:`get_exported` can be used. - - .. attribute:: name - - The load name of the template owning this context. - - .. attribute:: blocks - - A dict with the current mapping of blocks in the template. The keys - in this dict are the names of the blocks, and the values a list of - blocks registered. The last item in each list is the current active - block (latest in the inheritance chain). - - .. attribute:: eval_ctx - - The current :ref:`eval-context`. - - .. automethod:: jinja2.runtime.Context.call(callable, \*args, \**kwargs) - - -The context is immutable, it prevents modifications, and if it is -modified somehow despite that those changes may not show up. For -performance, Jinja does not use the context as data storage for, only as -a primary data source. Variables that the template does not define are -looked up in the context, but variables the template does define are -stored locally. - -Instead of modifying the context directly, a function should return -a value that can be assigned to a variable within the template itself. - -.. code-block:: jinja - - {% set comments = get_latest_comments() %} - - -.. _loaders: - -Loaders -------- - -Loaders are responsible for loading templates from a resource such as the -file system. The environment will keep the compiled modules in memory like -Python's `sys.modules`. Unlike `sys.modules` however this cache is limited in -size by default and templates are automatically reloaded. -All loaders are subclasses of :class:`BaseLoader`. If you want to create your -own loader, subclass :class:`BaseLoader` and override `get_source`. - -.. autoclass:: jinja2.BaseLoader - :members: get_source, load - -Here a list of the builtin loaders Jinja provides: - -.. autoclass:: jinja2.FileSystemLoader - -.. autoclass:: jinja2.PackageLoader - -.. autoclass:: jinja2.DictLoader - -.. autoclass:: jinja2.FunctionLoader - -.. autoclass:: jinja2.PrefixLoader - -.. autoclass:: jinja2.ChoiceLoader - -.. autoclass:: jinja2.ModuleLoader - - -.. _bytecode-cache: - -Bytecode Cache --------------- - -Jinja 2.1 and higher support external bytecode caching. Bytecode caches make -it possible to store the generated bytecode on the file system or a different -location to avoid parsing the templates on first use. - -This is especially useful if you have a web application that is initialized on -the first request and Jinja compiles many templates at once which slows down -the application. - -To use a bytecode cache, instantiate it and pass it to the :class:`Environment`. - -.. autoclass:: jinja2.BytecodeCache - :members: load_bytecode, dump_bytecode, clear - -.. autoclass:: jinja2.bccache.Bucket - :members: write_bytecode, load_bytecode, bytecode_from_string, - bytecode_to_string, reset - - .. attribute:: environment - - The :class:`Environment` that created the bucket. - - .. attribute:: key - - The unique cache key for this bucket - - .. attribute:: code - - The bytecode if it's loaded, otherwise `None`. - - -Builtin bytecode caches: - -.. autoclass:: jinja2.FileSystemBytecodeCache - -.. autoclass:: jinja2.MemcachedBytecodeCache - - -Async Support -------------- - -.. versionadded:: 2.9 - -Jinja supports the Python ``async`` and ``await`` syntax. For the -template designer, this support (when enabled) is entirely transparent, -templates continue to look exactly the same. However, developers should -be aware of the implementation as it affects what types of APIs you can -use. - -By default, async support is disabled. Enabling it will cause the -environment to compile different code behind the scenes in order to -handle async and sync code in an asyncio event loop. This has the -following implications: - -- Template rendering requires an event loop to be available to the - current thread. :func:`asyncio.get_running_loop` must return an - event loop. -- The compiled code uses ``await`` for functions and attributes, and - uses ``async for`` loops. In order to support using both async and - sync functions in this context, a small wrapper is placed around - all calls and access, which adds overhead compared to purely async - code. -- Sync methods and filters become wrappers around their corresponding - async implementations where needed. For example, ``render`` invokes - ``async_render``, and ``|map`` supports async iterables. - -Awaitable objects can be returned from functions in templates and any -function call in a template will automatically await the result. The -``await`` you would normally add in Python is implied. For example, you -can provide a method that asynchronously loads data from a database, and -from the template designer's point of view it can be called like any -other function. - - -.. _policies: - -Policies --------- - -Starting with Jinja 2.9 policies can be configured on the environment -which can slightly influence how filters and other template constructs -behave. They can be configured with the -:attr:`~jinja2.Environment.policies` attribute. - -Example:: - - env.policies['urlize.rel'] = 'nofollow noopener' - -``truncate.leeway``: - Configures the leeway default for the `truncate` filter. Leeway as - introduced in 2.9 but to restore compatibility with older templates - it can be configured to `0` to get the old behavior back. The default - is `5`. - -``urlize.rel``: - A string that defines the items for the `rel` attribute of generated - links with the `urlize` filter. These items are always added. The - default is `noopener`. - -``urlize.target``: - The default target that is issued for links from the `urlize` filter - if no other target is defined by the call explicitly. - -``urlize.extra_schemes``: - Recognize URLs that start with these schemes in addition to the - default ``http://``, ``https://``, and ``mailto:``. - -``json.dumps_function``: - If this is set to a value other than `None` then the `tojson` filter - will dump with this function instead of the default one. Note that - this function should accept arbitrary extra arguments which might be - passed in the future from the filter. Currently the only argument - that might be passed is `indent`. The default dump function is - ``json.dumps``. - -``json.dumps_kwargs``: - Keyword arguments to be passed to the dump function. The default is - ``{'sort_keys': True}``. - -.. _ext-i18n-trimmed: - -``ext.i18n.trimmed``: - If this is set to `True`, ``{% trans %}`` blocks of the - :ref:`i18n-extension` will always unify linebreaks and surrounding - whitespace as if the `trimmed` modifier was used. - - -Utilities ---------- - -These helper functions and classes are useful if you add custom filters or -functions to a Jinja environment. - -.. autofunction:: jinja2.pass_context - -.. autofunction:: jinja2.pass_eval_context - -.. autofunction:: jinja2.pass_environment - -.. autofunction:: jinja2.clear_caches - -.. autofunction:: jinja2.is_undefined - - -Exceptions ----------- - -.. autoexception:: jinja2.TemplateError - -.. autoexception:: jinja2.UndefinedError - -.. autoexception:: jinja2.TemplateNotFound - -.. autoexception:: jinja2.TemplatesNotFound - -.. autoexception:: jinja2.TemplateSyntaxError - - .. attribute:: message - - The error message. - - .. attribute:: lineno - - The line number where the error occurred. - - .. attribute:: name - - The load name for the template. - - .. attribute:: filename - - The filename that loaded the template in the encoding of the - file system (most likely utf-8, or mbcs on Windows systems). - -.. autoexception:: jinja2.TemplateRuntimeError - -.. autoexception:: jinja2.TemplateAssertionError - - -.. _writing-filters: - -Custom Filters --------------- - -Filters are Python functions that take the value to the left of the -filter as the first argument and produce a new value. Arguments passed -to the filter are passed after the value. - -For example, the filter ``{{ 42|myfilter(23) }}`` is called behind the -scenes as ``myfilter(42, 23)``. - -Jinja comes with some :ref:`built-in filters `. To use -a custom filter, write a function that takes at least a ``value`` -argument, then register it in :attr:`Environment.filters`. - -Here's a filter that formats datetime objects: - -.. code-block:: python - - def datetime_format(value, format="%H:%M %d-%m-%y"): - return value.strftime(format) - - environment.filters["datetime_format"] = datetime_format - -Now it can be used in templates: - -.. sourcecode:: jinja - - {{ article.pub_date|datetimeformat }} - {{ article.pub_date|datetimeformat("%B %Y") }} - -Some decorators are available to tell Jinja to pass extra information to -the filter. The object is passed as the first argument, making the value -being filtered the second argument. - -- :func:`pass_environment` passes the :class:`Environment`. -- :func:`pass_eval_context` passes the :ref:`eval-context`. -- :func:`pass_context` passes the current - :class:`~jinja2.runtime.Context`. - -Here's a filter that converts line breaks into HTML ``
`` and ``

`` -tags. It uses the eval context to check if autoescape is currently -enabled before escaping the input and marking the output safe. - -.. code-block:: python - - import re - from jinja2 import pass_eval_context - from markupsafe import Markup, escape - - @pass_eval_context - def nl2br(eval_ctx, value): - br = "
\n" - - if eval_ctx.autoescape: - value = escape(value) - br = Markup(br) - - result = "\n\n".join( - f"

{br.join(p.splitlines())}<\p>" - for p in re.split(r"(?:\r\n|\r(?!\n)|\n){2,}", value) - ) - return Markup(result) if autoescape else result - - -.. _writing-tests: - -Custom Tests ------------- - -Test are Python functions that take the value to the left of the test as -the first argument, and return ``True`` or ``False``. Arguments passed -to the test are passed after the value. - -For example, the test ``{{ 42 is even }}`` is called behind the scenes -as ``is_even(42)``. - -Jinja comes with some :ref:`built-in tests `. To use a -custom tests, write a function that takes at least a ``value`` argument, -then register it in :attr:`Environment.tests`. - -Here's a test that checks if a value is a prime number: - -.. code-block:: python - - import math - - def is_prime(n): - if n == 2: - return True - - for i in range(2, int(math.ceil(math.sqrt(n))) + 1): - if n % i == 0: - return False - - return True - - environment.tests["prime"] = is_prime - -Now it can be used in templates: - -.. sourcecode:: jinja - - {% if value is prime %} - {{ value }} is a prime number - {% else %} - {{ value }} is not a prime number - {% endif %} - -Some decorators are available to tell Jinja to pass extra information to -the filter. The object is passed as the first argument, making the value -being filtered the second argument. - -- :func:`pass_environment` passes the :class:`Environment`. -- :func:`pass_eval_context` passes the :ref:`eval-context`. -- :func:`pass_context` passes the current - :class:`~jinja2.runtime.Context`. - - -.. _eval-context: - -Evaluation Context ------------------- - -The evaluation context (short eval context or eval ctx) makes it -possible to activate and deactivate compiled features at runtime. - -Currently it is only used to enable and disable automatic escaping, but -it can be used by extensions as well. - -The ``autoescape`` setting should be checked on the evaluation context, -not the environment. The evaluation context will have the computed value -for the current template. - -Instead of ``pass_environment``: - -.. code-block:: python - - @pass_environment - def filter(env, value): - result = do_something(value) - - if env.autoescape: - result = Markup(result) - - return result - -Use ``pass_eval_context`` if you only need the setting: - -.. code-block:: python - - @pass_eval_context - def filter(eval_ctx, value): - result = do_something(value) - - if eval_ctx.autoescape: - result = Markup(result) - - return result - -Or use ``pass_context`` if you need other context behavior as well: - -.. code-block:: python - - @pass_context - def filter(context, value): - result = do_something(value) - - if context.eval_ctx.autoescape: - result = Markup(result) - - return result - -The evaluation context must not be modified at runtime. Modifications -must only happen with a :class:`nodes.EvalContextModifier` and -:class:`nodes.ScopedEvalContextModifier` from an extension, not on the -eval context object itself. - -.. autoclass:: jinja2.nodes.EvalContext - - .. attribute:: autoescape - - `True` or `False` depending on if autoescaping is active or not. - - .. attribute:: volatile - - `True` if the compiler cannot evaluate some expressions at compile - time. At runtime this should always be `False`. - - -.. _global-namespace: - -The Global Namespace --------------------- - -The global namespace stores variables and functions that should be -available without needing to pass them to :meth:`Template.render`. They -are also available to templates that are imported or included without -context. Most applications should only use :attr:`Environment.globals`. - -:attr:`Environment.globals` are intended for data that is common to all -templates loaded by that environment. :attr:`Template.globals` are -intended for data that is common to all renders of that template, and -default to :attr:`Environment.globals` unless they're given in -:meth:`Environment.get_template`, etc. Data that is specific to a -render should be passed as context to :meth:`Template.render`. - -Only one set of globals is used during any specific rendering. If -templates A and B both have template globals, and B extends A, then -only B's globals are used for both when using ``b.render()``. - -Environment globals should not be changed after loading any templates, -and template globals should not be changed at any time after loading the -template. Changing globals after loading a template will result in -unexpected behavior as they may be shared between the environment and -other templates. - - -.. _low-level-api: - -Low Level API -------------- - -The low level API exposes functionality that can be useful to understand some -implementation details, debugging purposes or advanced :ref:`extension -` techniques. Unless you know exactly what you are doing we -don't recommend using any of those. - -.. automethod:: Environment.lex - -.. automethod:: Environment.parse - -.. automethod:: Environment.preprocess - -.. automethod:: Template.new_context - -.. method:: Template.root_render_func(context) - - This is the low level render function. It's passed a :class:`Context` - that has to be created by :meth:`new_context` of the same template or - a compatible template. This render function is generated by the - compiler from the template code and returns a generator that yields - strings. - - If an exception in the template code happens the template engine will - not rewrite the exception but pass through the original one. As a - matter of fact this function should only be called from within a - :meth:`render` / :meth:`generate` / :meth:`stream` call. - -.. attribute:: Template.blocks - - A dict of block render functions. Each of these functions works exactly - like the :meth:`root_render_func` with the same limitations. - -.. attribute:: Template.is_up_to_date - - This attribute is `False` if there is a newer version of the template - available, otherwise `True`. - -.. admonition:: Note - - The low-level API is fragile. Future Jinja versions will try not to - change it in a backwards incompatible way but modifications in the Jinja - core may shine through. For example if Jinja introduces a new AST node - in later versions that may be returned by :meth:`~Environment.parse`. - -The Meta API ------------- - -.. versionadded:: 2.2 - -The meta API returns some information about abstract syntax trees that -could help applications to implement more advanced template concepts. All -the functions of the meta API operate on an abstract syntax tree as -returned by the :meth:`Environment.parse` method. - -.. autofunction:: jinja2.meta.find_undeclared_variables - -.. autofunction:: jinja2.meta.find_referenced_templates diff --git a/docs/changes.rst b/docs/changes.rst deleted file mode 100644 index 955deaf..0000000 --- a/docs/changes.rst +++ /dev/null @@ -1,4 +0,0 @@ -Changes -======= - -.. include:: ../CHANGES.rst diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index f65d462..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,53 +0,0 @@ -from pallets_sphinx_themes import get_version -from pallets_sphinx_themes import ProjectLink - -# Project -------------------------------------------------------------- - -project = "Jinja" -copyright = "2007 Pallets" -author = "Pallets" -release, version = get_version("Jinja2") - -# General -------------------------------------------------------------- - -master_doc = "index" -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.intersphinx", - "pallets_sphinx_themes", - "sphinxcontrib.log_cabinet", - "sphinx_issues", -] -autodoc_typehints = "description" -intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} -issues_github_path = "pallets/jinja" - -# HTML ----------------------------------------------------------------- - -html_theme = "jinja" -html_theme_options = {"index_sidebar_logo": False} -html_context = { - "project_links": [ - ProjectLink("Donate", "https://palletsprojects.com/donate"), - ProjectLink("PyPI Releases", "https://pypi.org/project/Jinja2/"), - ProjectLink("Source Code", "https://github.com/pallets/jinja/"), - ProjectLink("Issue Tracker", "https://github.com/pallets/jinja/issues/"), - ProjectLink("Website", "https://palletsprojects.com/p/jinja/"), - ProjectLink("Twitter", "https://twitter.com/PalletsTeam"), - ProjectLink("Chat", "https://discord.gg/pallets"), - ] -} -html_sidebars = { - "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"], - "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"], -} -singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]} -html_static_path = ["_static"] -html_favicon = "_static/jinja-logo-sidebar.png" -html_logo = "_static/jinja-logo-sidebar.png" -html_title = f"Jinja Documentation ({version})" -html_show_sourcelink = False - -# LaTeX ---------------------------------------------------------------- - -latex_documents = [(master_doc, f"Jinja-{version}.tex", html_title, author, "manual")] diff --git a/docs/examples/cache_extension.py b/docs/examples/cache_extension.py deleted file mode 100644 index 46af67c..0000000 --- a/docs/examples/cache_extension.py +++ /dev/null @@ -1,54 +0,0 @@ -from jinja2 import nodes -from jinja2.ext import Extension - - -class FragmentCacheExtension(Extension): - # a set of names that trigger the extension. - tags = {"cache"} - - def __init__(self, environment): - super().__init__(environment) - - # add the defaults to the environment - environment.extend(fragment_cache_prefix="", fragment_cache=None) - - def parse(self, parser): - # the first token is the token that started the tag. In our case - # we only listen to ``'cache'`` so this will be a name token with - # `cache` as value. We get the line number so that we can give - # that line number to the nodes we create by hand. - lineno = next(parser.stream).lineno - - # now we parse a single expression that is used as cache key. - args = [parser.parse_expression()] - - # if there is a comma, the user provided a timeout. If not use - # None as second parameter. - if parser.stream.skip_if("comma"): - args.append(parser.parse_expression()) - else: - args.append(nodes.Const(None)) - - # now we parse the body of the cache block up to `endcache` and - # drop the needle (which would always be `endcache` in that case) - body = parser.parse_statements(["name:endcache"], drop_needle=True) - - # now return a `CallBlock` node that calls our _cache_support - # helper method on this extension. - return nodes.CallBlock( - self.call_method("_cache_support", args), [], [], body - ).set_lineno(lineno) - - def _cache_support(self, name, timeout, caller): - """Helper callback.""" - key = self.environment.fragment_cache_prefix + name - - # try to load the block from the cache - # if there is no fragment in the cache, render it and store - # it in the cache. - rv = self.environment.fragment_cache.get(key) - if rv is not None: - return rv - rv = caller() - self.environment.fragment_cache.add(key, rv, timeout) - return rv diff --git a/docs/examples/inline_gettext_extension.py b/docs/examples/inline_gettext_extension.py deleted file mode 100644 index bf8b9db..0000000 --- a/docs/examples/inline_gettext_extension.py +++ /dev/null @@ -1,72 +0,0 @@ -import re - -from jinja2.exceptions import TemplateSyntaxError -from jinja2.ext import Extension -from jinja2.lexer import count_newlines -from jinja2.lexer import Token - - -_outside_re = re.compile(r"\\?(gettext|_)\(") -_inside_re = re.compile(r"\\?[()]") - - -class InlineGettext(Extension): - """This extension implements support for inline gettext blocks:: - -

_(Welcome)

-

_(This is a paragraph)

- - Requires the i18n extension to be loaded and configured. - """ - - def filter_stream(self, stream): - paren_stack = 0 - - for token in stream: - if token.type != "data": - yield token - continue - - pos = 0 - lineno = token.lineno - - while True: - if not paren_stack: - match = _outside_re.search(token.value, pos) - else: - match = _inside_re.search(token.value, pos) - if match is None: - break - new_pos = match.start() - if new_pos > pos: - preval = token.value[pos:new_pos] - yield Token(lineno, "data", preval) - lineno += count_newlines(preval) - gtok = match.group() - if gtok[0] == "\\": - yield Token(lineno, "data", gtok[1:]) - elif not paren_stack: - yield Token(lineno, "block_begin", None) - yield Token(lineno, "name", "trans") - yield Token(lineno, "block_end", None) - paren_stack = 1 - else: - if gtok == "(" or paren_stack > 1: - yield Token(lineno, "data", gtok) - paren_stack += -1 if gtok == ")" else 1 - if not paren_stack: - yield Token(lineno, "block_begin", None) - yield Token(lineno, "name", "endtrans") - yield Token(lineno, "block_end", None) - pos = match.end() - - if pos < len(token.value): - yield Token(lineno, "data", token.value[pos:]) - - if paren_stack: - raise TemplateSyntaxError( - "unclosed gettext expression", - token.lineno, - stream.name, - stream.filename, - ) diff --git a/docs/extensions.rst b/docs/extensions.rst deleted file mode 100644 index 45ead3b..0000000 --- a/docs/extensions.rst +++ /dev/null @@ -1,423 +0,0 @@ -.. _jinja-extensions: - -Extensions -========== - -Jinja supports extensions that can add extra filters, tests, globals or even -extend the parser. The main motivation of extensions is to move often used -code into a reusable class like adding support for internationalization. - - -Adding Extensions ------------------ - -Extensions are added to the Jinja environment at creation time. To add an -extension pass a list of extension classes or import paths to the -``extensions`` parameter of the :class:`~jinja2.Environment` constructor. The following -example creates a Jinja environment with the i18n extension loaded:: - - jinja_env = Environment(extensions=['jinja2.ext.i18n']) - -To add extensions after creation time, use the :meth:`~jinja2.Environment.add_extension` method:: - - jinja_env.add_extension('jinja2.ext.debug') - - -.. _i18n-extension: - -i18n Extension --------------- - -**Import name:** ``jinja2.ext.i18n`` - -The i18n extension can be used in combination with `gettext`_ or -`Babel`_. When it's enabled, Jinja provides a ``trans`` statement that -marks a block as translatable and calls ``gettext``. - -After enabling, an application has to provide functions for ``gettext``, -``ngettext``, and optionally ``pgettext`` and ``npgettext``, either -globally or when rendering. A ``_()`` function is added as an alias to -the ``gettext`` function. - - -Environment Methods -~~~~~~~~~~~~~~~~~~~ - -After enabling the extension, the environment provides the following -additional methods: - -.. method:: jinja2.Environment.install_gettext_translations(translations, newstyle=False) - - Installs a translation globally for the environment. The - ``translations`` object must implement ``gettext``, ``ngettext``, - and optionally ``pgettext`` and ``npgettext``. - :class:`gettext.NullTranslations`, :class:`gettext.GNUTranslations`, - and `Babel`_\s ``Translations`` are supported. - - .. versionchanged:: 3.0 - Added ``pgettext`` and ``npgettext``. - - .. versionchanged:: 2.5 - Added new-style gettext support. - -.. method:: jinja2.Environment.install_null_translations(newstyle=False) - - Install no-op gettext functions. This is useful if you want to - prepare the application for internationalization but don't want to - implement the full system yet. - - .. versionchanged:: 2.5 Added new-style gettext support. - -.. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False, pgettext=None, npgettext=None) - - Install the given ``gettext``, ``ngettext``, ``pgettext``, and - ``npgettext`` callables into the environment. They should behave - exactly like :func:`gettext.gettext`, :func:`gettext.ngettext`, - :func:`gettext.pgettext` and :func:`gettext.npgettext`. - - If ``newstyle`` is activated, the callables are wrapped to work like - newstyle callables. See :ref:`newstyle-gettext` for more information. - - .. versionchanged:: 3.0 - Added ``pgettext`` and ``npgettext``. - - .. versionadded:: 2.5 - Added new-style gettext support. - -.. method:: jinja2.Environment.uninstall_gettext_translations() - - Uninstall the environment's globally installed translation. - -.. method:: jinja2.Environment.extract_translations(source) - - Extract localizable strings from the given template node or source. - - For every string found this function yields a ``(lineno, function, - message)`` tuple, where: - - - ``lineno`` is the number of the line on which the string was - found. - - ``function`` is the name of the ``gettext`` function used (if - the string was extracted from embedded Python code). - - ``message`` is the string itself, or a tuple of strings for - functions with multiple arguments. - - If `Babel`_ is installed, see :ref:`babel-integration` to extract - the strings. - -For a web application that is available in multiple languages but gives -all the users the same language (for example, multilingual forum -software installed for a French community), the translation may be -installed when the environment is created. - -.. code-block:: python - - translations = get_gettext_translations() - env = Environment(extensions=["jinja2.ext.i18n"]) - env.install_gettext_translations(translations) - -The ``get_gettext_translations`` function would return the translator -for the current configuration, for example by using ``gettext.find``. - -The usage of the ``i18n`` extension for template designers is covered in -:ref:`the template documentation `. - -.. _gettext: https://docs.python.org/3/library/gettext.html -.. _Babel: https://babel.pocoo.org/ - - -Whitespace Trimming -~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.10 - -Within ``{% trans %}`` blocks, it can be useful to trim line breaks and -whitespace so that the block of text looks like a simple string with -single spaces in the translation file. - -Linebreaks and surrounding whitespace can be automatically trimmed by -enabling the ``ext.i18n.trimmed`` :ref:`policy `. - - -.. _newstyle-gettext: - -New Style Gettext -~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.5 - -New style gettext calls are less to type, less error prone, and support -autoescaping better. - -You can use "new style" gettext calls by setting -``env.newstyle_gettext = True`` or passing ``newstyle=True`` to -``env.install_translations``. They are fully supported by the Babel -extraction tool, but might not work as expected with other extraction -tools. - -With standard ``gettext`` calls, string formatting is a separate step -done with the ``|format`` filter. This requires duplicating work for -``ngettext`` calls. - -.. sourcecode:: jinja - - {{ gettext("Hello, World!") }} - {{ gettext("Hello, %(name)s!")|format(name=name) }} - {{ ngettext( - "%(num)d apple", "%(num)d apples", apples|count - )|format(num=apples|count) }} - {{ pgettext("greeting", "Hello, World!") }} - {{ npgettext( - "fruit", "%(num)d apple", "%(num)d apples", apples|count - )|format(num=apples|count) }} - -New style ``gettext`` make formatting part of the call, and behind the -scenes enforce more consistency. - -.. sourcecode:: jinja - - {{ gettext("Hello, World!") }} - {{ gettext("Hello, %(name)s!", name=name) }} - {{ ngettext("%(num)d apple", "%(num)d apples", apples|count) }} - {{ pgettext("greeting", "Hello, World!") }} - {{ npgettext("fruit", "%(num)d apple", "%(num)d apples", apples|count) }} - -The advantages of newstyle gettext are: - -- There's no separate formatting step, you don't have to remember to - use the ``|format`` filter. -- Only named placeholders are allowed. This solves a common problem - translators face because positional placeholders can't switch - positions meaningfully. Named placeholders always carry semantic - information about what value goes where. -- String formatting is used even if no placeholders are used, which - makes all strings use a consistent format. Remember to escape any - raw percent signs as ``%%``, such as ``100%%``. -- The translated string is marked safe, formatting performs escaping - as needed. Mark a parameter as ``|safe`` if it has already been - escaped. - - -Expression Statement --------------------- - -**Import name:** ``jinja2.ext.do`` - -The "do" aka expression-statement extension adds a simple ``do`` tag to the -template engine that works like a variable expression but ignores the -return value. - -.. _loopcontrols-extension: - -Loop Controls -------------- - -**Import name:** ``jinja2.ext.loopcontrols`` - -This extension adds support for ``break`` and ``continue`` in loops. After -enabling, Jinja provides those two keywords which work exactly like in -Python. - -.. _with-extension: - -With Statement --------------- - -**Import name:** ``jinja2.ext.with_`` - -.. versionchanged:: 2.9 - - This extension is now built-in and no longer does anything. - -.. _autoescape-extension: - -Autoescape Extension --------------------- - -**Import name:** ``jinja2.ext.autoescape`` - -.. versionchanged:: 2.9 - - This extension was removed and is now built-in. Enabling the - extension no longer does anything. - - -.. _debug-extension: - -Debug Extension ---------------- - -**Import name:** ``jinja2.ext.debug`` - -Adds a ``{% debug %}`` tag to dump the current context as well as the -available filters and tests. This is useful to see what's available to -use in the template without setting up a debugger. - - -.. _writing-extensions: - -Writing Extensions ------------------- - -.. module:: jinja2.ext - -By writing extensions you can add custom tags to Jinja. This is a non-trivial -task and usually not needed as the default tags and expressions cover all -common use cases. The i18n extension is a good example of why extensions are -useful. Another one would be fragment caching. - -When writing extensions you have to keep in mind that you are working with the -Jinja template compiler which does not validate the node tree you are passing -to it. If the AST is malformed you will get all kinds of compiler or runtime -errors that are horrible to debug. Always make sure you are using the nodes -you create correctly. The API documentation below shows which nodes exist and -how to use them. - - -Example Extensions ------------------- - -Cache -~~~~~ - -The following example implements a ``cache`` tag for Jinja by using the -`cachelib`_ library: - -.. literalinclude:: examples/cache_extension.py - :language: python - -And here is how you use it in an environment:: - - from jinja2 import Environment - from cachelib import SimpleCache - - env = Environment(extensions=[FragmentCacheExtension]) - env.fragment_cache = SimpleCache() - -Inside the template it's then possible to mark blocks as cacheable. The -following example caches a sidebar for 300 seconds: - -.. sourcecode:: html+jinja - - {% cache 'sidebar', 300 %} - - {% endcache %} - -.. _cachelib: https://github.com/pallets/cachelib - - -Inline ``gettext`` -~~~~~~~~~~~~~~~~~~ - -The following example demonstrates using :meth:`Extension.filter_stream` -to parse calls to the ``_()`` gettext function inline with static data -without needing Jinja blocks. - -.. code-block:: html - -

_(Welcome)

-

_(This is a paragraph)

- -It requires the i18n extension to be loaded and configured. - -.. literalinclude:: examples/inline_gettext_extension.py - :language: python - - -Extension API -------------- - -Extension -~~~~~~~~~ - -Extensions always have to extend the :class:`jinja2.ext.Extension` class: - -.. autoclass:: Extension - :members: preprocess, filter_stream, parse, attr, call_method - - .. attribute:: identifier - - The identifier of the extension. This is always the true import name - of the extension class and must not be changed. - - .. attribute:: tags - - If the extension implements custom tags this is a set of tag names - the extension is listening for. - - -Parser -~~~~~~ - -The parser passed to :meth:`Extension.parse` provides ways to parse -expressions of different types. The following methods may be used by -extensions: - -.. autoclass:: jinja2.parser.Parser - :members: parse_expression, parse_tuple, parse_assign_target, - parse_statements, free_identifier, fail - - .. attribute:: filename - - The filename of the template the parser processes. This is **not** - the load name of the template. For the load name see :attr:`name`. - For templates that were not loaded form the file system this is - ``None``. - - .. attribute:: name - - The load name of the template. - - .. attribute:: stream - - The current :class:`~jinja2.lexer.TokenStream` - -.. autoclass:: jinja2.lexer.TokenStream - :members: push, look, eos, skip, __next__, next_if, skip_if, expect - - .. attribute:: current - - The current :class:`~jinja2.lexer.Token`. - -.. autoclass:: jinja2.lexer.Token - :members: test, test_any - - .. attribute:: lineno - - The line number of the token - - .. attribute:: type - - The type of the token. This string is interned so you may compare - it with arbitrary strings using the ``is`` operator. - - .. attribute:: value - - The value of the token. - -There is also a utility function in the lexer module that can count newline -characters in strings: - -.. autofunction:: jinja2.lexer.count_newlines - - -AST -~~~ - -The AST (Abstract Syntax Tree) is used to represent a template after parsing. -It's build of nodes that the compiler then converts into executable Python -code objects. Extensions that provide custom statements can return nodes to -execute custom Python code. - -The list below describes all nodes that are currently available. The AST may -change between Jinja versions but will stay backwards compatible. - -For more information have a look at the repr of :meth:`jinja2.Environment.parse`. - -.. module:: jinja2.nodes - -.. jinja:nodes:: jinja2.nodes.Node - -.. autoexception:: Impossible diff --git a/docs/faq.rst b/docs/faq.rst deleted file mode 100644 index 493dc38..0000000 --- a/docs/faq.rst +++ /dev/null @@ -1,75 +0,0 @@ -Frequently Asked Questions -========================== - - -Why is it called Jinja? ------------------------ - -"Jinja" is a Japanese `Shinto shrine`_, or temple, and temple and -template share a similar English pronunciation. It is not named after -the `city in Uganda`_. - -.. _Shinto shrine: https://en.wikipedia.org/wiki/Shinto_shrine -.. _city in Uganda: https://en.wikipedia.org/wiki/Jinja%2C_Uganda - - -How fast is Jinja? ------------------- - -Jinja is relatively fast among template engines because it compiles and -caches template code to Python code, so that the template does not need -to be parsed and interpreted each time. Rendering a template becomes as -close to executing a Python function as possible. - -Jinja also makes extensive use of caching. Templates are cached by name -after loading, so future uses of the template avoid loading. The -template loading itself uses a bytecode cache to avoid repeated -compiling. The caches can be external to persist across restarts. -Templates can also be precompiled and loaded as fast Python imports. - -We dislike benchmarks because they don't reflect real use. Performance -depends on many factors. Different engines have different default -configurations and tradeoffs that make it unclear how to set up a useful -comparison. Often, database access, API calls, and data processing have -a much larger effect on performance than the template engine. - - -Isn't it a bad idea to put logic in templates? ----------------------------------------------- - -Without a doubt you should try to remove as much logic from templates as -possible. With less logic, the template is easier to understand, has -fewer potential side effects, and is faster to compile and render. But a -template without any logic means processing must be done in code before -rendering. A template engine that does that is shipped with Python, -called :class:`string.Template`, and while it's definitely fast it's not -convenient. - -Jinja's features such as blocks, statements, filters, and function calls -make it much easier to write expressive templates, with very few -restrictions. Jinja doesn't allow arbitrary Python code in templates, or -every feature available in the Python language. This keeps the engine -easier to maintain, and keeps templates more readable. - -Some amount of logic is required in templates to keep everyone happy. -Too much logic in the template can make it complex to reason about and -maintain. It's up to you to decide how your application will work and -balance how much logic you want to put in the template. - - -Why is HTML escaping not the default? -------------------------------------- - -Jinja provides a feature that can be enabled to escape HTML syntax in -rendered templates. However, it is disabled by default. - -Jinja is a general purpose template engine, it is not only used for HTML -documents. You can generate plain text, LaTeX, emails, CSS, JavaScript, -configuration files, etc. HTML escaping wouldn't make sense for any of -these document types. - -While automatic escaping means that you are less likely have an XSS -problem, it also requires significant extra processing during compiling -and rendering, which can reduce performance. Jinja uses MarkupSafe for -escaping, which provides optimized C code for speed, but it still -introduces overhead to track escaping across methods and formatting. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 4ce2071..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. rst-class:: hide-header - -Jinja -===== - -.. image:: _static/jinja-logo.png - :align: center - :target: https://palletsprojects.com/p/jinja/ - -Jinja is a fast, expressive, extensible templating engine. Special -placeholders in the template allow writing code similar to Python -syntax. Then the template is passed data to render the final document. - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - intro - api - sandbox - nativetypes - templates - extensions - integration - switching - tricks - faq - license - changes diff --git a/docs/integration.rst b/docs/integration.rst deleted file mode 100644 index b945fb3..0000000 --- a/docs/integration.rst +++ /dev/null @@ -1,94 +0,0 @@ -Integration -=========== - - -Flask ------ - -The `Flask`_ web application framework, also maintained by Pallets, uses -Jinja templates by default. Flask sets up a Jinja environment and -template loader for you, and provides functions to easily render -templates from view functions. - -.. _Flask: https://flask.palletsprojects.com - - -Django ------- - -Django supports using Jinja as its template engine, see -https://docs.djangoproject.com/en/stable/topics/templates/#support-for-template-engines. - - -.. _babel-integration: - -Babel ------ - -Jinja provides support for extracting gettext messages from templates -via a `Babel`_ extractor entry point called -``jinja2.ext.babel_extract``. The support is implemented as part of the -:ref:`i18n-extension` extension. - -Gettext messages are extracted from both ``trans`` tags and code -expressions. - -To extract gettext messages from templates, the project needs a Jinja -section in its Babel extraction method `mapping file`_: - -.. sourcecode:: ini - - [jinja2: **/templates/**.html] - encoding = utf-8 - -The syntax related options of the :class:`Environment` are also -available as configuration values in the mapping file. For example, to -tell the extractor that templates use ``%`` as -``line_statement_prefix`` you can use this code: - -.. sourcecode:: ini - - [jinja2: **/templates/**.html] - encoding = utf-8 - line_statement_prefix = % - -:ref:`jinja-extensions` may also be defined by passing a comma separated -list of import paths as the ``extensions`` value. The i18n extension is -added automatically. - -Template syntax errors are ignored by default. The assumption is that -tests will catch syntax errors in templates. If you don't want to ignore -errors, add ``silent = false`` to the settings. - -.. _Babel: https://babel.readthedocs.io/ -.. _mapping file: https://babel.readthedocs.io/en/latest/messages.html#extraction-method-mapping-and-configuration - - -Pylons ------- - -It's easy to integrate Jinja into a `Pylons`_ application. - -The template engine is configured in ``config/environment.py``. The -configuration for Jinja looks something like this: - -.. code-block:: python - - from jinja2 import Environment, PackageLoader - config['pylons.app_globals'].jinja_env = Environment( - loader=PackageLoader('yourapplication', 'templates') - ) - -After that you can render Jinja templates by using the ``render_jinja`` -function from the ``pylons.templating`` module. - -Additionally it's a good idea to set the Pylons ``c`` object to strict -mode. By default attribute access on missing attributes on the ``c`` -object returns an empty string and not an undefined object. To change -this add this to ``config/environment.py``: - -.. code-block:: python - - config['pylons.strict_c'] = True - -.. _Pylons: https://pylonshq.com/ diff --git a/docs/intro.rst b/docs/intro.rst deleted file mode 100644 index fd6f84f..0000000 --- a/docs/intro.rst +++ /dev/null @@ -1,63 +0,0 @@ -Introduction -============ - -Jinja is a fast, expressive, extensible templating engine. Special -placeholders in the template allow writing code similar to Python -syntax. Then the template is passed data to render the final document. - -It includes: - -- Template inheritance and inclusion. -- Define and import macros within templates. -- HTML templates can use autoescaping to prevent XSS from untrusted - user input. -- A sandboxed environment can safely render untrusted templates. -- Async support for generating templates that automatically handle - sync and async functions without extra syntax. -- I18N support with Babel. -- Templates are compiled to optimized Python code just-in-time and - cached, or can be compiled ahead-of-time. -- Exceptions point to the correct line in templates to make debugging - easier. -- Extensible filters, tests, functions, and even syntax. - -Jinja's philosophy is that while application logic belongs in Python if -possible, it shouldn't make the template designer's job difficult by -restricting functionality too much. - - -Installation ------------- - -We recommend using the latest version of Python. Jinja supports Python -3.7 and newer. We also recommend using a `virtual environment`_ in order -to isolate your project dependencies from other projects and the system. - -.. _virtual environment: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments - -Install the most recent Jinja version using pip: - -.. code-block:: text - - $ pip install Jinja2 - - -Dependencies -~~~~~~~~~~~~ - -These will be installed automatically when installing Jinja. - -- `MarkupSafe`_ escapes untrusted input when rendering templates to - avoid injection attacks. - -.. _MarkupSafe: https://markupsafe.palletsprojects.com/ - - -Optional Dependencies -~~~~~~~~~~~~~~~~~~~~~ - -These distributions will not be installed automatically. - -- `Babel`_ provides translation support in templates. - -.. _Babel: https://babel.pocoo.org/ diff --git a/docs/license.rst b/docs/license.rst deleted file mode 100644 index a53a98c..0000000 --- a/docs/license.rst +++ /dev/null @@ -1,4 +0,0 @@ -BSD-3-Clause License -==================== - -.. include:: ../LICENSE.rst diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index b162255..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/nativetypes.rst b/docs/nativetypes.rst deleted file mode 100644 index 1a08700..0000000 --- a/docs/nativetypes.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. module:: jinja2.nativetypes - -.. _nativetypes: - -Native Python Types -=================== - -The default :class:`~jinja2.Environment` renders templates to strings. With -:class:`NativeEnvironment`, rendering a template produces a native Python type. -This is useful if you are using Jinja outside the context of creating text -files. For example, your code may have an intermediate step where users may use -templates to define values that will then be passed to a traditional string -environment. - -Examples --------- - -Adding two values results in an integer, not a string with a number: - ->>> env = NativeEnvironment() ->>> t = env.from_string('{{ x + y }}') ->>> result = t.render(x=4, y=2) ->>> print(result) -6 ->>> print(type(result)) -int - -Rendering list syntax produces a list: - ->>> t = env.from_string('[{% for item in data %}{{ item + 1 }},{% endfor %}]') ->>> result = t.render(data=range(5)) ->>> print(result) -[1, 2, 3, 4, 5] ->>> print(type(result)) -list - -Rendering something that doesn't look like a Python literal produces a string: - ->>> t = env.from_string('{{ x }} * {{ y }}') ->>> result = t.render(x=4, y=2) ->>> print(result) -4 * 2 ->>> print(type(result)) -str - -Rendering a Python object produces that object as long as it is the only node: - ->>> class Foo: -... def __init__(self, value): -... self.value = value -... ->>> result = env.from_string('{{ x }}').render(x=Foo(15)) ->>> print(type(result).__name__) -Foo ->>> print(result.value) -15 - -API ---- - -.. autoclass:: NativeEnvironment([options]) - -.. autoclass:: NativeTemplate([options]) - :members: render diff --git a/docs/sandbox.rst b/docs/sandbox.rst deleted file mode 100644 index fc9c31f..0000000 --- a/docs/sandbox.rst +++ /dev/null @@ -1,111 +0,0 @@ -Sandbox -======= - -The Jinja sandbox can be used to render untrusted templates. Access to -attributes, method calls, operators, mutating data structures, and -string formatting can be intercepted and prohibited. - -.. code-block:: pycon - - >>> from jinja2.sandbox import SandboxedEnvironment - >>> env = SandboxedEnvironment() - >>> func = lambda: "Hello, Sandbox!" - >>> env.from_string("{{ func() }}").render(func=func) - 'Hello, Sandbox!' - >>> env.from_string("{{ func.__code__.co_code }}").render(func=func) - Traceback (most recent call last): - ... - SecurityError: access to attribute '__code__' of 'function' object is unsafe. - -A sandboxed environment can be useful, for example, to allow users of an -internal reporting system to create custom emails. You would document -what data is available in the templates, then the user would write a -template using that information. Your code would generate the report -data and pass it to the user's sandboxed template to render. - - -Security Considerations ------------------------ - -The sandbox alone is not a solution for perfect security. Keep these -things in mind when using the sandbox. - -Templates can still raise errors when compiled or rendered. Your code -should attempt to catch errors instead of crashing. - -It is possible to construct a relatively small template that renders to -a very large amount of output, which could correspond to a high use of -CPU or memory. You should run your application with limits on resources -such as CPU and memory to mitigate this. - -Jinja only renders text, it does not understand, for example, JavaScript -code. Depending on how the rendered template will be used, you may need -to do other postprocessing to restrict the output. - -Pass only the data that is relevant to the template. Avoid passing -global data, or objects with methods that have side effects. By default -the sandbox prevents private and internal attribute access. You can -override :meth:`~SandboxedEnvironment.is_safe_attribute` to further -restrict attributes access. Decorate methods with :func:`unsafe` to -prevent calling them from templates when passing objects as data. Use -:class:`ImmutableSandboxedEnvironment` to prevent modifying lists and -dictionaries. - - -API ---- - -.. module:: jinja2.sandbox - -.. autoclass:: SandboxedEnvironment([options]) - :members: is_safe_attribute, is_safe_callable, default_binop_table, - default_unop_table, intercepted_binops, intercepted_unops, - call_binop, call_unop - -.. autoclass:: ImmutableSandboxedEnvironment([options]) - -.. autoexception:: SecurityError - -.. autofunction:: unsafe - -.. autofunction:: is_internal_attribute - -.. autofunction:: modifies_known_mutable - - -Operator Intercepting ---------------------- - -For performance, Jinja outputs operators directly when compiling. This -means it's not possible to intercept operator behavior by overriding -:meth:`SandboxEnvironment.call ` by default, because -operator special methods are handled by the Python interpreter, and -might not correspond with exactly one method depending on the operator's -use. - -The sandbox can instruct the compiler to output a function to intercept -certain operators instead. Override -:attr:`SandboxedEnvironment.intercepted_binops` and -:attr:`SandboxedEnvironment.intercepted_unops` with the operator symbols -you want to intercept. The compiler will replace the symbols with calls -to :meth:`SandboxedEnvironment.call_binop` and -:meth:`SandboxedEnvironment.call_unop` instead. The default -implementation of those methods will use -:attr:`SandboxedEnvironment.binop_table` and -:attr:`SandboxedEnvironment.unop_table` to translate operator symbols -into :mod:`operator` functions. - -For example, the power (``**``) operator can be disabled: - -.. code-block:: python - - from jinja2.sandbox import SandboxedEnvironment - - class MyEnvironment(SandboxedEnvironment): - intercepted_binops = frozenset(["**"]) - - def call_binop(self, context, operator, left, right): - if operator == "**": - return self.undefined("The power (**) operator is unavailable.") - - return super().call_binop(self, context, operator, left, right) diff --git a/docs/switching.rst b/docs/switching.rst deleted file mode 100644 index caa35c3..0000000 --- a/docs/switching.rst +++ /dev/null @@ -1,181 +0,0 @@ -Switching From Other Template Engines -===================================== - -This is a brief guide on some of the differences between Jinja syntax -and other template languages. See :doc:`/templates` for a comprehensive -guide to Jinja syntax and features. - - -Django ------- - -If you have previously worked with Django templates, you should find -Jinja very familiar. Many of the syntax elements look and work the same. -However, Jinja provides some more syntax elements, and some work a bit -differently. - -This section covers the template changes. The API, including extension -support, is fundamentally different so it won't be covered here. - -Django supports using Jinja as its template engine, see -https://docs.djangoproject.com/en/stable/topics/templates/#support-for-template-engines. - - -Method Calls -~~~~~~~~~~~~ - -In Django, methods are called implicitly, without parentheses. - -.. code-block:: django - - {% for page in user.get_created_pages %} - ... - {% endfor %} - -In Jinja, using parentheses is required for calls, like in Python. This -allows you to pass variables to the method, which is not possible -in Django. This syntax is also used for calling macros. - -.. code-block:: jinja - - {% for page in user.get_created_pages() %} - ... - {% endfor %} - - -Filter Arguments -~~~~~~~~~~~~~~~~ - -In Django, one literal value can be passed to a filter after a colon. - -.. code-block:: django - - {{ items|join:", " }} - -In Jinja, filters can take any number of positional and keyword -arguments in parentheses, like function calls. Arguments can also be -variables instead of literal values. - -.. code-block:: jinja - - {{ items|join(", ") }} - - -Tests -~~~~~ - -In addition to filters, Jinja also has "tests" used with the ``is`` -operator. This operator is not the same as the Python operator. - -.. code-block:: jinja - - {% if user.user_id is odd %} - {{ user.username|e }} is odd - {% else %} - hmm. {{ user.username|e }} looks pretty normal - {% endif %} - -Loops -~~~~~ - -In Django, the special variable for the loop context is called -``forloop``, and the ``empty`` is used for no loop items. - -.. code-block:: django - - {% for item in items %} - {{ item }} - {% empty %} - No items! - {% endfor %} - -In Jinja, the special variable for the loop context is called ``loop``, -and the ``else`` block is used for no loop items. - -.. code-block:: jinja - - {% for item in items %} - {{ loop.index}}. {{ item }} - {% else %} - No items! - {% endfor %} - - -Cycle -~~~~~ - -In Django, the ``{% cycle %}`` can be used in a for loop to alternate -between values per loop. - -.. code-block:: django - - {% for user in users %} -
  • {{ user }}
  • - {% endfor %} - -In Jinja, the ``loop`` context has a ``cycle`` method. - -.. code-block:: jinja - - {% for user in users %} -
  • {{ user }}
  • - {% endfor %} - -A cycler can also be assigned to a variable and used outside or across -loops with the ``cycle()`` global function. - - -Mako ----- - -You can configure Jinja to look more like Mako: - -.. code-block:: python - - env = Environment( - block_start_string="<%", - block_end_string="%>", - variable_start_string="${", - variable_end_string="}", - comment_start_string="<%doc>", - commend_end_string="", - line_statement_prefix="%", - line_comment_prefix="##", - ) - -With an environment configured like that, Jinja should be able to -interpret a small subset of Mako templates without any changes. - -Jinja does not support embedded Python code, so you would have to move -that out of the template. You could either process the data with the -same code before rendering, or add a global function or filter to the -Jinja environment. - -The syntax for defs (which are called macros in Jinja) and template -inheritance is different too. - -The following Mako template: - -.. code-block:: mako - - <%inherit file="layout.html" /> - <%def name="title()">Page Title -
      - % for item in list: -
    • ${item}
    • - % endfor -
    - -Looks like this in Jinja with the above configuration: - -.. code-block:: jinja - - <% extends "layout.html" %> - <% block title %>Page Title<% endblock %> - <% block body %> -
      - % for item in list: -
    • ${item}
    • - % endfor -
    - <% endblock %> diff --git a/docs/templates.rst b/docs/templates.rst deleted file mode 100644 index 7a64750..0000000 --- a/docs/templates.rst +++ /dev/null @@ -1,1949 +0,0 @@ -.. py:currentmodule:: jinja2 -.. highlight:: html+jinja - -Template Designer Documentation -=============================== - -This document describes the syntax and semantics of the template engine and -will be most useful as reference to those creating Jinja templates. As the -template engine is very flexible, the configuration from the application can -be slightly different from the code presented here in terms of delimiters and -behavior of undefined values. - - -Synopsis --------- - -A Jinja template is simply a text file. Jinja can generate any text-based -format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn't need to have a -specific extension: ``.html``, ``.xml``, or any other extension is just fine. - -A template contains **variables** and/or **expressions**, which get replaced -with values when a template is *rendered*; and **tags**, which control the -logic of the template. The template syntax is heavily inspired by Django and -Python. - -Below is a minimal template that illustrates a few basics using the default -Jinja configuration. We will cover the details later in this document:: - - - - - My Webpage - - - - -

    My Webpage

    - {{ a_variable }} - - {# a comment #} - - - -The following example shows the default configuration settings. An application -developer can change the syntax configuration from ``{% foo %}`` to ``<% foo -%>``, or something similar. - -There are a few kinds of delimiters. The default Jinja delimiters are -configured as follows: - -* ``{% ... %}`` for :ref:`Statements ` -* ``{{ ... }}`` for :ref:`Expressions` to print to the template output -* ``{# ... #}`` for :ref:`Comments` not included in the template output - -:ref:`Line Statements and Comments ` are also possible, -though they don't have default prefix characters. To use them, set -``line_statement_prefix`` and ``line_comment_prefix`` when creating the -:class:`~jinja2.Environment`. - - -Template File Extension -~~~~~~~~~~~~~~~~~~~~~~~ - -As stated above, any file can be loaded as a template, regardless of -file extension. Adding a ``.jinja`` extension, like ``user.html.jinja`` -may make it easier for some IDEs or editor plugins, but is not required. -Autoescaping, introduced later, can be applied based on file extension, -so you'll need to take the extra suffix into account in that case. - -Another good heuristic for identifying templates is that they are in a -``templates`` folder, regardless of extension. This is a common layout -for projects. - - -.. _variables: - -Variables ---------- - -Template variables are defined by the context dictionary passed to the -template. - -You can mess around with the variables in templates provided they are passed in -by the application. Variables may have attributes or elements on them you can -access too. What attributes a variable has depends heavily on the application -providing that variable. - -You can use a dot (``.``) to access attributes of a variable in addition -to the standard Python ``__getitem__`` "subscript" syntax (``[]``). - -The following lines do the same thing:: - - {{ foo.bar }} - {{ foo['bar'] }} - -It's important to know that the outer double-curly braces are *not* part of the -variable, but the print statement. If you access variables inside tags don't -put the braces around them. - -If a variable or attribute does not exist, you will get back an undefined -value. What you can do with that kind of value depends on the application -configuration: the default behavior is to evaluate to an empty string if -printed or iterated over, and to fail for every other operation. - -.. _notes-on-subscriptions: - -.. admonition:: Implementation - - For the sake of convenience, ``foo.bar`` in Jinja does the following - things on the Python layer: - - - check for an attribute called `bar` on `foo` - (``getattr(foo, 'bar')``) - - if there is not, check for an item ``'bar'`` in `foo` - (``foo.__getitem__('bar')``) - - if there is not, return an undefined object. - - ``foo['bar']`` works mostly the same with a small difference in sequence: - - - check for an item ``'bar'`` in `foo`. - (``foo.__getitem__('bar')``) - - if there is not, check for an attribute called `bar` on `foo`. - (``getattr(foo, 'bar')``) - - if there is not, return an undefined object. - - This is important if an object has an item and attribute with the same - name. Additionally, the :func:`attr` filter only looks up attributes. - -.. _filters: - -Filters -------- - -Variables can be modified by **filters**. Filters are separated from the -variable by a pipe symbol (``|``) and may have optional arguments in -parentheses. Multiple filters can be chained. The output of one filter is -applied to the next. - -For example, ``{{ name|striptags|title }}`` will remove all HTML Tags from -variable `name` and title-case the output (``title(striptags(name))``). - -Filters that accept arguments have parentheses around the arguments, just like -a function call. For example: ``{{ listx|join(', ') }}`` will join a list with -commas (``str.join(', ', listx)``). - -The :ref:`builtin-filters` below describes all the builtin filters. - -.. _tests: - -Tests ------ - -Beside filters, there are also so-called "tests" available. Tests can be used -to test a variable against a common expression. To test a variable or -expression, you add `is` plus the name of the test after the variable. For -example, to find out if a variable is defined, you can do ``name is defined``, -which will then return true or false depending on whether `name` is defined -in the current template context. - -Tests can accept arguments, too. If the test only takes one argument, you can -leave out the parentheses. For example, the following two -expressions do the same thing:: - - {% if loop.index is divisibleby 3 %} - {% if loop.index is divisibleby(3) %} - -The :ref:`builtin-tests` below describes all the builtin tests. - - -.. _comments: - -Comments --------- - -To comment-out part of a line in a template, use the comment syntax which is -by default set to ``{# ... #}``. This is useful to comment out parts of the -template for debugging or to add information for other template designers or -yourself:: - - {# note: commented-out template because we no longer use this - {% for user in users %} - ... - {% endfor %} - #} - - -Whitespace Control ------------------- - -In the default configuration: - -* a single trailing newline is stripped if present -* other whitespace (spaces, tabs, newlines etc.) is returned unchanged - -If an application configures Jinja to `trim_blocks`, the first newline after a -template tag is removed automatically (like in PHP). The `lstrip_blocks` -option can also be set to strip tabs and spaces from the beginning of a -line to the start of a block. (Nothing will be stripped if there are -other characters before the start of the block.) - -With both `trim_blocks` and `lstrip_blocks` enabled, you can put block tags -on their own lines, and the entire block line will be removed when -rendered, preserving the whitespace of the contents. For example, -without the `trim_blocks` and `lstrip_blocks` options, this template:: - -
    - {% if True %} - yay - {% endif %} -
    - -gets rendered with blank lines inside the div:: - -
    - - yay - -
    - -But with both `trim_blocks` and `lstrip_blocks` enabled, the template block -lines are removed and other whitespace is preserved:: - -
    - yay -
    - -You can manually disable the `lstrip_blocks` behavior by putting a -plus sign (``+``) at the start of a block:: - -
    - {%+ if something %}yay{% endif %} -
    - -Similarly, you can manually disable the ``trim_blocks`` behavior by -putting a plus sign (``+``) at the end of a block:: - -
    - {% if something +%} - yay - {% endif %} -
    - -You can also strip whitespace in templates by hand. If you add a minus -sign (``-``) to the start or end of a block (e.g. a :ref:`for-loop` tag), a -comment, or a variable expression, the whitespaces before or after -that block will be removed:: - - {% for item in seq -%} - {{ item }} - {%- endfor %} - -This will yield all elements without whitespace between them. If `seq` was -a list of numbers from ``1`` to ``9``, the output would be ``123456789``. - -If :ref:`line-statements` are enabled, they strip leading whitespace -automatically up to the beginning of the line. - -By default, Jinja also removes trailing newlines. To keep single -trailing newlines, configure Jinja to `keep_trailing_newline`. - -.. admonition:: Note - - You must not add whitespace between the tag and the minus sign. - - **valid**:: - - {%- if foo -%}...{% endif %} - - **invalid**:: - - {% - if foo - %}...{% endif %} - - -Escaping --------- - -It is sometimes desirable -- even necessary -- to have Jinja ignore parts -it would otherwise handle as variables or blocks. For example, if, with -the default syntax, you want to use ``{{`` as a raw string in a template and -not start a variable, you have to use a trick. - -The easiest way to output a literal variable delimiter (``{{``) is by using a -variable expression:: - - {{ '{{' }} - -For bigger sections, it makes sense to mark a block `raw`. For example, to -include example Jinja syntax in a template, you can use this snippet:: - - {% raw %} -
      - {% for item in seq %} -
    • {{ item }}
    • - {% endfor %} -
    - {% endraw %} - -.. admonition:: Note - - Minus sign at the end of ``{% raw -%}`` tag cleans all the spaces and newlines - preceding the first character of your raw data. - - -.. _line-statements: - -Line Statements ---------------- - -If line statements are enabled by the application, it's possible to mark a -line as a statement. For example, if the line statement prefix is configured -to ``#``, the following two examples are equivalent:: - -
      - # for item in seq -
    • {{ item }}
    • - # endfor -
    - -
      - {% for item in seq %} -
    • {{ item }}
    • - {% endfor %} -
    - -The line statement prefix can appear anywhere on the line as long as no text -precedes it. For better readability, statements that start a block (such as -`for`, `if`, `elif` etc.) may end with a colon:: - - # for item in seq: - ... - # endfor - - -.. admonition:: Note - - Line statements can span multiple lines if there are open parentheses, - braces or brackets:: - -
      - # for href, caption in [('index.html', 'Index'), - ('about.html', 'About')]: -
    • {{ caption }}
    • - # endfor -
    - -Since Jinja 2.2, line-based comments are available as well. For example, if -the line-comment prefix is configured to be ``##``, everything from ``##`` to -the end of the line is ignored (excluding the newline sign):: - - # for item in seq: -
  • {{ item }}
  • ## this comment is ignored - # endfor - - -.. _template-inheritance: - -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:: - - - - - {% block head %} - - {% block title %}{% endblock %} - My Webpage - {% 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 tell the template engine that a -child template may override those placeholders in the template. - -``block`` tags can be inside other blocks such as ``if``, but they will -always be executed regardless of if the ``if`` block is actually -rendered. - -Child Template -~~~~~~~~~~~~~~ - -A child template might look like this:: - - {% extends "base.html" %} - {% block title %}Index{% endblock %} - {% block head %} - {{ super() }} - - {% endblock %} - {% block content %} -

    Index

    -

    - Welcome to my awesome 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, it first locates the parent. The extends tag should be the -first tag in the template. Everything before it is printed out normally and -may cause confusion. For details about this behavior and how to take -advantage of it, see :ref:`null-default-fallback`. Also a block will always be -filled in regardless of whether the surrounding condition is evaluated to be true -or false. - -The filename of the template depends on the template loader. For example, the -:class:`FileSystemLoader` allows you to access other templates by giving the -filename. You can access templates in subdirectories with a slash:: - - {% extends "layout/default.html" %} - -But this behavior can depend on the application embedding Jinja. Note that -since the child template doesn't define the ``footer`` block, the value from -the parent template is used instead. - -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 placeholder to fill -- it also defines the content that fills the placeholder 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. - -If you want to print a block multiple times, you can, however, use the special -`self` variable and call the block with that name:: - - {% block title %}{% endblock %} -

    {{ self.title() }}

    - {% block body %}{% endblock %} - - -Super Blocks -~~~~~~~~~~~~ - -It's possible to render the contents of the parent block by calling ``super()``. -This gives back the results of the parent block:: - - {% block sidebar %} -

    Table Of Contents

    - ... - {{ super() }} - {% endblock %} - - -Nesting extends -~~~~~~~~~~~~~~~ - -In the case of multiple levels of ``{% extends %}``, -``super`` references may be chained (as in ``super.super()``) -to skip levels in the inheritance tree. - -For example:: - - # parent.tmpl - body: {% block body %}Hi from parent.{% endblock %} - - # child.tmpl - {% extends "parent.tmpl" %} - {% block body %}Hi from child. {{ super() }}{% endblock %} - - # grandchild1.tmpl - {% extends "child.tmpl" %} - {% block body %}Hi from grandchild1.{% endblock %} - - # grandchild2.tmpl - {% extends "child.tmpl" %} - {% block body %}Hi from grandchild2. {{ super.super() }} {% endblock %} - - -Rendering ``child.tmpl`` will give -``body: Hi from child. Hi from parent.`` - -Rendering ``grandchild1.tmpl`` will give -``body: Hi from grandchild1.`` - -Rendering ``grandchild2.tmpl`` will give -``body: Hi from grandchild2. Hi from parent.`` - - -Named Block End-Tags -~~~~~~~~~~~~~~~~~~~~ - -Jinja allows you to put the name of the block after the end tag for better -readability:: - - {% block sidebar %} - {% block inner_sidebar %} - ... - {% endblock inner_sidebar %} - {% endblock sidebar %} - -However, the name after the `endblock` word must match the block name. - - -Block Nesting and Scope -~~~~~~~~~~~~~~~~~~~~~~~ - -Blocks can be nested for more complex layouts. However, per default blocks -may not access variables from outer scopes:: - - {% for item in seq %} -
  • {% block loop_item %}{{ item }}{% endblock %}
  • - {% endfor %} - -This example would output empty ``
  • `` items because `item` is unavailable -inside the block. The reason for this is that if the block is replaced by -a child template, a variable would appear that was not defined in the block or -passed to the context. - -Starting with Jinja 2.2, you can explicitly specify that variables are -available in a block by setting the block to "scoped" by adding the `scoped` -modifier to a block declaration:: - - {% for item in seq %} -
  • {% block loop_item scoped %}{{ item }}{% endblock %}
  • - {% endfor %} - -When overriding a block, the `scoped` modifier does not have to be provided. - - -Required Blocks -~~~~~~~~~~~~~~~ - -Blocks can be marked as ``required``. They must be overridden at some -point, but not necessarily by the direct child template. Required blocks -may only contain space and comments, and they cannot be rendered -directly. - -.. code-block:: jinja - :caption: ``page.txt`` - - {% block body required %}{% endblock %} - -.. code-block:: jinja - :caption: ``issue.txt`` - - {% extends "page.txt" %} - -.. code-block:: jinja - :caption: ``bug_report.txt`` - - {% extends "issue.txt" %} - {% block body %}Provide steps to demonstrate the bug.{% endblock %} - -Rendering ``page.txt`` or ``issue.txt`` will raise -``TemplateRuntimeError`` because they don't override the ``body`` block. -Rendering ``bug_report.txt`` will succeed because it does override the -block. - -When combined with ``scoped``, the ``required`` modifier must be placed -*after* the scoped modifier. Here are some valid examples: - -.. code-block:: jinja - - {% block body scoped %}{% endblock %} - {% block body required %}{% endblock %} - {% block body scoped required %}{% endblock %} - - -Template Objects -~~~~~~~~~~~~~~~~ - -``extends``, ``include``, and ``import`` can take a template object -instead of the name of a template to load. This could be useful in some -advanced situations, since you can use Python code to load a template -first and pass it in to ``render``. - -.. code-block:: python - - if debug_mode: - layout = env.get_template("debug_layout.html") - else: - layout = env.get_template("layout.html") - - user_detail = env.get_template("user/detail.html", layout=layout) - -.. code-block:: jinja - - {% extends layout %} - -Note how ``extends`` is passed the variable with the template object -that was passed to ``render``, instead of a string. - - -HTML Escaping -------------- - -When generating HTML from templates, there's always a risk that a variable will -include characters that affect the resulting HTML. There are two approaches: - -a. manually escaping each variable; or -b. automatically escaping everything by default. - -Jinja supports both. What is used depends on the application configuration. -The default configuration is no automatic escaping; for various reasons: - -- Escaping everything except for safe values will also mean that Jinja is - escaping variables known to not include HTML (e.g. numbers, booleans) - which can be a huge performance hit. - -- The information about the safety of a variable is very fragile. It could - happen that by coercing safe and unsafe values, the return value is - double-escaped HTML. - -Working with Manual Escaping -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If manual escaping is enabled, it's **your** responsibility to escape -variables if needed. What to escape? If you have a variable that *may* -include any of the following chars (``>``, ``<``, ``&``, or ``"``) you -**SHOULD** escape it unless the variable contains well-formed and trusted -HTML. Escaping works by piping the variable through the ``|e`` filter:: - - {{ user.username|e }} - -Working with Automatic Escaping -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When automatic escaping is enabled, everything is escaped by default except -for values explicitly marked as safe. Variables and expressions -can be marked as safe either in: - -a. The context dictionary by the application with - :class:`markupsafe.Markup` -b. The template, with the ``|safe`` filter. - -If a string that you marked safe is passed through other Python code -that doesn't understand that mark, it may get lost. Be aware of when -your data is marked safe and how it is processed before arriving at the -template. - -If a value has been escaped but is not marked safe, auto-escaping will -still take place and result in double-escaped characters. If you know -you have data that is already safe but not marked, be sure to wrap it in -``Markup`` or use the ``|safe`` filter. - -Jinja functions (macros, `super`, `self.BLOCKNAME`) always return template -data that is marked as safe. - -String literals in templates with automatic escaping are considered -unsafe because native Python strings are not safe. - -.. _list-of-control-structures: - -List of Control Structures --------------------------- - -A control structure refers to all those things that control the flow of a -program - conditionals (i.e. if/elif/else), for-loops, as well as things like -macros and blocks. With the default syntax, control structures appear inside -``{% ... %}`` blocks. - -.. _for-loop: - -For -~~~ - -Loop over each item in a sequence. For example, to display a list of users -provided in a variable called `users`:: - -

    Members

    -
      - {% for user in users %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    - -As variables in templates retain their object properties, it is possible to -iterate over containers like `dict`:: - -
    - {% for key, value in my_dict.items() %} -
    {{ key|e }}
    -
    {{ value|e }}
    - {% endfor %} -
    - -Python dicts may not be in the order you want to display them in. If -order matters, use the ``|dictsort`` filter. - -.. code-block:: jinja - -
    - {% for key, value in my_dict | dictsort %} -
    {{ key|e }}
    -
    {{ value|e }}
    - {% endfor %} -
    - -Inside of a for-loop block, you can access some special variables: - -+-----------------------+---------------------------------------------------+ -| Variable | Description | -+=======================+===================================================+ -| `loop.index` | The current iteration of the loop. (1 indexed) | -+-----------------------+---------------------------------------------------+ -| `loop.index0` | The current iteration of the loop. (0 indexed) | -+-----------------------+---------------------------------------------------+ -| `loop.revindex` | The number of iterations from the end of the loop | -| | (1 indexed) | -+-----------------------+---------------------------------------------------+ -| `loop.revindex0` | The number of iterations from the end of the loop | -| | (0 indexed) | -+-----------------------+---------------------------------------------------+ -| `loop.first` | True if first iteration. | -+-----------------------+---------------------------------------------------+ -| `loop.last` | True if last iteration. | -+-----------------------+---------------------------------------------------+ -| `loop.length` | The number of items in the sequence. | -+-----------------------+---------------------------------------------------+ -| `loop.cycle` | A helper function to cycle between a list of | -| | sequences. See the explanation below. | -+-----------------------+---------------------------------------------------+ -| `loop.depth` | Indicates how deep in a recursive loop | -| | the rendering currently is. Starts at level 1 | -+-----------------------+---------------------------------------------------+ -| `loop.depth0` | Indicates how deep in a recursive loop | -| | the rendering currently is. Starts at level 0 | -+-----------------------+---------------------------------------------------+ -| `loop.previtem` | The item from the previous iteration of the loop. | -| | Undefined during the first iteration. | -+-----------------------+---------------------------------------------------+ -| `loop.nextitem` | The item from the following iteration of the loop.| -| | Undefined during the last iteration. | -+-----------------------+---------------------------------------------------+ -| `loop.changed(*val)` | True if previously called with a different value | -| | (or not called at all). | -+-----------------------+---------------------------------------------------+ - -Within a for-loop, it's possible to cycle among a list of strings/variables -each time through the loop by using the special `loop.cycle` helper:: - - {% for row in rows %} -
  • {{ row }}
  • - {% endfor %} - -Since Jinja 2.1, an extra `cycle` helper exists that allows loop-unbound -cycling. For more information, have a look at the :ref:`builtin-globals`. - -.. _loop-filtering: - -Unlike in Python, it's not possible to `break` or `continue` in a loop. You -can, however, filter the sequence during iteration, which allows you to skip -items. The following example skips all the users which are hidden:: - - {% for user in users if not user.hidden %} -
  • {{ user.username|e }}
  • - {% endfor %} - -The advantage is that the special `loop` variable will count correctly; thus -not counting the users not iterated over. - -If no iteration took place because the sequence was empty or the filtering -removed all the items from the sequence, you can render a default block -by using `else`:: - -
      - {% for user in users %} -
    • {{ user.username|e }}
    • - {% else %} -
    • no users found
    • - {% endfor %} -
    - -Note that, in Python, `else` blocks are executed whenever the corresponding -loop **did not** `break`. Since Jinja loops cannot `break` anyway, -a slightly different behavior of the `else` keyword was chosen. - -It is also possible to use loops recursively. This is useful if you are -dealing with recursive data such as sitemaps or RDFa. -To use loops recursively, you basically have to add the `recursive` modifier -to the loop definition and call the `loop` variable with the new iterable -where you want to recurse. - -The following example implements a sitemap with recursive loops:: - -
      - {%- for item in sitemap recursive %} -
    • {{ item.title }} - {%- if item.children -%} - - {%- endif %}
    • - {%- endfor %} -
    - -The `loop` variable always refers to the closest (innermost) loop. If we -have more than one level of loops, we can rebind the variable `loop` by -writing `{% set outer_loop = loop %}` after the loop that we want to -use recursively. Then, we can call it using `{{ outer_loop(...) }}` - -Please note that assignments in loops will be cleared at the end of the -iteration and cannot outlive the loop scope. Older versions of Jinja had -a bug where in some circumstances it appeared that assignments would work. -This is not supported. See :ref:`assignments` for more information about -how to deal with this. - -If all you want to do is check whether some value has changed since the -last iteration or will change in the next iteration, you can use `previtem` -and `nextitem`:: - - {% for value in values %} - {% if loop.previtem is defined and value > loop.previtem %} - The value just increased! - {% endif %} - {{ value }} - {% if loop.nextitem is defined and loop.nextitem > value %} - The value will increase even more! - {% endif %} - {% endfor %} - -If you only care whether the value changed at all, using `changed` is even -easier:: - - {% for entry in entries %} - {% if loop.changed(entry.category) %} -

    {{ entry.category }}

    - {% endif %} -

    {{ entry.message }}

    - {% endfor %} - -.. _if: - -If -~~ - -The `if` statement in Jinja is comparable with the Python if statement. -In the simplest form, you can use it to test if a variable is defined, not -empty and not false:: - - {% if users %} -
      - {% for user in users %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    - {% endif %} - -For multiple branches, `elif` and `else` can be used like in Python. You can -use more complex :ref:`expressions` there, too:: - - {% if kenny.sick %} - Kenny is sick. - {% elif kenny.dead %} - You killed Kenny! You bastard!!! - {% else %} - Kenny looks okay --- so far - {% endif %} - -If can also be used as an :ref:`inline expression ` and for -:ref:`loop filtering `. - -.. _macros: - -Macros -~~~~~~ - -Macros are comparable with functions in regular programming languages. They -are useful to put often used idioms into reusable functions to not repeat -yourself ("DRY"). - -Here's a small example of a macro that renders a form element:: - - {% macro input(name, value='', type='text', size=20) -%} - - {%- endmacro %} - -The macro can then be called like a function in the namespace:: - -

    {{ input('username') }}

    -

    {{ input('password', type='password') }}

    - -If the macro was defined in a different template, you have to -:ref:`import ` it first. - -Inside macros, you have access to three special variables: - -`varargs` - If more positional arguments are passed to the macro than accepted by the - macro, they end up in the special `varargs` variable as a list of values. - -`kwargs` - Like `varargs` but for keyword arguments. All unconsumed keyword - arguments are stored in this special variable. - -`caller` - If the macro was called from a :ref:`call` tag, the caller is stored - in this variable as a callable macro. - -Macros also expose some of their internal details. The following attributes -are available on a macro object: - -`name` - The name of the macro. ``{{ input.name }}`` will print ``input``. - -`arguments` - A tuple of the names of arguments the macro accepts. - -`catch_kwargs` - This is `true` if the macro accepts extra keyword arguments (i.e.: accesses - the special `kwargs` variable). - -`catch_varargs` - This is `true` if the macro accepts extra positional arguments (i.e.: - accesses the special `varargs` variable). - -`caller` - This is `true` if the macro accesses the special `caller` variable and may - be called from a :ref:`call` tag. - -If a macro name starts with an underscore, it's not exported and can't -be imported. - -Due to how scopes work in Jinja, a macro in a child template does not -override a macro in a parent template. The following will output -"LAYOUT", not "CHILD". - -.. code-block:: jinja - :caption: ``layout.txt`` - - {% macro foo() %}LAYOUT{% endmacro %} - {% block body %}{% endblock %} - -.. code-block:: jinja - :caption: ``child.txt`` - - {% extends 'layout.txt' %} - {% macro foo() %}CHILD{% endmacro %} - {% block body %}{{ foo() }}{% endblock %} - - -.. _call: - -Call -~~~~ - -In some cases it can be useful to pass a macro to another macro. For this -purpose, you can use the special `call` block. The following example shows -a macro that takes advantage of the call functionality and how it can be -used:: - - {% macro render_dialog(title, class='dialog') -%} -
    -

    {{ title }}

    -
    - {{ caller() }} -
    -
    - {%- endmacro %} - - {% call render_dialog('Hello World') %} - This is a simple dialog rendered by using a macro and - a call block. - {% endcall %} - -It's also possible to pass arguments back to the call block. This makes it -useful as a replacement for loops. Generally speaking, a call block works -exactly like a macro without a name. - -Here's an example of how a call block can be used with arguments:: - - {% macro dump_users(users) -%} -
      - {%- for user in users %} -
    • {{ user.username|e }}

      {{ caller(user) }}
    • - {%- endfor %} -
    - {%- endmacro %} - - {% call(user) dump_users(list_of_user) %} -
    -
    Realname
    -
    {{ user.realname|e }}
    -
    Description
    -
    {{ user.description }}
    -
    - {% endcall %} - - -Filters -~~~~~~~ - -Filter sections allow you to apply regular Jinja filters on a block of -template data. Just wrap the code in the special `filter` section:: - - {% filter upper %} - This text becomes uppercase - {% endfilter %} - - -.. _assignments: - -Assignments -~~~~~~~~~~~ - -Inside code blocks, you can also assign values to variables. Assignments at -top level (outside of blocks, macros or loops) are exported from the template -like top level macros and can be imported by other templates. - -Assignments use the `set` tag and can have multiple targets:: - - {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %} - {% set key, value = call_something() %} - -.. admonition:: Scoping Behavior - - Please keep in mind that it is not possible to set variables inside a - block and have them show up outside of it. This also applies to - loops. The only exception to that rule are if statements which do not - introduce a scope. As a result the following template is not going - to do what you might expect:: - - {% set iterated = false %} - {% for item in seq %} - {{ item }} - {% set iterated = true %} - {% endfor %} - {% if not iterated %} did not iterate {% endif %} - - It is not possible with Jinja syntax to do this. Instead use - alternative constructs like the loop else block or the special `loop` - variable:: - - {% for item in seq %} - {{ item }} - {% else %} - did not iterate - {% endfor %} - - As of version 2.10 more complex use cases can be handled using namespace - objects which allow propagating of changes across scopes:: - - {% set ns = namespace(found=false) %} - {% for item in items %} - {% if item.check_something() %} - {% set ns.found = true %} - {% endif %} - * {{ item.title }} - {% endfor %} - Found item having something: {{ ns.found }} - - Note that the ``obj.attr`` notation in the `set` tag is only allowed for - namespace objects; attempting to assign an attribute on any other object - will raise an exception. - - .. versionadded:: 2.10 Added support for namespace objects - - -Block Assignments -~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.8 - -Starting with Jinja 2.8, it's possible to also use block assignments to -capture the contents of a block into a variable name. This can be useful -in some situations as an alternative for macros. In that case, instead of -using an equals sign and a value, you just write the variable name and then -everything until ``{% endset %}`` is captured. - -Example:: - - {% set navigation %} -
  • Index -
  • Downloads - {% endset %} - -The `navigation` variable then contains the navigation HTML source. - -.. versionchanged:: 2.10 - -Starting with Jinja 2.10, the block assignment supports filters. - -Example:: - - {% set reply | wordwrap %} - You wrote: - {{ message }} - {% endset %} - - -.. _extends: - -Extends -~~~~~~~ - -The `extends` tag can be used to extend one template from another. You can -have multiple `extends` tags in a file, but only one of them may be executed at -a time. - -See the section about :ref:`template-inheritance` above. - - -.. _blocks: - -Blocks -~~~~~~ - -Blocks are used for inheritance and act as both placeholders and replacements -at the same time. They are documented in detail in the -:ref:`template-inheritance` section. - - -Include -~~~~~~~ - -The ``include`` tag renders another template and outputs the result into -the current template. - -.. code-block:: jinja - - {% include 'header.html' %} - Body goes here. - {% include 'footer.html' %} - -The included template has access to context of the current template by -default. Use ``without context`` to use a separate context instead. -``with context`` is also valid, but is the default behavior. See -:ref:`import-visibility`. - -The included template can ``extend`` another template and override -blocks in that template. However, the current template cannot override -any blocks that the included template outputs. - -Use ``ignore missing`` to ignore the statement if the template does not -exist. It must be placed *before* a context visibility statement. - -.. code-block:: jinja - - {% include "sidebar.html" without context %} - {% include "sidebar.html" ignore missing %} - {% include "sidebar.html" ignore missing with context %} - {% include "sidebar.html" ignore missing without context %} - -If a list of templates is given, each will be tried in order until one -is not missing. This can be used with ``ignore missing`` to ignore if -none of the templates exist. - -.. code-block:: jinja - - {% include ['page_detailed.html', 'page.html'] %} - {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} - -A variable, with either a template name or template object, can also be -passed to the statment. - -.. _import: - -Import -~~~~~~ - -Jinja supports putting often used code into macros. These macros can go into -different templates and get imported from there. This works similarly to the -import statements in Python. It's important to know that imports are cached -and imported templates don't have access to the current template variables, -just the globals by default. For more details about context behavior of -imports and includes, see :ref:`import-visibility`. - -There are two ways to import templates. You can import a complete template -into a variable or request specific macros / exported variables from it. - -Imagine we have a helper module that renders forms (called `forms.html`):: - - {% macro input(name, value='', type='text') -%} - - {%- endmacro %} - - {%- macro textarea(name, value='', rows=10, cols=40) -%} - - {%- endmacro %} - -The easiest and most flexible way to access a template's variables -and macros is to import the whole template module into a variable. -That way, you can access the attributes:: - - {% import 'forms.html' as forms %} -
    -
    Username
    -
    {{ forms.input('username') }}
    -
    Password
    -
    {{ forms.input('password', type='password') }}
    -
    -

    {{ forms.textarea('comment') }}

    - - -Alternatively, you can import specific names from a template into the current -namespace:: - - {% from 'forms.html' import input as input_field, textarea %} -
    -
    Username
    -
    {{ input_field('username') }}
    -
    Password
    -
    {{ input_field('password', type='password') }}
    -
    -

    {{ textarea('comment') }}

    - -Macros and variables starting with one or more underscores are private and -cannot be imported. - -.. versionchanged:: 2.4 - If a template object was passed to the template context, you can - import from that object. - - -.. _import-visibility: - -Import Context Behavior ------------------------ - -By default, included templates are passed the current context and imported -templates are not. The reason for this is that imports, unlike includes, -are cached; as imports are often used just as a module that holds macros. - -This behavior can be changed explicitly: by adding `with context` -or `without context` to the import/include directive, the current context -can be passed to the template and caching is disabled automatically. - -Here are two examples:: - - {% from 'forms.html' import input with context %} - {% include 'header.html' without context %} - -.. admonition:: Note - - In Jinja 2.0, the context that was passed to the included template - did not include variables defined in the template. As a matter of - fact, this did not work:: - - {% for box in boxes %} - {% include "render_box.html" %} - {% endfor %} - - The included template ``render_box.html`` is *not* able to access - `box` in Jinja 2.0. As of Jinja 2.1, ``render_box.html`` *is* able - to do so. - - -.. _expressions: - -Expressions ------------ - -Jinja allows basic expressions everywhere. These work very similarly to -regular Python; even if you're not working with Python -you should feel comfortable with it. - -Literals -~~~~~~~~ - -The simplest form of expressions are literals. Literals are representations -for Python objects such as strings and numbers. The following literals exist: - -``"Hello World"`` - Everything between two double or single quotes is a string. They are - useful whenever you need a string in the template (e.g. as - arguments to function calls and filters, or just to extend or include a - template). - -``42`` / ``123_456`` - Integers are whole numbers without a decimal part. The '_' character - can be used to separate groups for legibility. - -``42.23`` / ``42.1e2`` / ``123_456.789`` - Floating point numbers can be written using a '.' as a decimal mark. - They can also be written in scientific notation with an upper or - lower case 'e' to indicate the exponent part. The '_' character can - be used to separate groups for legibility, but cannot be used in the - exponent part. - -``['list', 'of', 'objects']`` - Everything between two brackets is a list. Lists are useful for storing - sequential data to be iterated over. For example, you can easily - create a list of links using lists and tuples for (and with) a for loop:: - -
      - {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), - ('downloads.html', 'Downloads')] %} -
    • {{ caption }}
    • - {% endfor %} -
    - -``('tuple', 'of', 'values')`` - Tuples are like lists that cannot be modified ("immutable"). If a tuple - only has one item, it must be followed by a comma (``('1-tuple',)``). - Tuples are usually used to represent items of two or more elements. - See the list example above for more details. - -``{'dict': 'of', 'key': 'and', 'value': 'pairs'}`` - A dict in Python is a structure that combines keys and values. Keys must - be unique and always have exactly one value. Dicts are rarely used in - templates; they are useful in some rare cases such as the :func:`xmlattr` - filter. - -``true`` / ``false`` - ``true`` is always true and ``false`` is always false. - -.. admonition:: Note - - The special constants `true`, `false`, and `none` are indeed lowercase. - Because that caused confusion in the past, (`True` used to expand - to an undefined variable that was considered false), - all three can now also be written in title case - (`True`, `False`, and `None`). - However, for consistency, (all Jinja identifiers are lowercase) - you should use the lowercase versions. - -Math -~~~~ - -Jinja allows you to calculate with values. This is rarely useful in templates -but exists for completeness' sake. The following operators are supported: - -``+`` - Adds two objects together. Usually the objects are numbers, but if both are - strings or lists, you can concatenate them this way. This, however, is not - the preferred way to concatenate strings! For string concatenation, have - a look-see at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``. - -``-`` - Subtract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. - -``/`` - Divide two numbers. The return value will be a floating point number. - ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. - -``//`` - Divide two numbers and return the truncated integer result. - ``{{ 20 // 7 }}`` is ``2``. - -``%`` - Calculate the remainder of an integer division. ``{{ 11 % 7 }}`` is ``4``. - -``*`` - Multiply the left operand with the right one. ``{{ 2 * 2 }}`` would - return ``4``. This can also be used to repeat a string multiple times. - ``{{ '=' * 80 }}`` would print a bar of 80 equal signs. - -``**`` - Raise the left operand to the power of the right operand. - ``{{ 2**3 }}`` would return ``8``. - - Unlike Python, chained pow is evaluated left to right. - ``{{ 3**3**3 }}`` is evaluated as ``(3**3)**3`` in Jinja, but would - be evaluated as ``3**(3**3)`` in Python. Use parentheses in Jinja - to be explicit about what order you want. It is usually preferable - to do extended math in Python and pass the results to ``render`` - rather than doing it in the template. - - This behavior may be changed in the future to match Python, if it's - possible to introduce an upgrade path. - - -Comparisons -~~~~~~~~~~~ - -``==`` - Compares two objects for equality. - -``!=`` - Compares two objects for inequality. - -``>`` - ``true`` if the left hand side is greater than the right hand side. - -``>=`` - ``true`` if the left hand side is greater or equal to the right hand side. - -``<`` - ``true`` if the left hand side is lower than the right hand side. - -``<=`` - ``true`` if the left hand side is lower or equal to the right hand side. - -Logic -~~~~~ - -For ``if`` statements, ``for`` filtering, and ``if`` expressions, it can be useful to -combine multiple expressions: - -``and`` - Return true if the left and the right operand are true. - -``or`` - Return true if the left or the right operand are true. - -``not`` - negate a statement (see below). - -``(expr)`` - Parentheses group an expression. - -.. admonition:: Note - - 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).`` - - -Other Operators -~~~~~~~~~~~~~~~ - -The following operators are very useful but don't fit into any of the other -two categories: - -``in`` - Perform a sequence / mapping containment test. Returns true if the left - operand is contained in the right. ``{{ 1 in [1, 2, 3] }}`` would, for - example, return true. - -``is`` - Performs a :ref:`test `. - -``|`` (pipe, vertical bar) - Applies a :ref:`filter `. - -``~`` (tilde) - Converts all operands into strings and concatenates them. - - ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is set - to ``'John'``) ``Hello John!``. - -``()`` - Call a callable: ``{{ post.render() }}``. Inside of the parentheses you - can use positional arguments and keyword arguments like in Python: - - ``{{ post.render(user, full=true) }}``. - -``.`` / ``[]`` - Get an attribute of an object. (See :ref:`variables`) - - -.. _if-expression: - -If Expression -~~~~~~~~~~~~~ - -It is also possible to use inline `if` expressions. These are useful in some -situations. For example, you can use this to extend from one template if a -variable is defined, otherwise from the default layout template:: - - {% extends layout_template if layout_template is defined else 'default.html' %} - -The general syntax is `` if else ``. - -The `else` part is optional. If not provided, the else block implicitly -evaluates into an :class:`Undefined` object (regardless of what ``undefined`` -in the environment is set to): - -.. code-block:: jinja - - {{ "[{}]".format(page.title) if page.title }} - - -.. _python-methods: - -Python Methods -~~~~~~~~~~~~~~ - -You can also use any of the methods defined on a variable's type. -The value returned from the method invocation is used as the value of the expression. -Here is an example that uses methods defined on strings (where ``page.title`` is a string): - -.. code-block:: text - - {{ page.title.capitalize() }} - -This works for methods on user-defined types. For example, if variable -``f`` of type ``Foo`` has a method ``bar`` defined on it, you can do the -following: - -.. code-block:: text - - {{ f.bar(value) }} - -Operator methods also work as expected. For example, ``%`` implements -printf-style for strings: - -.. code-block:: text - - {{ "Hello, %s!" % name }} - -Although you should prefer the ``.format`` method for that case (which -is a bit contrived in the context of rendering a template): - -.. code-block:: text - - {{ "Hello, {}!".format(name) }} - - -.. _builtin-filters: - -List of Builtin Filters ------------------------ - -.. py:currentmodule:: jinja-filters - -.. jinja:filters:: jinja2.defaults.DEFAULT_FILTERS - - -.. _builtin-tests: - -List of Builtin Tests ---------------------- - -.. py:currentmodule:: jinja-tests - -.. jinja:tests:: jinja2.defaults.DEFAULT_TESTS - - -.. _builtin-globals: - -List of Global Functions ------------------------- - -The following functions are available in the global scope by default: - -.. py:currentmodule:: jinja-globals - -.. function:: range([start,] stop[, step]) - - Return a list containing an arithmetic progression of integers. - ``range(i, j)`` returns ``[i, i+1, i+2, ..., j-1]``; - start (!) defaults to ``0``. - When step is given, it specifies the increment (or decrement). - For example, ``range(4)`` and ``range(0, 4, 1)`` return ``[0, 1, 2, 3]``. - The end point is omitted! - These are exactly the valid indices for a list of 4 elements. - - This is useful to repeat a template block multiple times, e.g. - to fill a list. Imagine you have 7 users in the list but you want to - render three empty items to enforce a height with CSS:: - -
      - {% for user in users %} -
    • {{ user.username }}
    • - {% endfor %} - {% for number in range(10 - users|count) %} -
    • ...
    • - {% endfor %} -
    - -.. function:: lipsum(n=5, html=True, min=20, max=100) - - Generates some lorem ipsum for the template. By default, five paragraphs - of HTML are generated with each paragraph between 20 and 100 words. - If html is False, regular text is returned. This is useful to generate simple - contents for layout testing. - -.. function:: dict(\**items) - - A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same - as ``dict(foo='bar')``. - -.. class:: cycler(\*items) - - Cycle through values by yielding them one at a time, then restarting - once the end is reached. - - Similar to ``loop.cycle``, but can be used outside loops or across - multiple loops. For example, render a list of folders and files in a - list, alternating giving them "odd" and "even" classes. - - .. code-block:: html+jinja - - {% set row_class = cycler("odd", "even") %} -
      - {% for folder in folders %} -
    • {{ folder }} - {% endfor %} - {% for file in files %} -
    • {{ file }} - {% endfor %} -
    - - :param items: Each positional argument will be yielded in the order - given for each cycle. - - .. versionadded:: 2.1 - - .. method:: current - :property: - - Return the current item. Equivalent to the item that will be - returned next time :meth:`next` is called. - - .. method:: next() - - Return the current item, then advance :attr:`current` to the - next item. - - .. method:: reset() - - Resets the current item to the first item. - -.. class:: joiner(sep=', ') - - A tiny helper that can be used to "join" multiple sections. A joiner is - passed a string and will return that string every time it's called, except - the first time (in which case it returns an empty string). You can - use this to join things:: - - {% set pipe = joiner("|") %} - {% if categories %} {{ pipe() }} - Categories: {{ categories|join(", ") }} - {% endif %} - {% if author %} {{ pipe() }} - Author: {{ author() }} - {% endif %} - {% if can_edit %} {{ pipe() }} - Edit - {% endif %} - - .. versionadded:: 2.1 - -.. class:: namespace(...) - - Creates a new container that allows attribute assignment using the - ``{% set %}`` tag:: - - {% set ns = namespace() %} - {% set ns.foo = 'bar' %} - - The main purpose of this is to allow carrying a value from within a loop - body to an outer scope. Initial values can be provided as a dict, as - keyword arguments, or both (same behavior as Python's `dict` constructor):: - - {% set ns = namespace(found=false) %} - {% for item in items %} - {% if item.check_something() %} - {% set ns.found = true %} - {% endif %} - * {{ item.title }} - {% endfor %} - Found item having something: {{ ns.found }} - - .. versionadded:: 2.10 - - -Extensions ----------- - -.. py:currentmodule:: jinja2 - -The following sections cover the built-in Jinja extensions that may be -enabled by an application. An application could also provide further -extensions not covered by this documentation; in which case there should -be a separate document explaining said :ref:`extensions -`. - - -.. _i18n-in-templates: - -i18n -~~~~ - -If the :ref:`i18n-extension` is enabled, it's possible to mark text in -the template as translatable. To mark a section as translatable, use a -``trans`` block: - -.. code-block:: jinja - - {% trans %}Hello, {{ user }}!{% endtrans %} - -Inside the block, no statements are allowed, only text and simple -variable tags. - -Variable tags can only be a name, not attribute access, filters, or -other expressions. To use an expression, bind it to a name in the -``trans`` tag for use in the block. - -.. code-block:: jinja - - {% trans user=user.username %}Hello, {{ user }}!{% endtrans %} - -To bind more than one expression, separate each with a comma (``,``). - -.. code-block:: jinja - - {% trans book_title=book.title, author=author.name %} - This is {{ book_title }} by {{ author }} - {% endtrans %} - -To pluralize, specify both the singular and plural forms separated by -the ``pluralize`` tag. - -.. code-block:: jinja - - {% trans count=list|length %} - There is {{ count }} {{ name }} object. - {% pluralize %} - There are {{ count }} {{ name }} objects. - {% endtrans %} - -By default, the first variable in a block is used to determine whether -to use singular or plural form. If that isn't correct, specify the -variable used for pluralizing as a parameter to ``pluralize``. - -.. code-block:: jinja - - {% trans ..., user_count=users|length %}... - {% pluralize user_count %}...{% endtrans %} - -When translating blocks of text, whitespace and linebreaks result in -hard to read and error-prone translation strings. To avoid this, a trans -block can be marked as trimmed, which will replace all linebreaks and -the whitespace surrounding them with a single space and remove leading -and trailing whitespace. - -.. code-block:: jinja - - {% trans trimmed book_title=book.title %} - This is {{ book_title }}. - You should read it! - {% endtrans %} - -This results in ``This is %(book_title)s. You should read it!`` in the -translation file. - -If trimming is enabled globally, the ``notrimmed`` modifier can be used -to disable it for a block. - -.. versionadded:: 2.10 - The ``trimmed`` and ``notrimmed`` modifiers have been added. - -If the translation depends on the context that the message appears in, -the ``pgettext`` and ``npgettext`` functions take a ``context`` string -as the first argument, which is used to select the appropriate -translation. To specify a context with the ``{% trans %}`` tag, provide -a string as the first token after ``trans``. - -.. code-block:: jinja - - {% trans "fruit" %}apple{% endtrans %} - {% trans "fruit" trimmed count -%} - 1 apple - {%- pluralize -%} - {{ count }} apples - {%- endtrans %} - -.. versionadded:: 3.1 - A context can be passed to the ``trans`` tag to use ``pgettext`` and - ``npgettext``. - -It's possible to translate strings in expressions with these functions: - -- ``_(message)``: Alias for ``gettext``. -- ``gettext(message)``: Translate a message. -- ``ngettext(singluar, plural, n)``: Translate a singular or plural - message based on a count variable. -- ``pgettext(context, message)``: Like ``gettext()``, but picks the - translation based on the context string. -- ``npgettext(context, singular, plural, n)``: Like ``npgettext()``, - but picks the translation based on the context string. - -You can print a translated string like this: - -.. code-block:: jinja - - {{ _("Hello, World!") }} - -To use placeholders, use the ``format`` filter. - -.. code-block:: jinja - - {{ _("Hello, %(user)s!")|format(user=user.username) }} - -Always use keyword arguments to ``format``, as other languages may not -use the words in the same order. - -If :ref:`newstyle-gettext` calls are activated, using placeholders is -easier. Formatting is part of the ``gettext`` call instead of using the -``format`` filter. - -.. sourcecode:: jinja - - {{ gettext('Hello World!') }} - {{ gettext('Hello %(name)s!', name='World') }} - {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} - -The ``ngettext`` function's format string automatically receives the -count as a ``num`` parameter in addition to the given parameters. - - -Expression Statement -~~~~~~~~~~~~~~~~~~~~ - -If the expression-statement extension is loaded, a tag called `do` is available -that works exactly like the regular variable expression (``{{ ... }}``); except -it doesn't print anything. This can be used to modify lists:: - - {% do navigation.append('a string') %} - - -Loop Controls -~~~~~~~~~~~~~ - -If the application enables the :ref:`loopcontrols-extension`, it's possible to -use `break` and `continue` in loops. When `break` is reached, the loop is -terminated; if `continue` is reached, the processing is stopped and continues -with the next iteration. - -Here's a loop that skips every second item:: - - {% for user in users %} - {%- if loop.index is even %}{% continue %}{% endif %} - ... - {% endfor %} - -Likewise, a loop that stops processing after the 10th iteration:: - - {% for user in users %} - {%- if loop.index >= 10 %}{% break %}{% endif %} - {%- endfor %} - -Note that ``loop.index`` starts with 1, and ``loop.index0`` starts with 0 -(See: :ref:`for-loop`). - - -Debug Statement -~~~~~~~~~~~~~~~ - -If the :ref:`debug-extension` is enabled, a ``{% debug %}`` tag will be -available to dump the current context as well as the available filters -and tests. This is useful to see what's available to use in the template -without setting up a debugger. - -.. code-block:: html+jinja - -
    {% debug %}
    - -.. code-block:: text - - {'context': {'cycler': , - ..., - 'namespace': }, - 'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd', - ..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'], - 'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined', - ..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']} - - -With Statement -~~~~~~~~~~~~~~ - -.. versionadded:: 2.3 - -The with statement makes it possible to create a new inner scope. -Variables set within this scope are not visible outside of the scope. - -With in a nutshell:: - - {% with %} - {% set foo = 42 %} - {{ foo }} foo is 42 here - {% endwith %} - foo is not visible here any longer - -Because it is common to set variables at the beginning of the scope, -you can do that within the `with` statement. The following two examples -are equivalent:: - - {% with foo = 42 %} - {{ foo }} - {% endwith %} - - {% with %} - {% set foo = 42 %} - {{ foo }} - {% endwith %} - -An important note on scoping here. In Jinja versions before 2.9 the -behavior of referencing one variable to another had some unintended -consequences. In particular one variable could refer to another defined -in the same with block's opening statement. This caused issues with the -cleaned up scoping behavior and has since been improved. In particular -in newer Jinja versions the following code always refers to the variable -`a` from outside the `with` block:: - - {% with a={}, b=a.attribute %}...{% endwith %} - -In earlier Jinja versions the `b` attribute would refer to the results of -the first attribute. If you depend on this behavior you can rewrite it to -use the ``set`` tag:: - - {% with a={} %} - {% set b = a.attribute %} - {% endwith %} - -.. admonition:: Extension - - In older versions of Jinja (before 2.9) it was required to enable this - feature with an extension. It's now enabled by default. - -.. _autoescape-overrides: - -Autoescape Overrides --------------------- - -.. versionadded:: 2.4 - -If you want you can activate and deactivate the autoescaping from within -the templates. - -Example:: - - {% autoescape true %} - Autoescaping is active within this block - {% endautoescape %} - - {% autoescape false %} - Autoescaping is inactive within this block - {% endautoescape %} - -After an `endautoescape` the behavior is reverted to what it was before. - -.. admonition:: Extension - - In older versions of Jinja (before 2.9) it was required to enable this - feature with an extension. It's now enabled by default. diff --git a/docs/tricks.rst b/docs/tricks.rst deleted file mode 100644 index b58c5bb..0000000 --- a/docs/tricks.rst +++ /dev/null @@ -1,100 +0,0 @@ -Tips and Tricks -=============== - -.. highlight:: html+jinja - -This part of the documentation shows some tips and tricks for Jinja -templates. - - -.. _null-default-fallback: - -Null-Default Fallback ---------------------- - -Jinja supports dynamic inheritance and does not distinguish between parent -and child template as long as no `extends` tag is visited. While this leads -to the surprising behavior that everything before the first `extends` tag -including whitespace is printed out instead of being ignored, it can be used -for a neat trick. - -Usually child templates extend from one template that adds a basic HTML -skeleton. However it's possible to put the `extends` tag into an `if` tag to -only extend from the layout template if the `standalone` variable evaluates -to false which it does per default if it's not defined. Additionally a very -basic skeleton is added to the file so that if it's indeed rendered with -`standalone` set to `True` a very basic HTML skeleton is added:: - - {% if not standalone %}{% extends 'default.html' %}{% endif -%} - - {% block title %}The Page Title{% endblock %} - - {% block body %} -

    This is the page body.

    - {% endblock %} - - -Alternating Rows ----------------- - -If you want to have different styles for each row of a table or -list you can use the `cycle` method on the `loop` object:: - -
      - {% for row in rows %} -
    • {{ row }}
    • - {% endfor %} -
    - -`cycle` can take an unlimited number of strings. Each time this -tag is encountered the next item from the list is rendered. - - -Highlighting Active Menu Items ------------------------------- - -Often you want to have a navigation bar with an active navigation -item. This is really simple to achieve. Because assignments outside -of `block`\s in child templates are global and executed before the layout -template is evaluated it's possible to define the active menu item in the -child template:: - - {% extends "layout.html" %} - {% set active_page = "index" %} - -The layout template can then access `active_page`. Additionally it makes -sense to define a default for that variable:: - - {% set navigation_bar = [ - ('/', 'index', 'Index'), - ('/downloads/', 'downloads', 'Downloads'), - ('/about/', 'about', 'About') - ] -%} - {% set active_page = active_page|default('index') -%} - ... - - ... - -.. _accessing-the-parent-loop: - -Accessing the parent Loop -------------------------- - -The special `loop` variable always points to the innermost loop. If it's -desired to have access to an outer loop it's possible to alias it:: - - - {% for row in table %} - - {% set rowloop = loop %} - {% for cell in row %} - - {% endfor %} - - {% endfor %} -
    {{ cell }}
    diff --git a/src/jinja2/environment.py b/environment.py similarity index 100% rename from src/jinja2/environment.py rename to environment.py diff --git a/examples/basic/cycle.py b/examples/basic/cycle.py deleted file mode 100644 index 1f97e37..0000000 --- a/examples/basic/cycle.py +++ /dev/null @@ -1,16 +0,0 @@ -from jinja2 import Environment - -env = Environment( - line_statement_prefix="#", variable_start_string="${", variable_end_string="}" -) -print( - env.from_string( - """\ -
      -# for item in range(10) -
    • ${item}
    • -# endfor -
    \ -""" - ).render() -) diff --git a/examples/basic/debugger.py b/examples/basic/debugger.py deleted file mode 100644 index f6a9627..0000000 --- a/examples/basic/debugger.py +++ /dev/null @@ -1,6 +0,0 @@ -from jinja2 import Environment -from jinja2.loaders import FileSystemLoader - -env = Environment(loader=FileSystemLoader("templates")) -tmpl = env.get_template("broken.html") -print(tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1])) diff --git a/examples/basic/inheritance.py b/examples/basic/inheritance.py deleted file mode 100644 index 6d928df..0000000 --- a/examples/basic/inheritance.py +++ /dev/null @@ -1,13 +0,0 @@ -from jinja2 import Environment -from jinja2.loaders import DictLoader - -env = Environment( - loader=DictLoader( - { - "a": "[A[{% block body %}{% endblock %}]]", - "b": "{% extends 'a' %}{% block body %}[B]{% endblock %}", - "c": "{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}", - } - ) -) -print(env.get_template("c").render()) diff --git a/examples/basic/templates/broken.html b/examples/basic/templates/broken.html deleted file mode 100644 index 294d5c9..0000000 --- a/examples/basic/templates/broken.html +++ /dev/null @@ -1,6 +0,0 @@ -{% from 'subbroken.html' import may_break %} -
      -{% for item in seq %} -
    • {{ may_break(item) }}
    • -{% endfor %} -
    diff --git a/examples/basic/templates/subbroken.html b/examples/basic/templates/subbroken.html deleted file mode 100644 index 245eb7e..0000000 --- a/examples/basic/templates/subbroken.html +++ /dev/null @@ -1,3 +0,0 @@ -{% macro may_break(item) -%} - [{{ item / 0 }}] -{%- endmacro %} diff --git a/examples/basic/test.py b/examples/basic/test.py deleted file mode 100644 index 7a58e1a..0000000 --- a/examples/basic/test.py +++ /dev/null @@ -1,29 +0,0 @@ -from jinja2 import Environment -from jinja2.loaders import DictLoader - -env = Environment( - loader=DictLoader( - { - "child.html": """\ -{% extends default_layout or 'default.html' %} -{% include helpers = 'helpers.html' %} -{% macro get_the_answer() %}42{% endmacro %} -{% title = 'Hello World' %} -{% block body %} - {{ get_the_answer() }} - {{ helpers.conspirate() }} -{% endblock %} -""", - "default.html": """\ - -{{ title }} -{% block body %}{% endblock %} -""", - "helpers.html": """\ -{% macro conspirate() %}23{% endmacro %} -""", - } - ) -) -tmpl = env.get_template("child.html") -print(tmpl.render()) diff --git a/examples/basic/test_filter_and_linestatements.py b/examples/basic/test_filter_and_linestatements.py deleted file mode 100644 index 9bbcbca..0000000 --- a/examples/basic/test_filter_and_linestatements.py +++ /dev/null @@ -1,27 +0,0 @@ -from jinja2 import Environment - -env = Environment( - line_statement_prefix="%", variable_start_string="${", variable_end_string="}" -) -tmpl = env.from_string( - """\ -% macro foo() - ${caller(42)} -% endmacro -
      -% for item in seq -
    • ${item}
    • -% endfor -
    -% call(var) foo() - [${var}] -% endcall -% filter escape - - % for item in [1, 2, 3] - - ${item} - % endfor -% endfilter -""" -) -print(tmpl.render(seq=range(10))) diff --git a/examples/basic/test_loop_filter.py b/examples/basic/test_loop_filter.py deleted file mode 100644 index 6bd89fd..0000000 --- a/examples/basic/test_loop_filter.py +++ /dev/null @@ -1,13 +0,0 @@ -from jinja2 import Environment - -tmpl = Environment().from_string( - """\ -
      -{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %} -
    • {{ loop.index }} / {{ loop.length }}: {{ item }}
    • -{%- endfor %} -
    -if condition: {{ 1 if foo else 0 }} -""" -) -print(tmpl.render(foo=True)) diff --git a/examples/basic/translate.py b/examples/basic/translate.py deleted file mode 100644 index e659681..0000000 --- a/examples/basic/translate.py +++ /dev/null @@ -1,18 +0,0 @@ -from jinja2 import Environment - -env = Environment(extensions=["jinja2.ext.i18n"]) -env.globals["gettext"] = {"Hello %(user)s!": "Hallo %(user)s!"}.__getitem__ -env.globals["ngettext"] = lambda s, p, n: { - "%(count)s user": "%(count)d Benutzer", - "%(count)s users": "%(count)d Benutzer", -}[s if n == 1 else p] -print( - env.from_string( - """\ -{% trans %}Hello {{ user }}!{% endtrans %} -{% trans count=users|count -%} -{{ count }} user{% pluralize %}{{ count }} users -{% endtrans %} -""" - ).render(user="someone", users=[1, 2, 3]) -) diff --git a/src/jinja2/exceptions.py b/exceptions.py similarity index 100% rename from src/jinja2/exceptions.py rename to exceptions.py diff --git a/src/jinja2/ext.py b/ext.py similarity index 100% rename from src/jinja2/ext.py rename to ext.py diff --git a/src/jinja2/filters.py b/filters.py similarity index 100% rename from src/jinja2/filters.py rename to filters.py diff --git a/src/jinja2/idtracking.py b/idtracking.py similarity index 100% rename from src/jinja2/idtracking.py rename to idtracking.py diff --git a/src/jinja2/lexer.py b/lexer.py similarity index 100% rename from src/jinja2/lexer.py rename to lexer.py diff --git a/src/jinja2/loaders.py b/loaders.py similarity index 100% rename from src/jinja2/loaders.py rename to loaders.py diff --git a/src/jinja2/meta.py b/meta.py similarity index 100% rename from src/jinja2/meta.py rename to meta.py diff --git a/src/jinja2/nativetypes.py b/nativetypes.py similarity index 100% rename from src/jinja2/nativetypes.py rename to nativetypes.py diff --git a/src/jinja2/nodes.py b/nodes.py similarity index 100% rename from src/jinja2/nodes.py rename to nodes.py diff --git a/src/jinja2/optimizer.py b/optimizer.py similarity index 100% rename from src/jinja2/optimizer.py rename to optimizer.py diff --git a/src/jinja2/parser.py b/parser.py similarity index 100% rename from src/jinja2/parser.py rename to parser.py diff --git a/src/jinja2/py.typed b/py.typed similarity index 100% rename from src/jinja2/py.typed rename to py.typed diff --git a/requirements/dev.in b/requirements/dev.in deleted file mode 100644 index 99f5942..0000000 --- a/requirements/dev.in +++ /dev/null @@ -1,6 +0,0 @@ --r docs.in --r tests.in --r typing.in -pip-compile-multi -pre-commit -tox diff --git a/requirements/dev.txt b/requirements/dev.txt deleted file mode 100644 index c9bcd60..0000000 --- a/requirements/dev.txt +++ /dev/null @@ -1,60 +0,0 @@ -# SHA1:54b5b77ec8c7a0064ffa93b2fd16cb0130ba177c -# -# This file is autogenerated by pip-compile-multi -# To update, run: -# -# pip-compile-multi -# --r docs.txt --r tests.txt --r typing.txt -cfgv==3.3.1 - # via pre-commit -click==8.1.2 - # via - # pip-compile-multi - # pip-tools -distlib==0.3.4 - # via virtualenv -filelock==3.6.0 - # via - # tox - # virtualenv -identify==2.5.0 - # via pre-commit -nodeenv==1.6.0 - # via pre-commit -pep517==0.12.0 - # via pip-tools -pip-compile-multi==2.4.5 - # via -r requirements/dev.in -pip-tools==6.6.0 - # via pip-compile-multi -platformdirs==2.5.2 - # via virtualenv -pre-commit==2.18.1 - # via -r requirements/dev.in -pyyaml==6.0 - # via pre-commit -six==1.16.0 - # via - # tox - # virtualenv -toml==0.10.2 - # via - # pre-commit - # tox -toposort==1.7 - # via pip-compile-multi -tox==3.25.0 - # via -r requirements/dev.in -virtualenv==20.14.1 - # via - # pre-commit - # tox -wheel==0.37.1 - # via pip-tools - -# The following packages are considered to be unsafe in a requirements file: -# pip -# setuptools diff --git a/requirements/docs.in b/requirements/docs.in deleted file mode 100644 index 7ec501b..0000000 --- a/requirements/docs.in +++ /dev/null @@ -1,4 +0,0 @@ -Pallets-Sphinx-Themes -Sphinx -sphinx-issues -sphinxcontrib-log-cabinet diff --git a/requirements/docs.txt b/requirements/docs.txt deleted file mode 100644 index 88f6279..0000000 --- a/requirements/docs.txt +++ /dev/null @@ -1,65 +0,0 @@ -# SHA1:45c590f97fe95b8bdc755eef796e91adf5fbe4ea -# -# This file is autogenerated by pip-compile-multi -# To update, run: -# -# pip-compile-multi -# -alabaster==0.7.12 - # via sphinx -babel==2.10.1 - # via sphinx -certifi==2021.10.8 - # via requests -charset-normalizer==2.0.12 - # via requests -docutils==0.17.1 - # via sphinx -idna==3.3 - # via requests -imagesize==1.3.0 - # via sphinx -jinja2==3.1.1 - # via sphinx -markupsafe==2.1.1 - # via jinja2 -packaging==21.3 - # via - # pallets-sphinx-themes - # sphinx -pallets-sphinx-themes==2.0.2 - # via -r requirements/docs.in -pygments==2.12.0 - # via sphinx -pyparsing==3.0.8 - # via packaging -pytz==2022.1 - # via babel -requests==2.27.1 - # via sphinx -snowballstemmer==2.2.0 - # via sphinx -sphinx==4.5.0 - # via - # -r requirements/docs.in - # pallets-sphinx-themes - # sphinx-issues - # sphinxcontrib-log-cabinet -sphinx-issues==3.0.1 - # via -r requirements/docs.in -sphinxcontrib-applehelp==1.0.2 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.0 - # via sphinx -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-log-cabinet==1.0.1 - # via -r requirements/docs.in -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -urllib3==1.26.9 - # via requests diff --git a/requirements/tests.in b/requirements/tests.in deleted file mode 100644 index e079f8a..0000000 --- a/requirements/tests.in +++ /dev/null @@ -1 +0,0 @@ -pytest diff --git a/requirements/tests.txt b/requirements/tests.txt deleted file mode 100644 index 4cd3fe9..0000000 --- a/requirements/tests.txt +++ /dev/null @@ -1,23 +0,0 @@ -# SHA1:0eaa389e1fdb3a1917c0f987514bd561be5718ee -# -# This file is autogenerated by pip-compile-multi -# To update, run: -# -# pip-compile-multi -# -attrs==21.4.0 - # via pytest -iniconfig==1.1.1 - # via pytest -packaging==21.3 - # via pytest -pluggy==1.0.0 - # via pytest -py==1.11.0 - # via pytest -pyparsing==3.0.8 - # via packaging -pytest==7.1.2 - # via -r requirements/tests.in -tomli==2.0.1 - # via pytest diff --git a/requirements/typing.in b/requirements/typing.in deleted file mode 100644 index f0aa93a..0000000 --- a/requirements/typing.in +++ /dev/null @@ -1 +0,0 @@ -mypy diff --git a/requirements/typing.txt b/requirements/typing.txt deleted file mode 100644 index 2d97fef..0000000 --- a/requirements/typing.txt +++ /dev/null @@ -1,15 +0,0 @@ -# SHA1:7983aaa01d64547827c20395d77e248c41b2572f -# -# This file is autogenerated by pip-compile-multi -# To update, run: -# -# pip-compile-multi -# -mypy==0.950 - # via -r requirements/typing.in -mypy-extensions==0.4.3 - # via mypy -tomli==2.0.1 - # via mypy -typing-extensions==4.2.0 - # via mypy diff --git a/src/jinja2/runtime.py b/runtime.py similarity index 100% rename from src/jinja2/runtime.py rename to runtime.py diff --git a/src/jinja2/sandbox.py b/sandbox.py similarity index 100% rename from src/jinja2/sandbox.py rename to sandbox.py diff --git a/scripts/generate_identifier_pattern.py b/scripts/generate_identifier_pattern.py deleted file mode 100755 index 6b47953..0000000 --- a/scripts/generate_identifier_pattern.py +++ /dev/null @@ -1,74 +0,0 @@ -import itertools -import os -import re -import sys - - -def get_characters(): - """Find every Unicode character that is valid in a Python `identifier`_ but - is not matched by the regex ``\\w`` group. - - ``\\w`` matches some characters that aren't valid in identifiers, but - :meth:`str.isidentifier` will catch that later in lexing. - - All start characters are valid continue characters, so we only test for - continue characters. - - _identifier: https://docs.python.org/3/reference/lexical_analysis.html#identifiers - """ - for cp in range(sys.maxunicode + 1): - s = chr(cp) - - if ("a" + s).isidentifier() and not re.match(r"\w", s): - yield s - - -def collapse_ranges(data): - """Given a sorted list of unique characters, generate ranges representing - sequential code points. - - Source: https://stackoverflow.com/a/4629241/400617 - """ - for _, b in itertools.groupby(enumerate(data), lambda x: ord(x[1]) - x[0]): - b = list(b) - yield b[0][1], b[-1][1] - - -def build_pattern(ranges): - """Output the regex pattern for ranges of characters. - - One and two character ranges output the individual characters. - """ - out = [] - - for a, b in ranges: - if a == b: # single char - out.append(a) - elif ord(b) - ord(a) == 1: # two chars, range is redundant - out.append(a) - out.append(b) - else: - out.append(f"{a}-{b}") - - return "".join(out) - - -def main(): - """Build the regex pattern and write it to - ``jinja2/_identifier.py``. - """ - pattern = build_pattern(collapse_ranges(get_characters())) - filename = os.path.abspath( - os.path.join(os.path.dirname(__file__), "..", "src", "jinja2", "_identifier.py") - ) - - with open(filename, "w", encoding="utf8") as f: - f.write("import re\n\n") - f.write("# generated by scripts/generate_identifier_pattern.py\n") - f.write("pattern = re.compile(\n") - f.write(f' r"[\\w{pattern}]+" # noqa: B950\n') - f.write(")\n") - - -if __name__ == "__main__": - main() diff --git a/src/jinja2/tests.py b/tests.py similarity index 100% rename from src/jinja2/tests.py rename to tests.py diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index e225ab9..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,49 +0,0 @@ -from pathlib import Path - -import pytest - -from jinja2 import loaders -from jinja2.environment import Environment - - -@pytest.fixture -def env(): - """returns a new environment.""" - return Environment() - - -@pytest.fixture -def dict_loader(): - """returns DictLoader""" - return loaders.DictLoader({"justdict.html": "FOO"}) - - -@pytest.fixture -def package_loader(): - """returns PackageLoader initialized from templates""" - return loaders.PackageLoader("res", "templates") - - -@pytest.fixture -def filesystem_loader(): - """returns FileSystemLoader initialized to res/templates directory""" - here = Path(__file__).parent.resolve() - return loaders.FileSystemLoader(here / "res" / "templates") - - -@pytest.fixture -def function_loader(): - """returns a FunctionLoader""" - return loaders.FunctionLoader({"justfunction.html": "FOO"}.get) - - -@pytest.fixture -def choice_loader(dict_loader, package_loader): - """returns a ChoiceLoader""" - return loaders.ChoiceLoader([dict_loader, package_loader]) - - -@pytest.fixture -def prefix_loader(filesystem_loader, dict_loader): - """returns a PrefixLoader""" - return loaders.PrefixLoader({"a": filesystem_loader, "b": dict_loader}) diff --git a/tests/res/__init__.py b/tests/res/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/res/package.zip b/tests/res/package.zip deleted file mode 100644 index d4c9ce9..0000000 Binary files a/tests/res/package.zip and /dev/null differ diff --git a/tests/res/templates/broken.html b/tests/res/templates/broken.html deleted file mode 100644 index 77669fa..0000000 --- a/tests/res/templates/broken.html +++ /dev/null @@ -1,3 +0,0 @@ -Before -{{ fail() }} -After diff --git a/tests/res/templates/foo/test.html b/tests/res/templates/foo/test.html deleted file mode 100644 index b7d6715..0000000 --- a/tests/res/templates/foo/test.html +++ /dev/null @@ -1 +0,0 @@ -FOO diff --git a/tests/res/templates/mojibake.txt b/tests/res/templates/mojibake.txt deleted file mode 100644 index 4b94aa6..0000000 --- a/tests/res/templates/mojibake.txt +++ /dev/null @@ -1 +0,0 @@ -文字化け diff --git a/tests/res/templates/syntaxerror.html b/tests/res/templates/syntaxerror.html deleted file mode 100644 index f21b817..0000000 --- a/tests/res/templates/syntaxerror.html +++ /dev/null @@ -1,4 +0,0 @@ -Foo -{% for item in broken %} - ... -{% endif %} diff --git a/tests/res/templates/test.html b/tests/res/templates/test.html deleted file mode 100644 index ba578e4..0000000 --- a/tests/res/templates/test.html +++ /dev/null @@ -1 +0,0 @@ -BAR diff --git a/tests/res/templates2/foo b/tests/res/templates2/foo deleted file mode 100644 index 1c4ad3e..0000000 --- a/tests/res/templates2/foo +++ /dev/null @@ -1,2 +0,0 @@ -Looks like the start of templates/foo/test.html -Tested by test_filesystem_loader_overlapping_names diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index 4db3b4a..0000000 --- a/tests/test_api.py +++ /dev/null @@ -1,434 +0,0 @@ -import shutil -import tempfile -from pathlib import Path - -import pytest - -from jinja2 import ChainableUndefined -from jinja2 import DebugUndefined -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import is_undefined -from jinja2 import make_logging_undefined -from jinja2 import meta -from jinja2 import StrictUndefined -from jinja2 import Template -from jinja2 import TemplatesNotFound -from jinja2 import Undefined -from jinja2 import UndefinedError -from jinja2.compiler import CodeGenerator -from jinja2.runtime import Context -from jinja2.utils import Cycler -from jinja2.utils import pass_context -from jinja2.utils import pass_environment -from jinja2.utils import pass_eval_context - - -class TestExtendedAPI: - def test_item_and_attribute(self, env): - from jinja2.sandbox import SandboxedEnvironment - - for env in Environment(), SandboxedEnvironment(): - tmpl = env.from_string("{{ foo.items()|list }}") - assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" - tmpl = env.from_string('{{ foo|attr("items")()|list }}') - assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" - tmpl = env.from_string('{{ foo["items"] }}') - assert tmpl.render(foo={"items": 42}) == "42" - - def test_finalize(self): - e = Environment(finalize=lambda v: "" if v is None else v) - t = e.from_string("{% for item in seq %}|{{ item }}{% endfor %}") - assert t.render(seq=(None, 1, "foo")) == "||1|foo" - - def test_finalize_constant_expression(self): - e = Environment(finalize=lambda v: "" if v is None else v) - t = e.from_string("<{{ none }}>") - assert t.render() == "<>" - - def test_no_finalize_template_data(self): - e = Environment(finalize=lambda v: type(v).__name__) - t = e.from_string("<{{ value }}>") - # If template data was finalized, it would print "strintstr". - assert t.render(value=123) == "" - - def test_context_finalize(self): - @pass_context - def finalize(context, value): - return value * context["scale"] - - e = Environment(finalize=finalize) - t = e.from_string("{{ value }}") - assert t.render(value=5, scale=3) == "15" - - def test_eval_finalize(self): - @pass_eval_context - def finalize(eval_ctx, value): - return str(eval_ctx.autoescape) + value - - e = Environment(finalize=finalize, autoescape=True) - t = e.from_string("{{ value }}") - assert t.render(value="