Jinja2-2.10/0000755000175000017500000000000013200660754012754 5ustar daviddavid00000000000000Jinja2-2.10/CHANGES.rst0000644000175000017500000005676313200657500014572 0ustar daviddavid00000000000000Jinja Changelog =============== Version 2.10 ------------ released on November 8th 2017 - Added a new extension node called ``OverlayScope`` which can be used to create an unoptimized scope that will look up all variables from a derived context. - Added an ``in`` test that works like the in operator. This can be used in combination with ``reject`` and ``select``. - Added ``previtem`` and ``nextitem`` to loop contexts, providing access to the previous/next item in the loop. If such an item does not exist, the value is undefined. - Added ``changed(*values)`` to loop contexts, providing an easy way of checking whether a value has changed since the last iteration (or rather since the last call of the method) - Added a ``namespace`` function that creates a special object which allows attribute assignment using the ``set`` tag. This can be used to carry data across scopes, e.g. from a loop body to code that comes after the loop. - Added a ``trimmed`` modifier to ``{% trans %}`` to strip linebreaks and surrounding whitespace. Also added a new policy to enable this for all ``trans`` blocks. - The ``random`` filter is no longer incorrectly constant folded and will produce a new random choice each time the template is rendered. (`#478`_) - Added a ``unique`` filter. (`#469`_) - Added ``min`` and ``max`` filters. (`#475`_) - Added tests for all comparison operators: ``eq``, ``ne``, ``lt``, ``le``, ``gt``, ``ge``. (`#665`_) - ``import`` statement cannot end with a trailing comma. (`#617`_, `#618`_) - ``indent`` filter will not indent blank lines by default. (`#685`_) - Add ``reverse`` argument for ``dictsort`` filter. (`#692`_) - Add a ``NativeEnvironment`` that renders templates to native Python types instead of strings. (`#708`_) - Added filter support to the block ``set`` tag. (`#489`_) - ``tojson`` filter marks output as safe to match documented behavior. (`#718`_) - Resolved a bug where getting debug locals for tracebacks could modify template context. - Fixed a bug where having many ``{% elif ... %}`` blocks resulted in a "too many levels of indentation" error. These blocks now compile to native ``elif ..:`` instead of ``else: if ..:`` (`#759`_) .. _#469: https://github.com/pallets/jinja/pull/469 .. _#475: https://github.com/pallets/jinja/pull/475 .. _#478: https://github.com/pallets/jinja/pull/478 .. _#489: https://github.com/pallets/jinja/pull/489 .. _#617: https://github.com/pallets/jinja/pull/617 .. _#618: https://github.com/pallets/jinja/pull/618 .. _#665: https://github.com/pallets/jinja/pull/665 .. _#685: https://github.com/pallets/jinja/pull/685 .. _#692: https://github.com/pallets/jinja/pull/692 .. _#708: https://github.com/pallets/jinja/pull/708 .. _#718: https://github.com/pallets/jinja/pull/718 .. _#759: https://github.com/pallets/jinja/pull/759 Version 2.9.6 ------------- (bugfix release, released on April 3rd 2017) - Fixed custom context behavior in fast resolve mode (#675) Version 2.9.5 ------------- (bugfix release, released on January 28th 2017) - Restored the original repr of the internal ``_GroupTuple`` because this caused issues with ansible and it was an unintended change. (#654) - Added back support for custom contexts that override the old ``resolve`` method since it was hard for people to spot that this could cause a regression. - Correctly use the buffer for the else block of for loops. This caused invalid syntax errors to be caused on 2.x and completely wrong behavior on Python 3 (#669) - Resolve an issue where the ``{% extends %}`` tag could not be used with async environments. (#668) - Reduce memory footprint slightly by reducing our unicode database dump we use for identifier matching on Python 3 (#666) - Fixed autoescaping not working for macros in async compilation mode. (#671) Version 2.9.4 ------------- (bugfix release, released on January 10th 2017) - Solved some warnings for string literals. (#646) - Increment the bytecode cache version which was not done due to an oversight before. - Corrected bad code generation and scoping for filtered loops. (#649) - Resolved an issue where top-level output silencing after known extend blocks could generate invalid code when blocks where contained in if statements. (#651) - Made the ``truncate.leeway`` default configurable to improve compatibility with older templates. Version 2.9.3 ------------- (bugfix release, released on January 8th 2017) - Restored the use of blocks in macros to the extend that was possible before. On Python 3 it would render a generator repr instead of the block contents. (#645) - Set a consistent behavior for assigning of variables in inner scopes when the variable is also read from an outer scope. This now sets the intended behavior in all situations however it does not restore the old behavior where limited assignments to outer scopes was possible. For more information and a discussion see #641 - Resolved an issue where ``block scoped`` would not take advantage of the new scoping rules. In some more exotic cases a variable overriden in a local scope would not make it into a block. - Change the code generation of the ``with`` statement to be in line with the new scoping rules. This resolves some unlikely bugs in edge cases. This also introduces a new internal ``With`` node that can be used by extensions. Version 2.9.2 ------------- (bugfix release, released on January 8th 2017) - Fixed a regression that caused for loops to not be able to use the same variable for the target as well as source iterator. (#640) - Add support for a previously unknown behavior of macros. It used to be possible in some circumstances to explicitly provide a caller argument to macros. While badly buggy and unintended it turns out that this is a common case that gets copy pasted around. To not completely break backwards compatibility with the most common cases it's now possible to provide an explicit keyword argument for caller if it's given an explicit default. (#642) Version 2.9.1 ------------- (bugfix release, released on January 7th 2017) - Resolved a regression with call block scoping for macros. Nested caller blocks that used the same identifiers as outer macros could refer to the wrong variable incorrectly. Version 2.9 ----------- (codename Derivation, released on January 7th 2017) - Change cache key definition in environment. This fixes a performance regression introduced in 2.8. - Added support for ``generator_stop`` on supported Python versions (Python 3.5 and later) - Corrected a long standing issue with operator precedence of math operations not being what was expected. - Added support for Python 3.6 async iterators through a new async mode. - Added policies for filter defaults and similar things. - urlize now sets "rel noopener" by default. - Support attribute fallback for old-style classes in 2.x. - Support toplevel set statements in extend situations. - Restored behavior of Cycler for Python 3 users. - Subtraction now follows the same behavior as other operators on undefined values. - ``map`` and friends will now give better error messages if you forgot to quote the parameter. - Depend on MarkupSafe 0.23 or higher. - Improved the ``truncate`` filter to support better truncation in case the string is barely truncated at all. - Change the logic for macro autoescaping to be based on the runtime autoescaping information at call time instead of macro define time. - Ported a modified version of the ``tojson`` filter from Flask to Jinja2 and hooked it up with the new policy framework. - Block sets are now marked ``safe`` by default. - On Python 2 the asciification of ASCII strings can now be disabled with the ``compiler.ascii_str`` policy. - Tests now no longer accept an arbitrary expression as first argument but a restricted one. This means that you can now properly use multiple tests in one expression without extra parentheses. In particular you can now write ``foo is divisibleby 2 or foo is divisibleby 3`` as you would expect. - Greatly changed the scoping system to be more consistent with what template designers and developers expect. There is now no more magic difference between the different include and import constructs. Context is now always propagated the same way. The only remaining differences is the defaults for ``with context`` and ``without context``. - The ``with`` and ``autoescape`` tags are now built-in. - Added the new ``select_autoescape`` function which helps configuring better autoescaping easier. - Fixed a runtime error in the sandbox when attributes of async generators were accessed. Version 2.8.1 ------------- (bugfix release, released on December 29th 2016) - Fixed the ``for_qs`` flag for ``urlencode``. - Fixed regression when applying ``int`` to non-string values. - SECURITY: if the sandbox mode is used format expressions are now sandboxed with the same rules as in Jinja. This solves various information leakage problems that can occur with format strings. Version 2.8 ----------- (codename Replacement, released on July 26th 2015) - Added ``target`` parameter to urlize function. - Added support for ``followsymlinks`` to the file system loader. - The truncate filter now counts the length. - Added equalto filter that helps with select filters. - Changed cache keys to use absolute file names if available instead of load names. - Fixed loop length calculation for some iterators. - Changed how Jinja2 enforces strings to be native strings in Python 2 to work when people break their default encoding. - Added :func:`make_logging_undefined` which returns an undefined object that logs failures into a logger. - If unmarshalling of cached data fails the template will be reloaded now. - Implemented a block ``set`` tag. - Default cache size was increased to 400 from a low 50. - Fixed ``is number`` test to accept long integers in all Python versions. - Changed ``is number`` to accept Decimal as a number. - Added a check for default arguments followed by non-default arguments. This change makes ``{% macro m(x, y=1, z) %}...{% endmacro %}`` a syntax error. The previous behavior for this code was broken anyway (resulting in the default value being applied to ``y``). - Add ability to use custom subclasses of ``jinja2.compiler.CodeGenerator`` and ``jinja2.runtime.Context`` by adding two new attributes to the environment (``code_generator_class`` and ``context_class``) (pull request ``#404``). - added support for context/environment/evalctx decorator functions on the finalize callback of the environment. - escape query strings for urlencode properly. Previously slashes were not escaped in that place. - Add 'base' parameter to 'int' filter. Version 2.7.3 ------------- (bugfix release, released on June 6th 2014) - Security issue: Corrected the security fix for the cache folder. This fix was provided by RedHat. Version 2.7.2 ------------- (bugfix release, released on January 10th 2014) - Prefix loader was not forwarding the locals properly to inner loaders. This is now fixed. - Security issue: Changed the default folder for the filesystem cache to be user specific and read and write protected on UNIX systems. See `Debian bug 734747`_ for more information. .. _Debian bug 734747: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747 Version 2.7.1 ------------- (bugfix release, released on August 7th 2013) - Fixed a bug with ``call_filter`` not working properly on environment and context filters. - Fixed lack of Python 3 support for bytecode caches. - Reverted support for defining blocks in included templates as this broke existing templates for users. - Fixed some warnings with hashing of undefineds and nodes if Python is run with warnings for Python 3. - Added support for properly hashing undefined objects. - Fixed a bug with the title filter not working on already uppercase strings. Version 2.7 ----------- (codename Translation, released on May 20th 2013) - Choice and prefix loaders now dispatch source and template lookup separately in order to work in combination with module loaders as advertised. - Fixed filesizeformat. - Added a non-silent option for babel extraction. - Added ``urlencode`` filter that automatically quotes values for URL safe usage with utf-8 as only supported encoding. If applications want to change this encoding they can override the filter. - Added ``keep-trailing-newline`` configuration to environments and templates to optionally preserve the final trailing newline. - Accessing ``last`` on the loop context no longer causes the iterator to be consumed into a list. - Python requirement changed: 2.6, 2.7 or >= 3.3 are required now, supported by same source code, using the "six" compatibility library. - Allow ``contextfunction`` and other decorators to be applied to ``__call__``. - Added support for changing from newline to different signs in the ``wordwrap`` filter. - Added support for ignoring memcache errors silently. - Added support for keeping the trailing newline in templates. - Added finer grained support for stripping whitespace on the left side of blocks. - Added ``map``, ``select``, ``reject``, ``selectattr`` and ``rejectattr`` filters. - Added support for ``loop.depth`` to figure out how deep inside a recursive loop the code is. - Disabled py_compile for pypy and python 3. Version 2.6 ----------- (codename Convolution, released on July 24th 2011) - internal attributes now raise an internal attribute error now instead of returning an undefined. This fixes problems when passing undefined objects to Python semantics expecting APIs. - traceback support now works properly for PyPy. (Tested with 1.4) - implemented operator intercepting for sandboxed environments. This allows application developers to disable builtin operators for better security. (For instance limit the mathematical operators to actual integers instead of longs) - groupby filter now supports dotted notation for grouping by attributes of attributes. - scoped blocks now properly treat toplevel assignments and imports. Previously an import suddenly "disappeared" in a scoped block. - automatically detect newer Python interpreter versions before loading code from bytecode caches to prevent segfaults on invalid opcodes. The segfault in earlier Jinja2 versions here was not a Jinja2 bug but a limitation in the underlying Python interpreter. If you notice Jinja2 segfaulting in earlier versions after an upgrade of the Python interpreter you don't have to upgrade, it's enough to flush the bytecode cache. This just no longer makes this necessary, Jinja2 will automatically detect these cases now. - the sum filter can now sum up values by attribute. This is a backwards incompatible change. The argument to the filter previously was the optional starting index which defaults to zero. This now became the second argument to the function because it's rarely used. - like sum, sort now also makes it possible to order items by attribute. - like sum and sort, join now also is able to join attributes of objects as string. - the internal eval context now has a reference to the environment. - added a mapping test to see if an object is a dict or an object with a similar interface. Version 2.5.5 ------------- (re-release of 2.5.4 with built documentation removed for filesize. Released on October 18th 2010) - built documentation is no longer part of release. Version 2.5.4 ------------- (bugfix release, released on October 17th 2010) - Fixed extensions not loading properly with overlays. - Work around a bug in cpython for the debugger that causes segfaults on 64bit big-endian architectures. Version 2.5.3 ------------- (bugfix release, released on October 17th 2010) - fixed an operator precedence error introduced in 2.5.2. Statements like "-foo.bar" had their implicit parentheses applied around the first part of the expression ("(-foo).bar") instead of the more correct "-(foo.bar)". Version 2.5.2 ------------- (bugfix release, released on August 18th 2010) - improved setup.py script to better work with assumptions people might still have from it (``--with-speedups``). - fixed a packaging error that excluded the new debug support. Version 2.5.1 ------------- (bugfix release, released on August 17th 2010) - StopIteration exceptions raised by functions called from templates are now intercepted and converted to undefineds. This solves a lot of debugging grief. (StopIteration is used internally to abort template execution) - improved performance of macro calls slightly. - babel extraction can now properly extract newstyle gettext calls. - using the variable ``num`` in newstyle gettext for something else than the pluralize count will no longer raise a :exc:`KeyError`. - removed builtin markup class and switched to markupsafe. For backwards compatibility the pure Python implementation still exists but is pulled from markupsafe by the Jinja2 developers. The debug support went into a separate feature called "debugsupport" and is disabled by default because it is only relevant for Python 2.4 - fixed an issue with unary operators having the wrong precedence. Version 2.5 ----------- (codename Incoherence, released on May 29th 2010) - improved the sort filter (should have worked like this for a long time) by adding support for case insensitive searches. - fixed a bug for getattribute constant folding. - support for newstyle gettext translations which result in a nicer in-template user interface and more consistent catalogs. (:ref:`newstyle-gettext`) - it's now possible to register extensions after an environment was created. Version 2.4.1 ------------- (bugfix release, released on April 20th 2010) - fixed an error reporting bug for undefineds. Version 2.4 ----------- (codename Correlation, released on April 13th 2010) - the environment template loading functions now transparently pass through a template object if it was passed to it. This makes it possible to import or extend from a template object that was passed to the template. - added a :class:`ModuleLoader` that can load templates from precompiled sources. The environment now features a method to compile the templates from a configured loader into a zip file or folder. - the _speedups C extension now supports Python 3. - added support for autoescaping toggling sections and support for evaluation contexts (:ref:`eval-context`). - extensions have a priority now. Version 2.3.1 ------------- (bugfix release, released on February 19th 2010) - fixed an error reporting bug on all python versions - fixed an error reporting bug on Python 2.4 Version 2.3 ----------- (codename 3000 Pythons, released on February 10th 2010) - fixes issue with code generator that causes unbound variables to be generated if set was used in if-blocks and other small identifier problems. - include tags are now able to select between multiple templates and take the first that exists, if a list of templates is given. - fixed a problem with having call blocks in outer scopes that have an argument that is also used as local variable in an inner frame (#360). - greatly improved error message reporting (#339) - implicit tuple expressions can no longer be totally empty. This change makes ``{% if %}...{% endif %}`` a syntax error now. (#364) - added support for translator comments if extracted via babel. - added with-statement extension. - experimental Python 3 support. Version 2.2.1 ------------- (bugfix release, released on September 14th 2009) - fixes some smaller problems for Jinja2 on Jython. Version 2.2 ----------- (codename Kong, released on September 13th 2009) - Include statements can now be marked with ``ignore missing`` to skip non existing templates. - Priority of ``not`` raised. It's now possible to write `not foo in bar` as an alias to `foo not in bar` like in python. Previously the grammar required parentheses (`not (foo in bar)`) which was odd. - Fixed a bug that caused syntax errors when defining macros or using the `{% call %}` tag inside loops. - Fixed a bug in the parser that made ``{{ foo[1, 2] }}`` impossible. - Made it possible to refer to names from outer scopes in included templates that were unused in the callers frame (#327) - Fixed a bug that caused internal errors if names where used as iteration variable and regular variable *after* the loop if that variable was unused *before* the loop. (#331) - Added support for optional ``scoped`` modifier to blocks. - Added support for line-comments. - Added the ``meta`` module. - Renamed (undocumented) attribute "overlay" to "overlayed" on the environment because it was clashing with a method of the same name. - speedup extension is now disabled by default. Version 2.1.1 ------------- (bugfix release, released on December 25th 2008) - Fixed a translation error caused by looping over empty recursive loops. Version 2.1 ----------- (codename Yasuzō, released on November 23rd 2008) - fixed a bug with nested loops and the special loop variable. Before the change an inner loop overwrote the loop variable from the outer one after iteration. - fixed a bug with the i18n extension that caused the explicit pluralization block to look up the wrong variable. - fixed a limitation in the lexer that made ``{{ foo.0.0 }}`` impossible. - index based subscribing of variables with a constant value returns an undefined object now instead of raising an index error. This was a bug caused by eager optimizing. - the i18n extension looks up ``foo.ugettext`` now followed by ``foo.gettext`` if an translations object is installed. This makes dealing with custom translations classes easier. - fixed a confusing behavior with conditional extending. loops were partially executed under some conditions even though they were not part of a visible area. - added ``sort`` filter that works like ``dictsort`` but for arbitrary sequences. - fixed a bug with empty statements in macros. - implemented a bytecode cache system. (:ref:`bytecode-cache`) - the template context is now weakref-able - inclusions and imports "with context" forward all variables now, not only the initial context. - added a cycle helper called ``cycler``. - added a joining helper called ``joiner``. - added a ``compile_expression`` method to the environment that allows compiling of Jinja expressions into callable Python objects. - fixed an escaping bug in urlize Version 2.0 ----------- (codename jinjavitus, released on July 17th 2008) - the subscribing of objects (looking up attributes and items) changed from slightly. It's now possible to give attributes or items a higher priority by either using dot-notation lookup or the bracket syntax. This also changed the AST slightly. ``Subscript`` is gone and was replaced with :class:`~jinja2.nodes.Getitem` and :class:`~jinja2.nodes.Getattr`. For more information see :ref:`the implementation details `. - added support for preprocessing and token stream filtering for extensions. This would allow extensions to allow simplified gettext calls in template data and something similar. - added :meth:`jinja2.environment.TemplateStream.dump`. - added missing support for implicit string literal concatenation. ``{{ "foo" "bar" }}`` is equivalent to ``{{ "foobar" }}`` - ``else`` is optional for conditional expressions. If not given it evaluates to ``false``. - improved error reporting for undefined values by providing a position. - ``filesizeformat`` filter uses decimal prefixes now per default and can be set to binary mode with the second parameter. - fixed bug in finalizer Version 2.0rc1 -------------- (no codename, released on June 9th 2008) - first release of Jinja2 Jinja2-2.10/artwork/0000755000175000017500000000000013200660754014445 5ustar daviddavid00000000000000Jinja2-2.10/artwork/jinjalogo.svg0000644000175000017500000004502213033733251017142 0ustar daviddavid00000000000000 image/svg+xml Jinja2-2.10/docs/0000755000175000017500000000000013200660754013704 5ustar daviddavid00000000000000Jinja2-2.10/docs/api.rst0000644000175000017500000010110013130203104015160 0ustar daviddavid00000000000000API === .. module:: jinja2 :synopsis: public Jinja2 API This document describes the API to Jinja2 and not the template language. It will be most useful as reference to those implementing the template interface to the application and not those who are creating Jinja2 templates. Basics ------ Jinja2 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 Jinja2 to load templates for your application looks roughly like this:: from jinja2 import Environment, PackageLoader, select_autoescape env = Environment( loader=PackageLoader('yourapplication', 'templates'), autoescape=select_autoescape(['html', 'xml']) ) This will create a template environment with the default settings and a loader that looks up the templates in the `templates` folder inside the `yourapplication` python package. Different loaders are available and you can also write your own if you want to load templates from a database or other resources. This also enables autoescaping for HTML and XML files. To load a template from this environment you just have to call the :meth:`get_template` method which then returns the loaded :class:`Template`:: template = env.get_template('mytemplate.html') To render it with some variables, just call the :meth:`render` method:: 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 Jinja2 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. Unicode ------- Jinja2 is using Unicode internally which means that you have to pass Unicode objects to the render function or bytestrings that only consist of ASCII characters. Additionally newlines are normalized to one end of line sequence which is per default UNIX style (``\n``). Python 2.x supports two ways of representing string objects. One is the `str` type and the other is the `unicode` type, both of which extend a type called `basestring`. Unfortunately the default is `str` which should not be used to store text based information unless only ASCII characters are used. With Python 2.6 it is possible to make `unicode` the default on a per module level and with Python 3 it will be the default. To explicitly use a Unicode string you have to prefix the string literal with a `u`: ``u'Hänsel und Gretel sagen Hallo'``. That way Python will store the string as Unicode by decoding the string with the character encoding from the current Python module. If no encoding is specified this defaults to 'ASCII' which means that you can't use any non ASCII identifier. To set a better module encoding add the following comment to the first or second line of the Python module using the Unicode literal:: # -*- coding: utf-8 -*- We recommend utf-8 as Encoding for Python modules and templates as it's possible to represent every Unicode character in utf-8 and because it's backwards compatible to ASCII. For Jinja2 the default encoding of templates is assumed to be utf-8. It is not possible to use Jinja2 to process non-Unicode data. The reason for this is that Jinja2 uses Unicode already on the language level. For example Jinja2 treats the non-breaking space as valid whitespace inside expressions which requires knowledge of the encoding or operating on an Unicode string. For more details about Unicode in Python have a look at the excellent `Unicode documentation`_. Another important thing is how Jinja2 is handling string literals in templates. A naive implementation would be using Unicode strings for all string literals but it turned out in the past that this is problematic as some libraries are typechecking against `str` explicitly. For example `datetime.strftime` does not accept Unicode arguments. To not break it completely Jinja2 is returning `str` for strings that fit into ASCII and for everything else `unicode`: >>> m = Template(u"{% set a, b = 'foo', 'föö' %}").module >>> m.a 'foo' >>> m.b u'f\xf6\xf6' .. _Unicode documentation: https://docs.python.org/dev/howto/unicode.html High Level API -------------- The high-level API is the API you will use in the application to load and render Jinja2 templates. The :ref:`low-level-api` on the other side is only useful if you want to dig deeper into Jinja2 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 global variables. These variables are always available in a template. As long as no template was loaded it's safe to modify this dict. For more details see :ref:`global-namespace`. For valid object names have a look at :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 The dict with the globals of that template. It's unsafe to modify this dict as it may be shared with other templates or the environment that loaded the template. .. 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 Jinja2 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, select_autoescape env = Environment(autoescape=select_autoescape(['html', 'htm', 'xml']), loader=PackageLoader('mypackage')) The :func:`~jinja.select_autoescape` function returns a function that works rougly 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 -------------------- Jinja2 uses the regular Python 2.x naming rules. Valid identifiers have to match ``[a-zA-Z_][a-zA-Z0-9_]*``. As a matter of fact non ASCII characters are currently not allowed. This limitation will probably go away as soon as unicode identifiers are fully specified for Python 3. 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.unicode`. 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 `StrictUndefined` which disallows all operations beside testing if it's an undefined object. .. autoclass:: jinja2.Undefined() .. attribute:: _undefined_hint Either `None` or an unicode 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.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` objects are implemented by overriding the special `__underscore__` methods. For example the default :class:`Undefined` class implements `__unicode__` in a way that it returns an empty string, however `__int__` and others still fail with an exception. To allow conversion to int by returning ``0`` you can implement your own:: class NullUndefined(Undefined): def __int__(self): return 0 def __float__(self): return 0.0 To disallow a method, just override it and raise :attr:`~Undefined._undefined_exception`. Because this is a very common idom in undefined objects there is the helper method :meth:`~Undefined._fail_with_undefined_error` that does the error raising automatically. Here a class that works like the regular :class:`Undefined` but chokes on iteration:: class NonIterableUndefined(Undefined): __iter__ = Undefined._fail_with_undefined_error The Context ----------- .. autoclass:: jinja2.runtime.Context() :members: resolve, 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) .. admonition:: Implementation Context is immutable for the same reason Python's frame locals are immutable inside functions. Both Jinja2 and Python are not using the context / frame locals as data storage for variables but only as primary data source. When a template accesses a variable the template does not define, Jinja2 looks up the variable in the context, after that the variable is treated as if it was defined in the template. .. _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 Jinja2 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 ------------- Starting with version 2.9, Jinja2 also supports the Python `async` and `await` constructs. As far as template designers go this feature is entirely opaque to them however as a developer you should be aware of how it's implemented as it influences what type of APIs you can safely expose to the template environment. First you need to be aware that by default async support is disabled as enabling it will generate different template code behind the scenes which passes everything through the asyncio event loop. This is important to understand because it has some impact to what you are doing: * template rendering will require an event loop to be set for the current thread (``asyncio.get_event_loop`` needs to return one) * all template generation code internally runs async generators which means that you will pay a performance penalty even if the non sync methods are used! * The sync methods are based on async methods if the async mode is enabled which means that `render` for instance will internally invoke `render_async` and run it as part of the current event loop until the execution finished. Awaitable objects can be returned from functions in templates and any function call in a template will automatically await the result. This means that you can let provide a method that asynchronously loads data from a database if you so desire and from the template designer's point of view this is just another function they can call. This means that the ``await`` you would normally issue in Python is implied. However this only applies to function calls. If an attribute for instance would be an avaitable object then this would not result in the expected behavior. Likewise iterations with a `for` loop support async iterators. .. _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' ``compiler.ascii_str``: This boolean controls on Python 2 if Jinja2 should store ASCII only literals as bytestring instead of unicode strings. This used to be always enabled for Jinja versions below 2.9 and now can be changed. Traditionally it was done this way since some APIs in Python 2 failed badly for unicode strings (for instance the datetime strftime API). Now however sometimes the inverse is true (for instance str.format). If this is set to False then all strings are stored as unicode internally. ``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. ``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 Jinja2 environment. .. autofunction:: jinja2.environmentfilter .. autofunction:: jinja2.contextfilter .. autofunction:: jinja2.evalcontextfilter .. autofunction:: jinja2.environmentfunction .. autofunction:: jinja2.contextfunction .. autofunction:: jinja2.evalcontextfunction .. function:: escape(s) Convert the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in string `s` to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. This function will not escaped objects that do have an HTML representation such as already escaped data. The return value is a :class:`Markup` string. .. autofunction:: jinja2.clear_caches .. autofunction:: jinja2.is_undefined .. autoclass:: jinja2.Markup([string]) :members: escape, unescape, striptags .. admonition:: Note The Jinja2 :class:`Markup` class is compatible with at least Pylons and Genshi. It's expected that more template engines and framework will pick up the `__html__` concept soon. Exceptions ---------- .. autoexception:: jinja2.TemplateError .. autoexception:: jinja2.UndefinedError .. autoexception:: jinja2.TemplateNotFound .. autoexception:: jinja2.TemplatesNotFound .. autoexception:: jinja2.TemplateSyntaxError .. attribute:: message The error message as utf-8 bytestring. .. attribute:: lineno The line number where the error occurred .. attribute:: name The load name for the template as unicode string. .. attribute:: filename The filename that loaded the template as bytestring in the encoding of the file system (most likely utf-8 or mbcs on Windows systems). The reason why the filename and error message are bytestrings and not unicode strings is that Python 2.x is not using unicode for exceptions and tracebacks as well as the compiler. This will change with Python 3. .. autoexception:: jinja2.TemplateRuntimeError .. autoexception:: jinja2.TemplateAssertionError .. _writing-filters: Custom Filters -------------- Custom filters are just regular Python functions that take the left side of the filter as first argument and the arguments passed to the filter as extra arguments or keyword arguments. For example in the filter ``{{ 42|myfilter(23) }}`` the function would be called with ``myfilter(42, 23)``. Here for example a simple filter that can be applied to datetime objects to format them:: def datetimeformat(value, format='%H:%M / %d-%m-%Y'): return value.strftime(format) You can register it on the template environment by updating the :attr:`~Environment.filters` dict on the environment:: environment.filters['datetimeformat'] = datetimeformat Inside the template it can then be used as follows: .. sourcecode:: jinja written on: {{ article.pub_date|datetimeformat }} publication date: {{ article.pub_date|datetimeformat('%d-%m-%Y') }} Filters can also be passed the current template context or environment. This is useful if a filter wants to return an undefined value or check the current :attr:`~Environment.autoescape` setting. For this purpose three decorators exist: :func:`environmentfilter`, :func:`contextfilter` and :func:`evalcontextfilter`. Here a small example filter that breaks a text into HTML line breaks and paragraphs and marks the return value as safe HTML string if autoescaping is enabled:: import re from jinja2 import evalcontextfilter, Markup, escape _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') @evalcontextfilter def nl2br(eval_ctx, value): result = u'\n\n'.join(u'

%s

' % p.replace('\n', Markup('
\n')) for p in _paragraph_re.split(escape(value))) if eval_ctx.autoescape: result = Markup(result) return result Context filters work the same just that the first argument is the current active :class:`Context` rather than the environment. .. _eval-context: Evaluation Context ------------------ The evaluation context (short eval context or eval ctx) is a new object introduced in Jinja 2.4 that makes it possible to activate and deactivate compiled features at runtime. Currently it is only used to enable and disable the automatic escaping but can be used for extensions as well. In previous Jinja versions filters and functions were marked as environment callables in order to check for the autoescape status from the environment. In new versions it's encouraged to check the setting from the evaluation context instead. Previous versions:: @environmentfilter def filter(env, value): result = do_something(value) if env.autoescape: result = Markup(result) return result In new versions you can either use a :func:`contextfilter` and access the evaluation context from the actual context, or use a :func:`evalcontextfilter` which directly passes the evaluation context to the function:: @contextfilter def filter(context, value): result = do_something(value) if context.eval_ctx.autoescape: result = Markup(result) return result @evalcontextfilter def filter(eval_ctx, value): result = do_something(value) if 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`. .. _writing-tests: Custom Tests ------------ Tests work like filters just that there is no way for a test to get access to the environment or context and that they can't be chained. The return value of a test should be `True` or `False`. The purpose of a test is to give the template designers the possibility to perform type and conformability checks. Here a simple test that checks if a variable is a prime number:: import math def is_prime(n): if n == 2: return True for i in xrange(2, int(math.ceil(math.sqrt(n))) + 1): if n % i == 0: return False return True You can register it on the template environment by updating the :attr:`~Environment.tests` dict on the environment:: environment.tests['prime'] = is_prime A template designer can then use the test like this: .. sourcecode:: jinja {% if 42 is prime %} 42 is a prime number {% else %} 42 is not a prime number {% endif %} .. _global-namespace: The Global Namespace -------------------- Variables stored in the :attr:`Environment.globals` dict are special as they are available for imported templates too, even if they are imported without context. This is the place where you can put variables and functions that should be available all the time. Additionally :attr:`Template.globals` exist that are variables available to a specific template that are available to all :meth:`~Template.render` calls. .. _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 unicode 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 Jinja2 versions will try not to change it in a backwards incompatible way but modifications in the Jinja2 core may shine through. For example if Jinja2 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 Jinja2-2.10/docs/latexindex.rst0000644000175000017500000000012313033733251016574 0ustar daviddavid00000000000000:orphan: Jinja2 Documentation ==================== .. include:: contents.rst.inc Jinja2-2.10/docs/sandbox.rst0000644000175000017500000000672013033733251016076 0ustar daviddavid00000000000000Sandbox ======= The Jinja2 sandbox can be used to evaluate untrusted code. Access to unsafe attributes and methods is prohibited. Assuming `env` is a :class:`SandboxedEnvironment` in the default configuration the following piece of code shows how it works: >>> env.from_string("{{ func.func_code }}").render(func=lambda:None) u'' >>> env.from_string("{{ func.func_code.do_something }}").render(func=lambda:None) Traceback (most recent call last): ... SecurityError: access to attribute 'func_code' of 'function' object is unsafe. 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 .. admonition:: Note The Jinja2 sandbox alone is no solution for perfect security. Especially for web applications you have to keep in mind that users may create templates with arbitrary HTML in so it's crucial to ensure that (if you are running multiple users on the same server) they can't harm each other via JavaScript insertions and much more. Also the sandbox is only as good as the configuration. We strongly recommend only passing non-shared resources to the template and use some sort of whitelisting for attributes. Also keep in mind that templates may raise runtime or compile time errors, so make sure to catch them. Operator Intercepting --------------------- .. versionadded:: 2.6 For maximum performance Jinja2 will let operators call directly the type specific callback methods. This means that it's not possible to have this intercepted by overriding :meth:`Environment.call`. Furthermore a conversion from operator to special method is not always directly possible due to how operators work. For instance for divisions more than one special method exist. With Jinja 2.6 there is now support for explicit operator intercepting. This can be used to customize specific operators as necessary. In order to intercept an operator one has to override the :attr:`SandboxedEnvironment.intercepted_binops` attribute. Once the operator that needs to be intercepted is added to that set Jinja2 will generate bytecode that calls the :meth:`SandboxedEnvironment.call_binop` function. For unary operators the `unary` attributes and methods have to be used instead. The default implementation of :attr:`SandboxedEnvironment.call_binop` will use the :attr:`SandboxedEnvironment.binop_table` to translate operator symbols into callbacks performing the default operator behavior. This example shows how the power (``**``) operator can be disabled in Jinja2:: 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 SandboxedEnvironment.call_binop(self, context, operator, left, right) Make sure to always call into the super method, even if you are not intercepting the call. Jinja2 might internally call the method to evaluate expressions. Jinja2-2.10/docs/templates.rst0000644000175000017500000015241413176350145016445 0ustar daviddavid00000000000000Template Designer Documentation =============================== .. highlight:: html+jinja 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 * ``# ... ##`` for :ref:`Line Statements ` .. _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 Jinja2 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 %}
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, Jinja2 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 %} .. _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. 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-master-fallback`. 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 %} Named Block End-Tags ~~~~~~~~~~~~~~~~~~~~ Jinja2 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. Template Objects ~~~~~~~~~~~~~~~~ .. versionchanged:: 2.4 If a template object was passed in the template context, you can extend from that object as well. Assuming the calling code passes a layout template as `layout_template` to the environment, this code works:: {% extends layout_template %} Previously, the `layout_template` variable had to be a string with the layout template's filename for this to work. 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 `MarkupSafe.Markup`, or b. the template, with the `|safe` filter The main problem with this approach is that Python itself doesn't have the concept of tainted values; so whether a value is safe or unsafe can get lost. If a value is not marked safe, auto-escaping will take place; which means that you could end up with double-escaped contents. Double-escaping is easy to avoid, however: just rely on the tools Jinja2 provides and *don't use builtin Python constructs such as str.format or the string modulo operator (%)*. Jinja2 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 (``str``, ``unicode``, ``basestring``) are not `MarkupSafe.Markup` strings with an ``__html__`` attribute. .. _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.iteritems() %}
    {{ key|e }}
    {{ value|e }}
    {% endfor %}
    Note, however, that **Python dicts are not ordered**; so you might want to either pass a sorted ``list`` of ``tuple`` s -- or a ``collections.OrderedDict`` -- to the template, or use the `dictsort` filter. 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 Jinja2 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. `defaults` A tuple of default values. `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. .. _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 Jinja2 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 hat 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` statement is useful to include a template and return the rendered contents of that file into the current namespace:: {% include 'header.html' %} Body {% include 'footer.html' %} Included templates have access to the variables of the active context by default. For more details about context behavior of imports and includes, see :ref:`import-visibility`. From Jinja 2.2 onwards, you can mark an include with ``ignore missing``; in which case Jinja will ignore the statement if the template to be included does not exist. When combined with ``with`` or ``without context``, it must be placed *before* the context visibility statement. Here are some valid examples:: {% include "sidebar.html" ignore missing %} {% include "sidebar.html" ignore missing with context %} {% include "sidebar.html" ignore missing without context %} .. versionadded:: 2.2 You can also provide a list of templates that are checked for existence before inclusion. The first template that exists will be included. If `ignore missing` is given, it will fall back to rendering nothing if none of the templates exist, otherwise it will raise an exception. Example:: {% include ['page_detailed.html', 'page.html'] %} {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} .. versionchanged:: 2.4 If a template object was passed to the template context, you can include that object using `include`. .. _import: Import ~~~~~~ Jinja2 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 / 42.23: Integers and floating point numbers are created by just writing the number down. If a dot is present, the number is a float, otherwise an integer. Keep in mind that, in Python, ``42`` and ``42.0`` are different (``int`` and ``float``, respectively). ['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 }}``. (Just like ``from __future__ import division``.) // 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``. 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) 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 `. \| Applies a :ref:`filter `. ~ 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 'master.html' %} The general syntax is `` if else ``. The `else` part is optional. If not provided, the else block implicitly evaluates into an undefined object:: .. sourcecode:: jinja {{ '[%s]' % page.title if page.title }} .. _builtin-filters: List of Builtin Filters ----------------------- .. jinjafilters:: .. _builtin-tests: List of Builtin Tests --------------------- .. jinjatests:: .. _builtin-globals: List of Global Functions ------------------------ The following functions are available in the global scope by default: .. 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) The cycler allows you to cycle among values similar to how `loop.cycle` works. Unlike `loop.cycle`, you can use this cycler outside of loops or over multiple loops. This can be very useful if you want to show a list of folders and files with the folders on top but both in the same list with alternating row colors. The following example shows how `cycler` can be used:: {% set row_class = cycler('odd', 'even') %}
      {% for folder in folders %}
    • {{ folder|e }}
    • {% endfor %} {% for filename in files %}
    • {{ filename|e }}
    • {% endfor %}
    A cycler has the following attributes and methods: .. method:: reset() Resets the cycle to the first item. .. method:: next() Goes one item ahead and returns the then-current item. .. attribute:: current Returns the current item. .. versionadded:: 2.1 .. 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 ---------- The following sections cover the built-in Jinja2 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 i18n extension is enabled, it's possible to mark parts in the template as translatable. To mark a section as translatable, you can use `trans`::

    {% trans %}Hello {{ user }}!{% endtrans %}

    To translate a template expression --- say, using template filters, or by just accessing an attribute of an object --- you need to bind the expression to a name for use within the translation block::

    {% trans user=user.username %}Hello {{ user }}!{% endtrans %}

    If you need to bind more than one expression inside a `trans` tag, separate the pieces with a comma (``,``):: {% trans book_title=book.title, author=author.name %} This is {{ book_title }} by {{ author }} {% endtrans %} Inside trans tags no statements are allowed, only variable tags are. To pluralize, specify both the singular and plural forms with the `pluralize` tag, which appears between `trans` and `endtrans`:: {% 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 the correct singular or plural form. If that doesn't work out, you can specify the name which should be used for pluralizing by adding it as parameter to `pluralize`:: {% trans ..., user_count=users|length %}... {% pluralize user_count %}...{% endtrans %} When translating longer blocks of text, whitespace and linebreaks result in rather ugly 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/trailing whitespace:: {% trans trimmed book_title=book.title %} This is {{ book_title }}. You should read it! {% endtrans %} If trimming is enabled globally, the `notrimmed` modifier can be used to disable it for a `trans` block. .. versionadded:: 2.10 The `trimmed` and `notrimmed` modifiers have been added. It's also possible to translate strings in expressions. For that purpose, three functions exist: - `gettext`: translate a single string - `ngettext`: translate a pluralizable string - `_`: alias for `gettext` For example, you can easily print a translated string like this:: {{ _('Hello World!') }} To use placeholders, use the `format` filter:: {{ _('Hello %(user)s!')|format(user=user.username) }} For multiple placeholders, always use keyword arguments to `format`, as other languages may not use the words in the same order. .. versionchanged:: 2.5 If newstyle gettext calls are activated (:ref:`newstyle-gettext`), using placeholders is a lot easier: .. sourcecode:: html+jinja {{ gettext('Hello World!') }} {{ gettext('Hello %(name)s!', name='World') }} {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} Note that the `ngettext` function's format string automatically receives the count as a `num` parameter in addition to the regular 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`). 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 Jinja2 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. Jinja2-2.10/docs/_themes/0000755000175000017500000000000013200660754015330 5ustar daviddavid00000000000000Jinja2-2.10/docs/_themes/README0000644000175000017500000000210513033733251016203 0ustar daviddavid00000000000000Flask Sphinx Styles =================== This repository contains sphinx styles for Flask and Flask related projects. To use this style in your Sphinx documentation, follow this guide: 1. put this folder as _themes into your docs folder. Alternatively you can also use git submodules to check out the contents there. 2. add this to your conf.py: sys.path.append(os.path.abspath('_themes')) html_theme_path = ['_themes'] html_theme = 'flask' The following themes exist: - 'flask' - the standard flask documentation theme for large projects - 'flask_small' - small one-page theme. Intended to be used by very small addon libraries for flask. The following options exist for the flask_small theme: [options] index_logo = '' filename of a picture in _static to be used as replacement for the h1 in the index.rst file. index_logo_height = 120px height of the index logo github_fork = '' repository name on github for the "fork me" badge Jinja2-2.10/docs/_themes/LICENSE0000644000175000017500000000337513033733251016342 0ustar daviddavid00000000000000Copyright (c) 2010 by Armin Ronacher. Some rights reserved. Redistribution and use in source and binary forms of the theme, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. We kindly ask you to only use these themes in an unmodified manner just for Flask and Flask-related products, not for unrelated projects. If you like the visual style and want to use it for your own projects, please consider making some larger changes to the themes (such as changing font faces, sizes, colors or margins). THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Jinja2-2.10/docs/_themes/jinja/0000755000175000017500000000000013200660754016423 5ustar daviddavid00000000000000Jinja2-2.10/docs/_themes/jinja/static/0000755000175000017500000000000013200660754017712 5ustar daviddavid00000000000000Jinja2-2.10/docs/_themes/jinja/static/jinja.css_t0000644000175000017500000001450213033733251022041 0ustar daviddavid00000000000000/* * jinja.css_t * ~~~~~~~~~~~ * * :copyright: Copyright 2011 by Armin Ronacher. * :license: Flask Design License, see LICENSE for details. */ @import url(http://fonts.googleapis.com/css?family=Crimson+Text); {% set page_width = '940px' %} {% set sidebar_width = '220px' %} {% set font_family = 'Georgia, serif' %} {% set header_font_family = 'Crimson Text, ' ~ font_family %} @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: {{ font_family }}; font-size: 17px; background-color: white; color: #000; margin: 0; padding: 0; } div.document { width: {{ page_width }}; margin: 30px auto 0 auto; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 {{ sidebar_width }}; } div.sphinxsidebar { width: {{ sidebar_width }}; } hr { border: 1px solid #B1B4B6; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 0 30px; } img.floatingflask { padding: 0 0 10px 10px; float: right; } div.footer { width: {{ page_width }}; margin: 20px auto 30px auto; font-size: 14px; color: #888; text-align: right; } div.footer a { color: #888; } div.related { display: none; } div.sphinxsidebar a { color: #444; text-decoration: none; border-bottom: 1px dotted #999; } div.sphinxsidebar a:hover { border-bottom: 1px solid #999; } div.sphinxsidebar { font-size: 15px; line-height: 1.5; } div.sphinxsidebarwrapper { padding: 18px 10px; } div.sphinxsidebarwrapper p.logo { padding: 0 0 20px 0; margin: 0; text-align: center; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: {{ font_family }}; color: #444; font-size: 24px; font-weight: normal; margin: 0 0 5px 0; padding: 0; } div.sphinxsidebar h4 { font-size: 20px; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p.logo a, div.sphinxsidebar h3 a, div.sphinxsidebar p.logo a:hover, div.sphinxsidebar h3 a:hover { border: none; } div.sphinxsidebar p { color: #555; margin: 10px 0; } div.sphinxsidebar ul { margin: 10px 0; padding: 0; color: #000; } div.sphinxsidebar input { border: 1px solid #ccc; font-family: {{ font_family }}; font-size: 14px; } div.sphinxsidebar form.search input[name="q"] { width: 130px; } /* -- body styles ----------------------------------------------------------- */ a { color: #aa0000; text-decoration: underline; } a:hover { color: #dd0000; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: {{ header_font_family }}; font-weight: normal; margin: 30px 0px 10px 0px; padding: 0; color: black; } div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } div.body h2 { font-size: 180%; } div.body h3 { font-size: 150%; } div.body h4 { font-size: 130%; } div.body h5 { font-size: 100%; } div.body h6 { font-size: 100%; } a.headerlink { color: #ddd; padding: 0 4px; text-decoration: none; } a.headerlink:hover { color: #444; background: #eaeaea; } div.body p, div.body dd, div.body li { line-height: 1.4em; } div.admonition { background: #fafafa; margin: 20px -30px; padding: 10px 30px; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } div.admonition tt.xref, div.admonition a tt { border-bottom: 1px solid #fafafa; } dd div.admonition { margin-left: -60px; padding-left: 60px; } div.admonition p.admonition-title { font-family: {{ font_family }}; font-weight: normal; font-size: 24px; margin: 0 0 10px 0; padding: 0; line-height: 1; } div.admonition p.last { margin-bottom: 0; } div.highlight { background-color: white; } dt:target, .highlight { background: #FAF3E8; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre, tt { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.85em; } img.screenshot { } tt.descname, tt.descclassname { font-size: 0.95em; } tt.descname { padding-right: 0.08em; } img.screenshot { -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils { border: 1px solid #888; -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils td, table.docutils th { border: 1px solid #888; padding: 0.25em 0.7em; } table.field-list, table.footnote { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } table.footnote { margin: 15px 0; width: 100%; border: 1px solid #eee; background: #fdfdfd; font-size: 0.9em; } table.footnote + table.footnote { margin-top: -15px; border-top: none; } table.field-list th { padding: 0 0.8em 0 0; } table.field-list td { padding: 0; } table.footnote td.label { width: 0px; padding: 0.3em 0 0.3em 0.5em; } table.footnote td { padding: 0.3em 0.5em; } dl { margin: 0; padding: 0; } dl dd { margin-left: 30px; } blockquote { margin: 0 0 0 30px; padding: 0; } ul, ol { margin: 10px 0 10px 30px; padding: 0; } pre { background: #eee; padding: 7px 30px; margin: 15px -30px; line-height: 1.3em; } dl pre, blockquote pre, li pre { margin-left: -60px; padding-left: 60px; } dl dl pre { margin-left: -90px; padding-left: 90px; } tt { background-color: #E8EFF0; color: #222; /* padding: 1px 2px; */ } tt.xref, a tt { background-color: #E8EFF0; border-bottom: 1px solid white; } a.reference { text-decoration: none; border-bottom: 1px dotted #bb0000; } a.reference:hover { border-bottom: 1px solid #dd0000; } a.footnote-reference { text-decoration: none; font-size: 0.7em; vertical-align: top; border-bottom: 1px dotted #bb0000; } a.footnote-reference:hover { border-bottom: 1px solid #dd0000; } a:hover tt { background: #EEE; } Jinja2-2.10/docs/_themes/jinja/relations.html0000644000175000017500000000111613033733251021305 0ustar daviddavid00000000000000

    Related Topics

    Jinja2-2.10/docs/_themes/jinja/layout.html0000644000175000017500000000037113033733251020624 0ustar daviddavid00000000000000{%- extends "basic/layout.html" %} {%- block relbar2 %}{% endblock %} {%- block footer %} {%- endblock %} Jinja2-2.10/docs/_themes/jinja/theme.conf0000644000175000017500000000005713033733251020373 0ustar daviddavid00000000000000[theme] inherit = basic stylesheet = jinja.css Jinja2-2.10/docs/jinjaext.py0000644000175000017500000001563213176154260016103 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ Jinja Documentation Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for automatically documenting filters and tests. :copyright: Copyright 2008 by Armin Ronacher. :license: BSD. """ import collections import os import re import inspect import logging import jinja2 from itertools import islice from types import BuiltinFunctionType from docutils import nodes from docutils.statemachine import ViewList from sphinx.ext.autodoc import prepare_docstring from sphinx.application import TemplateBridge from pygments.style import Style from pygments.token import Keyword, Name, Comment, String, Error, \ Number, Operator, Generic from jinja2 import Environment, FileSystemLoader def parse_rst(state, content_offset, doc): node = nodes.section() # hack around title style bookkeeping surrounding_title_styles = state.memo.title_styles surrounding_section_level = state.memo.section_level state.memo.title_styles = [] state.memo.section_level = 0 state.nested_parse(doc, content_offset, node, match_titles=1) state.memo.title_styles = surrounding_title_styles state.memo.section_level = surrounding_section_level return node.children class JinjaStyle(Style): title = 'Jinja Style' default_style = "" styles = { Comment: 'italic #aaaaaa', Comment.Preproc: 'noitalic #B11414', Comment.Special: 'italic #505050', Keyword: 'bold #B80000', Keyword.Type: '#808080', Operator.Word: 'bold #B80000', Name.Builtin: '#333333', Name.Function: '#333333', Name.Class: 'bold #333333', Name.Namespace: 'bold #333333', Name.Entity: 'bold #363636', Name.Attribute: '#686868', Name.Tag: 'bold #686868', Name.Decorator: '#686868', String: '#AA891C', Number: '#444444', Generic.Heading: 'bold #000080', Generic.Subheading: 'bold #800080', Generic.Deleted: '#aa0000', Generic.Inserted: '#00aa00', Generic.Error: '#aa0000', Generic.Emph: 'italic', Generic.Strong: 'bold', Generic.Prompt: '#555555', Generic.Output: '#888888', Generic.Traceback: '#aa0000', Error: '#F00 bg:#FAA' } _sig_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*(\(.*?\))') def format_function(name, aliases, func): lines = inspect.getdoc(func).splitlines() signature = '()' if isinstance(func, BuiltinFunctionType): match = _sig_re.match(lines[0]) if match is not None: del lines[:1 + bool(lines and not lines[0])] signature = match.group(1) else: try: argspec = inspect.getargspec(func) if getattr(func, 'environmentfilter', False) or \ getattr(func, 'contextfilter', False) or \ getattr(func, 'evalcontextfilter', False): del argspec[0][0] signature = inspect.formatargspec(*argspec) except: pass result = ['.. function:: %s%s' % (name, signature), ''] result.extend(' ' + line for line in lines) if aliases: result.extend(('', ' :aliases: %s' % ', '.join( '``%s``' % x for x in sorted(aliases)))) return result def dump_functions(mapping): def directive( dirname, arguments, options, content, lineno, content_offset, block_text, state, state_machine ): reverse_mapping = {} for name, func in mapping.items(): reverse_mapping.setdefault(func, []).append(name) filters = [] compare_ops = set(('lt', 'le', 'eq', 'ne', 'ge', 'gt')) for func, names in reverse_mapping.items(): aliases = sorted(names, key=len) aliases = sorted(aliases, key=lambda x: x in compare_ops) name = aliases.pop() filters.append((name, aliases, func)) filters.sort() result = ViewList() for name, aliases, func in filters: for item in format_function(name, aliases, func): result.append(item, '') node = nodes.paragraph() state.nested_parse(result, content_offset, node) return node.children return directive from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS jinja_filters = dump_functions(DEFAULT_FILTERS) jinja_tests = dump_functions(DEFAULT_TESTS) def jinja_nodes(dirname, arguments, options, content, lineno, content_offset, block_text, state, state_machine): from jinja2.nodes import Node doc = ViewList() def walk(node, indent): p = ' ' * indent sig = ', '.join(node.fields) doc.append(p + '.. autoclass:: %s(%s)' % (node.__name__, sig), '') if node.abstract: members = [] for key, name in node.__dict__.items(): if not key.startswith('_') and \ not hasattr(node.__base__, key) and isinstance(name, collections.Callable): members.append(key) if members: members.sort() doc.append('%s :members: %s' % (p, ', '.join(members)), '') if node.__base__ != object: doc.append('', '') doc.append('%s :Node type: :class:`%s`' % (p, node.__base__.__name__), '') doc.append('', '') children = node.__subclasses__() children.sort(key=lambda x: x.__name__.lower()) for child in children: walk(child, indent) walk(Node, 0) return parse_rst(state, content_offset, doc) def inject_toc(app, doctree, docname): titleiter = iter(doctree.traverse(nodes.title)) try: # skip first title, we are not interested in that one next(titleiter) title = next(titleiter) # and check if there is at least another title next(titleiter) except StopIteration: return tocnode = nodes.section('') tocnode['classes'].append('toc') toctitle = nodes.section('') toctitle['classes'].append('toctitle') toctitle.append(nodes.title(text='Table Of Contents')) tocnode.append(toctitle) tocnode += doctree.document.settings.env.get_toc_for(docname)[0][1] title.parent.insert(title.parent.children.index(title), tocnode) def setup(app): app.add_directive('jinjafilters', jinja_filters, 0, (0, 0, 0)) app.add_directive('jinjatests', jinja_tests, 0, (0, 0, 0)) app.add_directive('jinjanodes', jinja_nodes, 0, (0, 0, 0)) # uncomment for inline toc. links are broken unfortunately ##app.connect('doctree-resolved', inject_toc) Jinja2-2.10/docs/_templates/0000755000175000017500000000000013200660754016041 5ustar daviddavid00000000000000Jinja2-2.10/docs/_templates/sidebarintro.html0000644000175000017500000000130613130203104021374 0ustar daviddavid00000000000000

    About Jinja2

    Jinja2 is a full featured template engine for Python. It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed.

    Other Formats

    You can download the documentation in other formats as well:

    Useful Links

    Jinja2-2.10/docs/_templates/sidebarlogo.html0000644000175000017500000000021713033733251021216 0ustar daviddavid00000000000000 Jinja2-2.10/docs/intro.rst0000644000175000017500000001007613130203104015555 0ustar daviddavid00000000000000Introduction ============ This is the documentation for the Jinja2 general purpose templating language. Jinja2 is a library for Python that is designed to be flexible, fast and secure. If you have any exposure to other text-based template languages, such as Smarty or Django, you should feel right at home with Jinja2. It's both designer and developer friendly by sticking to Python's principles and adding functionality useful for templating environments. Prerequisites ------------- Jinja2 works with Python 2.6.x, 2.7.x and >= 3.3. If you are using Python 3.2 you can use an older release of Jinja2 (2.6) as support for Python 3.2 was dropped in Jinja2 version 2.7. If you wish to use the :class:`~jinja2.PackageLoader` class, you will also need `setuptools`_ or `distribute`_ installed at runtime. Installation ------------ You have multiple ways to install Jinja2. If you are unsure what to do, go with the Python egg or tarball. As a Python egg (via `easy_install`) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can install the most recent Jinja2 version using `easy_install`_ or `pip`_:: easy_install Jinja2 pip install Jinja2 This will install a Jinja2 egg in your Python installation's site-packages directory. From the tarball release ~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Download the most recent tarball from the `download page`_ 2. Unpack the tarball 3. ``python setup.py install`` Note that you either have to have `setuptools` or `distribute` installed; the latter is preferred. This will install Jinja2 into your Python installation's site-packages directory. Installing the development version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Install `git`_ 2. ``git clone git://github.com/pallets/jinja.git`` 3. ``cd jinja2`` 4. ``ln -s jinja2 /usr/lib/python2.X/site-packages`` As an alternative to steps 4 you can also do ``python setup.py develop`` which will install the package via `distribute` in development mode. This also has the advantage that the C extensions are compiled. .. _download page: https://pypi.python.org/pypi/Jinja2 .. _distribute: https://pypi.python.org/pypi/distribute .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall .. _pip: https://pypi.python.org/pypi/pip .. _git: https://git-scm.org/ MarkupSafe Dependency ~~~~~~~~~~~~~~~~~~~~~ As of version 2.7 Jinja2 depends on the `MarkupSafe`_ module. If you install Jinja2 via `pip` or `easy_install` it will be installed automatically for you. .. _MarkupSafe: https://pypi.python.org/pypi/MarkupSafe Basic API Usage --------------- This section gives you a brief introduction to the Python API for Jinja2 templates. The most basic way to create a template and render it is through :class:`~jinja2.Template`. This however is not the recommended way to work with it if your templates are not loaded from strings but the file system or another data source: >>> from jinja2 import Template >>> template = Template('Hello {{ name }}!') >>> template.render(name='John Doe') u'Hello John Doe!' By creating an instance of :class:`~jinja2.Template` you get back a new template object that provides a method called :meth:`~jinja2.Template.render` which when called with a dict or keyword arguments expands the template. The dict or keywords arguments passed to the template are the so-called "context" of the template. What you can see here is that Jinja2 is using unicode internally and the return value is an unicode string. So make sure that your application is indeed using unicode internally. Experimental Python 3 Support ----------------------------- Jinja 2.7 brings experimental support for Python >=3.3. It means that all unittests pass on the new version, but there might still be small bugs in there and behavior might be inconsistent. If you notice any bugs, please provide feedback in the `Jinja bug tracker`_. Also please keep in mind that the documentation is written with Python 2 in mind, so you will have to adapt the shown code examples to Python 3 syntax for yourself. .. _Jinja bug tracker: https://github.com/pallets/jinja/issues Jinja2-2.10/docs/conf.py0000644000175000017500000001160713033733251015205 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- # # Jinja2 documentation build configuration file, created by # sphinx-quickstart on Sun Apr 27 21:42:41 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. sys.path.append(os.path.dirname(os.path.abspath(__file__))) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'jinjaext'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'Jinja2' copyright = '2008, Armin Ronacher' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. import pkg_resources try: release = pkg_resources.get_distribution('Jinja2').version except ImportError: print 'To build the documentation, The distribution information of Jinja2' print 'Has to be available. Either install the package into your' print 'development environment or run "setup.py develop" to setup the' print 'metadata. A virtualenv is recommended!' sys.exit(1) if 'dev' in release: release = release.split('dev')[0] + 'dev' version = '.'.join(release.split('.')[:2]) # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'jinjaext.JinjaStyle' # Options for HTML output # ----------------------- html_theme = 'jinja' html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # no modindex html_use_modindex = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. #html_use_opensearch = False # Output file base name for HTML help builder. htmlhelp_basename = 'Jinja2doc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('latexindex', 'Jinja2.tex', 'Jinja2 Documentation', 'Armin Ronacher', 'manual'), ] # Additional stuff for LaTeX latex_elements = { 'fontpkg': r'\usepackage{mathpazo}', 'papersize': 'a4paper', 'pointsize': '12pt', 'preamble': r''' \usepackage{jinjastyle} % i hate you latex \DeclareUnicodeCharacter{14D}{o} ''' } latex_use_parts = True latex_additional_files = ['jinjastyle.sty', 'logo.pdf'] # If false, no module index is generated. latex_use_modindex = False html_sidebars = { 'index': ['sidebarlogo.html', 'sidebarintro.html', 'sourcelink.html', 'searchbox.html'], '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } Jinja2-2.10/docs/integration.rst0000644000175000017500000000675513127761734017006 0ustar daviddavid00000000000000Integration =========== Jinja2 provides some code for integration into other tools such as frameworks, the `Babel`_ library or your favourite editor for fancy code highlighting. This is a brief description of whats included. Files to help integration are available `here. `_ .. _babel-integration: Babel Integration ----------------- Jinja provides support for extracting gettext messages from templates via a `Babel`_ extractor entry point called `jinja2.ext.babel_extract`. The Babel support is implemented as part of the :ref:`i18n-extension` extension. Gettext messages extracted from both `trans` tags and code expressions. To extract gettext messages from templates, the project needs a Jinja2 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 extraction 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 `extensions` value. The i18n extension is added automatically. .. versionchanged:: 2.7 Until 2.7 template syntax errors were always ignored. This was done since many people are dropping non template html files into the templates folder and it would randomly fail. The assumption was that testsuites will catch syntax errors in templates anyways. If you don't want that behavior you can add ``silent=false`` to the settings and exceptions are propagated. .. _mapping file: http://babel.pocoo.org/en/latest/messages.html#extraction-method-mapping-and-configuration Pylons ------ With `Pylons`_ 0.9.7 onwards it's incredible easy to integrate Jinja into a Pylons powered application. The template engine is configured in `config/environment.py`. The configuration for Jinja2 looks something like that:: 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 into strict mode. Per default any attribute to not existing attributes on the `c` object return an empty string and not an undefined object. To change this just use this snippet and add it into your `config/environment.py`:: config['pylons.strict_c'] = True .. _Pylons: http://www.pylonshq.com/ TextMate -------- There is a `bundle for TextMate`_ that supports syntax highlighting for Jinja1 and Jinja2 for text based templates as well as HTML. It also contains a few often used snippets. .. _bundle for TextMate: https://github.com/mitsuhiko/jinja2-tmbundle Vim --- A syntax plugin for `Vim`_ exists in the Vim-scripts directory as well as the `ext` folder at the root of the Jinja2 project. `The script `_ supports Jinja1 and Jinja2. Once installed two file types are available `jinja` and `htmljinja`. The first one for text based templates, the latter for HTML templates. Copy the files into your `syntax` folder. .. _Babel: http://babel.pocoo.org/ .. _Vim: http://www.vim.org/ Jinja2-2.10/docs/_static/0000755000175000017500000000000013200660754015332 5ustar daviddavid00000000000000Jinja2-2.10/docs/_static/.ignore0000644000175000017500000000000013033733251016601 0ustar daviddavid00000000000000Jinja2-2.10/docs/_static/jinja-small.png0000644000175000017500000002436413033733251020247 0ustar daviddavid00000000000000PNG  IHDResBIT|d pHYs~~[tEXtSoftwarewww.inkscape.org< IDATxy,Guw}{Z6AY ` 0`00 ^f 6a!60ad"C 0F I0hyBOzһۻ}{̾ٙYU}}/23:uɓ~SD\?SJNgJ)vDe+9Y85Mbr9_}:=7Slk TD~ Md/zm Z8ۦDt:t]QJ]0U96$4~:0 $va)"r"6=Ed SlDq%3y$1h" R"h[hrP)㉴r D  ̄7EP{HҟVJ=9)F.׀ |PJ$0n ;pKV#[IjGJ?4F7Qh@21yBDvvqSOPECiMv#cz Au&'ROӷ"2 B[,m1T^ﵻ#MI8$X<>~|N)uGB+mw2I䶥&:e)cm)_R׌4<ÌBu7y1R2o)CR_;T1ՠG:xu6!3)l@;po{2m`֍qvѓV-> |ٺ6E碋.:p1l2K(IKE] b[w6}J.1fK`L'njhv*5mp fmt)== u禶8ȋS36WZ[l e~UĨ_'/vyQSmN{\ ߧjn:ꡣ탱f`7.}ȕoChr_o,=uK8?cT7^9C0ꉗ6s9?7=M²S)uHZDp[)uF "_k#vq,-^zN 1ulruT^Nѫ{z51K\q.sՍPZ9B rU1BiU30y=oRq͝Nǝ跱nu6MMs P@xǵځfD >kv ƭKy+uR9^g$%/i+r#B)um@I"8=_艜 N9mWB3hĽ^^7Bތvvkv5 ? VP`"XcEwu릷u뱱1-..QS^YYa}=:Vn~|H)ƍC]!EN`UyU='OTK >fj%v,&gL6:[-ǥGMX[C{Cm:R /bUB+Dlivlk@[ҒO>p9}3>0< 8EU6taG/#V:Cbaa={$ۜu[ф^)ZX^amms%ĉlll#SBgRt7SaM;w.b,sO8aw/SJ}n0ouu=ػwoTZ8qw JȷLA!  ,zVtO>{+pT)""l}hs^iL mɸa x "`ŶU,:9U tԷk\˅~%SўR"|/po߭jTvXDda;M)d'zm"٠h:o;9pĜ]v~[Gtxf [$h!Ybmll :^΃QҞj$@t:()u;LNOvLgo.Y[[;cyy]vԙ5K#_6BDDNî]XX cMOBd'NG,8CN`-bZD-?ʉAKu*n7Uw~J4v}31~Y\\9D}1 ar-"/ϳW{iqqv _!=/SMCcǶ'Y]j2annu;~x| -;? `yyݻwײGͱo߾GyirgbD[pc#<mX1QQ?<Ŧ͍"n!iC9*l)/..$X=~1mG"vKнq7(GSO Z#DCl IR&>mZȋ.ls <FnhUvb)^ĈKT'*6%ŎV9*p,Ds˨"vIUHUu]ݓ-/``c@9y.M쒼vt_BZ?fy[ cuwmm;6"RP^9ta 5FBR-%UۖUmH!|ilBnnNOhY[[!k ѝ}R~bhة:R:UfN W tC3k׮bC'x衧Hݮ;[APZ H~}2-vSs*C49oZu'aby]T맥t*Xhc R%DT*ab:$'EntN:v.)ccޱM"cw@:s_o>Y  t`W5qZG2}(!t m'$y3ߥq( T'(~R=F$4 6.4BҺ=XyM& vEqalVJHA 1k „ڴ%hV"Hh ozT p$N$j^:l î>MM[H衐W! HhS }1r$B*m_~s|ē'}%@pqWԣO*B< BP9)*%B|xTx?mccǏI#'EU0L蔤6G -"Ik7!@ѫךf{mYq%7RcMZD~4xR۫mNLY6W/JRk &%ptyH=םJ.T_D^ x^gii)* kkk~W3QPsss6n#<9-`ee^vw99}CDB_朗ЗI7 {kfjV= 8 E$OdAF?u":tPf{9^o {RjC)uy!:ك C3MudS~>p58dvڵ!`(`m+5z=7B8hF$R'>Rr^]8%:Vso;"? MއW`ff6777hN8&K;eǀx^l~}2=zthEW*'Q C{g2>e666X^^fnnnK WsGSOR?+.:RꈈztGiM*t.Z2WR?{V0DhtC-,qF*ʔ%*}>/ 4!f ˱~ƐN6offƾ(?(.__})NJv-]* NYMFZ?RC`﷔R+loSǀJ5' hJ@w[ǎcyy{Rv%;ҳ/Árz {\]xPJ@D<xGU5#{RHl7X[cc$[]]umaݐLӶ!G),)=Z1 F$#ͧ챈܈ by9Rs,"Nԡ }iB_. Rm3Kڍ )hi^} -0"CR4rXFπ}1Io^JP=>&L:t wIBrԁ2R%tƛѫ=MMot֜:߻"d!B)u3#"G}v ޽{YXX!:7tSA A\BK 2ʿxk{3^ʾRjOFb,Id6)5ǹU sCж[i .:D/iRBҋ$tl?DB+n>nj9>Z%tdn*CU~oE 3S= .SJJG ΩNYT9%See%Z!t3PN3I _vߘ&GF/I!"^\PCv X:YhKwb| ]6]mJ<$J3h3?SB;:MYx<؎ m_3sgHhy:ft8A@z>NDY)U:6.ֽ:wE֙pEWtDZvjwpB3f?p'Ϡ}~2@y ?9}*Szl^RChk!9;T~F [t [͆0[/{-71k|~R*U J%`;)ň:n,zC uAjYCNx~2q$JT·p䀋i]9:tkJn,M4WQ{Rµy #Y Py7~c6ГoJ70s:t'jY:6)e ZXX`ccQ:."/@{E9"OjWLjnu&:x`Pʅ^?mnn.*A?A-77JjWKc܋W`NRr3~^ AadߕL;pN#GG aΖi+"\ع)-kJwE k)T^[iU'M )BM:9nz;9'?*rebHBP[Cmyr䷑6 >R V-ܴ>ui-Bgnkkkzdh{#'Fv%E(zu9u;c}'E-%u/mJگrxeKn˞=mĶZRKzPF]B߁QB488m9v,w0o{8"766{ z$o? A>mOer ]"mp 햿:ZVJ= @D|ӦHfp5&`Ki9/DH}(c=b1ջU~2YVV9-3QJ`]_0RnʁN2:`L8zf06$Ž*>PZ,}5ϾTRw/y|A]ڑGR^ ?t<ADv$:ޒR9amG1N{^^I :"SL"PJ9`!z #NYק{?fpڟ#lF&۩tٵ[[~"S;\Tƫ'`0lt7Bb@D%5%}`4flŐN.m IԬ-kJZR Z%tӪ.H遄vP 1xd:6YCe*0rH\iTQC*׾")t ;.NZڢN! UŸ:)ԑБr5ع*Gm0_!t- 1NACR[Сw;O BMf*!u+!I&7\P9JҡzVv!= GBͤO?nK҃su&v΃Z(J dw 3^NaF;TS+G I]Sk f;[_vS9_b$UIŶkn'3Pgi u ]ΫQBdB; eV9ƤCQ`ruytN򮭕NaձFt+;0:Xn&;)֐YYIr$Q[[_OCh,mpsС ;Pv}fjG5#KDž^|Z:B| ,.P9Nѡ:aTBH`sJXU~=jY__K>Yp CSbh"hd:v0֦Dja7J~\SQMvf'J4w?Dd_;T[z+"r::Nkυ"r3z5XTXUiNOO6.ΪȠdJ }ts#ut4/Is+ZD^Y,?% iWGpU){U*;WD+,@-B) \Eܦ<) =dAZXXtؽ[ipF""RsOPN[8sVGB+[u~RG(=Af]KB[H*8Jw %?ʨҧs/IozJώA|aUMG$]\&i#{o§έKPZ-Q%gt CHIر@/33T h(ÿ)J_!O*}νj›^'"O>:!wk=en2c˃2fw3.oq0oڗwM*;oJЖIey^-Uz_/6KsLMJVJN%"z?-&cOq = E1,B:\k)/A K%r?ߢs[$K 9qBYxh͐ s=f_kϫρ/ro/pVHCiNDtpф.=N" 1W:{caӝOzE~a'B۪+k4vӝkyN3cn_"HsN5IuͪpG\h#vj︈18y'J!uaD(nRRB[8Fmr =ܓȵQ6#t5CsBA# ![;.w.W3*!F-&r@@B7@B'PrJ{e? tݙ+gd`aLTJow>j;GBo|p%_PK1wvn ^LʂSiE67ѐ?L9Xy)'![U;W]#۬w5Q)u#pcm7TFLؼU&l„F_O`G&ڄVJxwm9DBg~LcBfŀYh+N+~\UQ›SltLVI5T;r*C >aS pٶ ?F,VJiEd=z%Ru-iڑ" 9xZx,mIg$rqWqIENDB`Jinja2-2.10/docs/contents.rst.inc0000644000175000017500000000042613176160444017050 0ustar daviddavid00000000000000Jinja2 Documentation -------------------- .. toctree:: :maxdepth: 2 intro api sandbox nativetypes templates extensions integration switching tricks Additional Information ---------------------- .. toctree:: :maxdepth: 2 faq changelog Jinja2-2.10/docs/extensions.rst0000644000175000017500000002574613176154260016655 0ustar daviddavid00000000000000.. _jinja-extensions: Extensions ========== Jinja2 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 Jinja2 environment at creation time. Once the environment is created additional extensions cannot be added. To add an extension pass a list of extension classes or import paths to the `extensions` parameter of the :class:`Environment` constructor. The following example creates a Jinja2 environment with the i18n extension loaded:: jinja_env = Environment(extensions=['jinja2.ext.i18n']) .. _i18n-extension: i18n Extension -------------- **Import name:** `jinja2.ext.i18n` The i18n extension can be used in combination with `gettext`_ or `babel`_. If the i18n extension is enabled Jinja2 provides a `trans` statement that marks the wrapped string as translatable and calls `gettext`. After enabling, dummy `_` function that forwards calls to `gettext` is added to the environment globals. An internationalized application then has to provide a `gettext` function and optionally an `ngettext` function into the namespace, either globally or for each rendering. 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 that environment. The translations object provided must implement at least `ugettext` and `ungettext`. The `gettext.NullTranslations` and `gettext.GNUTranslations` classes as well as `Babel`_\s `Translations` class are supported. .. versionchanged:: 2.5 newstyle gettext added .. method:: jinja2.Environment.install_null_translations(newstyle=False) Install dummy gettext functions. This is useful if you want to prepare the application for internationalization but don't want to implement the full internationalization system yet. .. versionchanged:: 2.5 newstyle gettext added .. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False) Installs the given `gettext` and `ngettext` callables into the environment as globals. They are supposed to behave exactly like the standard library's :func:`gettext.ugettext` and :func:`gettext.ungettext` functions. If `newstyle` is activated, the callables are wrapped to work like newstyle callables. See :ref:`newstyle-gettext` for more information. .. versionadded:: 2.5 .. method:: jinja2.Environment.uninstall_gettext_translations() Uninstall the translations again. .. 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), and * `message` is the string itself (a `unicode` object, or a tuple of `unicode` objects for functions with multiple string arguments). If `Babel`_ is installed, :ref:`the babel integration ` can be used to extract strings for babel. For a web application that is available in multiple languages but gives all the users the same language (for example a multilingual forum software installed for a French community) may load the translations once and add the translation methods to the environment at environment generation time:: 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 as part :ref:`of the template documentation `. .. _gettext: https://docs.python.org/dev/library/gettext .. _Babel: http://babel.pocoo.org/ .. _newstyle-gettext: Whitespace Trimming ~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.10 Linebreaks and surrounding whitespace can be automatically trimmed by enabling the ``ext.i18n.trimmed`` :ref:`policy `. Newstyle Gettext ~~~~~~~~~~~~~~~~ .. versionadded:: 2.5 Starting with version 2.5 you can use newstyle gettext calls. These are inspired by trac's internal gettext functions and are fully supported by the babel extraction tool. They might not work as expected by other extraction tools in case you are not using Babel's. What's the big difference between standard and newstyle gettext calls? In general they are less to type and less error prone. Also if they are used in an autoescaping environment they better support automatic escaping. Here are some common differences between old and new calls: standard gettext: .. sourcecode:: html+jinja {{ gettext('Hello World!') }} {{ gettext('Hello %(name)s!')|format(name='World') }} {{ ngettext('%(num)d apple', '%(num)d apples', apples|count)|format( num=apples|count )}} newstyle gettext looks like this instead: .. sourcecode:: html+jinja {{ gettext('Hello World!') }} {{ gettext('Hello %(name)s!', name='World') }} {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} The advantages of newstyle gettext are that you have less to type and that named placeholders become mandatory. The latter sounds like a disadvantage but solves a lot of troubles translators are often facing when they are unable to switch the positions of two placeholder. With newstyle gettext, all format strings look the same. Furthermore with newstyle gettext, string formatting is also used if no placeholders are used which makes all strings behave exactly the same. Last but not least are newstyle gettext calls able to properly mark strings for autoescaping which solves lots of escaping related issues many templates are experiencing over time when using autoescaping. 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, Jinja2 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. .. _writing-extensions: Writing Extensions ------------------ .. module:: jinja2.ext By writing extensions you can add custom tags to Jinja2. 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 Jinja2 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 Extension ~~~~~~~~~~~~~~~~~ The following example implements a `cache` tag for Jinja2 by using the `Werkzeug`_ caching contrib module: .. literalinclude:: cache_extension.py :language: python And here is how you use it in an environment:: from jinja2 import Environment from werkzeug.contrib.cache 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 %} .. _Werkzeug: http://werkzeug.pocoo.org/ Extension API ~~~~~~~~~~~~~ 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 API ~~~~~~~~~~ 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 Jinja2 versions but will stay backwards compatible. For more information have a look at the repr of :meth:`jinja2.Environment.parse`. .. module:: jinja2.nodes .. jinjanodes:: .. autoexception:: Impossible Jinja2-2.10/docs/faq.rst0000644000175000017500000001766713130203104015206 0ustar daviddavid00000000000000Frequently Asked Questions ========================== This page answers some of the often asked questions about Jinja. .. highlight:: html+jinja Why is it called Jinja? ----------------------- The name Jinja was chosen because it's the name of a Japanese temple and temple and template share a similar pronunciation. It is not named after the city in Uganda. How fast is it? --------------- We really hate benchmarks especially since they don't reflect much. The performance of a template depends on many factors and you would have to benchmark different engines in different situations. The benchmarks from the testsuite show that Jinja2 has a similar performance to `Mako`_ and is between 10 and 20 times faster than Django's template engine or Genshi. These numbers should be taken with tons of salt as the benchmarks that took these numbers only test a few performance related situations such as looping. Generally speaking the performance of a template engine doesn't matter much as the usual bottleneck in a web application is either the database or the application code. .. _Mako: http://www.makotemplates.org/ How Compatible is Jinja2 with Django? ------------------------------------- The default syntax of Jinja2 matches Django syntax in many ways. However this similarity doesn't mean that you can use a Django template unmodified in Jinja2. For example filter arguments use a function call syntax rather than a colon to separate filter name and arguments. Additionally the extension interface in Jinja is fundamentally different from the Django one which means that your custom tags won't work any longer. Generally speaking you will use much less custom extensions as the Jinja template system allows you to use a certain subset of Python expressions which can replace most Django extensions. For example instead of using something like this:: {% load comments %} {% get_latest_comments 10 as latest_comments %} {% for comment in latest_comments %} ... {% endfor %} You will most likely provide an object with attributes to retrieve comments from the database:: {% for comment in models.comments.latest(10) %} ... {% endfor %} Or directly provide the model for quick testing:: {% for comment in Comment.objects.order_by('-pub_date')[:10] %} ... {% endfor %} Please keep in mind that even though you may put such things into templates it still isn't a good idea. Queries should go into the view code and not the template! Isn't it a terrible idea to put Logic into Templates? ----------------------------------------------------- Without a doubt you should try to remove as much logic from templates as possible. But templates without any logic mean that you have to do all the processing in the code which is boring and stupid. A template engine that does that is shipped with Python and called `string.Template`. Comes without loops and if conditions and is by far the fastest template engine you can get for Python. So some amount of logic is required in templates to keep everyone happy. And Jinja leaves it pretty much to you how much logic you want to put into templates. There are some restrictions in what you can do and what not. Jinja2 neither allows you to put arbitrary Python code into templates nor does it allow all Python expressions. The operators are limited to the most common ones and more advanced expressions such as list comprehensions and generator expressions are not supported. This keeps the template engine easier to maintain and templates more readable. Why is Autoescaping not the Default? ------------------------------------ There are multiple reasons why automatic escaping is not the default mode and also not the recommended one. While automatic escaping of variables means that you will less likely have an XSS problem it also causes a huge amount of extra processing in the template engine which can cause serious performance problems. As Python doesn't provide a way to mark strings as unsafe Jinja has to hack around that limitation by providing a custom string class (the :class:`Markup` string) that safely interacts with safe and unsafe strings. With explicit escaping however the template engine doesn't have to perform any safety checks on variables. Also a human knows not to escape integers or strings that may never contain characters one has to escape or already HTML markup. For example when iterating over a list over a table of integers and floats for a table of statistics the template designer can omit the escaping because he knows that integers or floats don't contain any unsafe parameters. Additionally Jinja2 is a general purpose template engine and not only used for HTML/XML generation. For example you may generate LaTeX, emails, CSS, JavaScript, or configuration files. Why is the Context immutable? ----------------------------- When writing a :func:`contextfunction` or something similar you may have noticed that the context tries to stop you from modifying it. If you have managed to modify the context by using an internal context API you may have noticed that changes in the context don't seem to be visible in the template. The reason for this is that Jinja uses the context only as primary data source for template variables for performance reasons. If you want to modify the context write a function that returns a variable instead that one can assign to a variable by using set:: {% set comments = get_latest_comments() %} My tracebacks look weird. What's happening? -------------------------------------------- If the debugsupport module is not compiled and you are using a Python installation without ctypes (Python 2.4 without ctypes, Jython or Google's AppEngine) Jinja2 is unable to provide correct debugging information and the traceback may be incomplete. There is currently no good workaround for Jython or the AppEngine as ctypes is unavailable there and it's not possible to use the debugsupport extension. If you are working in the Google AppEngine development server you can whitelist the ctypes module to restore the tracebacks. This however won't work in production environments:: import os if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'): from google.appengine.tools.devappserver2.python import sandbox sandbox._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt'] Credit for this snippet goes to `Thomas Johansson `_ Why is there no Python 2.3/2.4/2.5/3.1/3.2 support? --------------------------------------------------- Python 2.3 is missing a lot of features that are used heavily in Jinja2. This decision was made as with the upcoming Python 2.6 and 3.0 versions it becomes harder to maintain the code for older Python versions. If you really need Python 2.3 support you either have to use `Jinja 1`_ or other templating engines that still support 2.3. Python 2.4/2.5/3.1/3.2 support was removed when we switched to supporting Python 2 and 3 by the same sourcecode (without using 2to3). It was required to drop support because only Python 2.6/2.7 and >=3.3 support byte and unicode literals in a way compatible to each other version. If you really need support for older Python 2 (or 3) versions, you can just use Jinja2 2.6. My Macros are overridden by something ------------------------------------- In some situations the Jinja scoping appears arbitrary: layout.tmpl: .. sourcecode:: jinja {% macro foo() %}LAYOUT{% endmacro %} {% block body %}{% endblock %} child.tmpl: .. sourcecode:: jinja {% extends 'layout.tmpl' %} {% macro foo() %}CHILD{% endmacro %} {% block body %}{{ foo() }}{% endblock %} This will print ``LAYOUT`` in Jinja2. This is a side effect of having the parent template evaluated after the child one. This allows child templates passing information to the parent template. To avoid this issue rename the macro or variable in the parent template to have an uncommon prefix. .. _Jinja 1: http://jinja.pocoo.org/1/ Jinja2-2.10/docs/tricks.rst0000644000175000017500000000621513033733251015736 0ustar daviddavid00000000000000Tips and Tricks =============== .. highlight:: html+jinja This part of the documentation shows some tips and tricks for Jinja2 templates. .. _null-master-fallback: Null-Master Fallback -------------------- Jinja2 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 'master.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 amount 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 }}
    Jinja2-2.10/docs/switching.rst0000644000175000017500000001563313127233332016441 0ustar daviddavid00000000000000Switching from other Template Engines ===================================== .. highlight:: html+jinja If you have used a different template engine in the past and want to switch to Jinja2 here is a small guide that shows the basic syntactic and semantic changes between some common, similar text template engines for Python. Jinja1 ------ Jinja2 is mostly compatible with Jinja1 in terms of API usage and template syntax. The differences between Jinja1 and 2 are explained in the following list. API ~~~ Loaders Jinja2 uses a different loader API. Because the internal representation of templates changed there is no longer support for external caching systems such as memcached. The memory consumed by templates is comparable with regular Python modules now and external caching doesn't give any advantage. If you have used a custom loader in the past have a look at the new :ref:`loader API `. Loading templates from strings In the past it was possible to generate templates from a string with the default environment configuration by using `jinja.from_string`. Jinja2 provides a :class:`Template` class that can be used to do the same, but with optional additional configuration. Automatic unicode conversion Jinja1 performed automatic conversion of bytestrings in a given encoding into unicode objects. This conversion is no longer implemented as it was inconsistent as most libraries are using the regular Python ASCII bytestring to Unicode conversion. An application powered by Jinja2 *has to* use unicode internally everywhere or make sure that Jinja2 only gets unicode strings passed. i18n Jinja1 used custom translators for internationalization. i18n is now available as Jinja2 extension and uses a simpler, more gettext friendly interface and has support for babel. For more details see :ref:`i18n-extension`. Internal methods Jinja1 exposed a few internal methods on the environment object such as `call_function`, `get_attribute` and others. While they were marked as being an internal method it was possible to override them. Jinja2 doesn't have equivalent methods. Sandbox Jinja1 was running sandbox mode by default. Few applications actually used that feature so it became optional in Jinja2. For more details about the sandboxed execution see :class:`SandboxedEnvironment`. Context Jinja1 had a stacked context as storage for variables passed to the environment. In Jinja2 a similar object exists but it doesn't allow modifications nor is it a singleton. As inheritance is dynamic now multiple context objects may exist during template evaluation. Filters and Tests Filters and tests are regular functions now. It's no longer necessary and allowed to use factory functions. Templates ~~~~~~~~~ Jinja2 has mostly the same syntax as Jinja1. What's different is that macros require parentheses around the argument list now. Additionally Jinja2 allows dynamic inheritance now and dynamic includes. The old helper function `rendertemplate` is gone now, `include` can be used instead. Includes no longer import macros and variable assignments, for that the new `import` tag is used. This concept is explained in the :ref:`import` documentation. Another small change happened in the `for`-tag. The special loop variable doesn't have a `parent` attribute, instead you have to alias the loop yourself. See :ref:`accessing-the-parent-loop` for more details. Django ------ If you have previously worked with Django templates, you should find Jinja2 very familiar. In fact, most of the syntax elements look and work the same. However, Jinja2 provides some more syntax elements covered in the documentation and some work a bit different. This section covers the template changes. As the API is fundamentally different we won't cover it here. Method Calls ~~~~~~~~~~~~ In Django method calls work implicitly, while Jinja requires the explicit Python syntax. Thus this Django code:: {% for page in user.get_created_pages %} ... {% endfor %} ...looks like this in Jinja:: {% for page in user.get_created_pages() %} ... {% endfor %} This allows you to pass variables to the method, which is not possible in Django. This syntax is also used for macros. Filter Arguments ~~~~~~~~~~~~~~~~ Jinja2 provides more than one argument for filters. Also the syntax for argument passing is different. A template that looks like this in Django:: {{ items|join:", " }} looks like this in Jinja2:: {{ items|join(', ') }} It is a bit more verbose, but it allows different types of arguments - including variables - and more than one of them. Tests ~~~~~ In addition to filters there also are tests you can perform using the is operator. Here are some examples:: {% if user.user_id is odd %} {{ user.username|e }} is odd {% else %} hmm. {{ user.username|e }} looks pretty normal {% endif %} Loops ~~~~~ For loops work very similarly to Django, but notably the Jinja2 special variable for the loop context is called `loop`, not `forloop` as in Django. In addition, the Django `empty` argument is called `else` in Jinja2. For example, the Django template:: {% for item in items %} {{ item }} {% empty %} No items! {% endfor %} ...looks like this in Jinja2:: {% for item in items %} {{ item }} {% else %} No items! {% endfor %} Cycle ~~~~~ The ``{% cycle %}`` tag does not exist in Jinja2; however, you can achieve the same output by using the `cycle` method on the loop context special variable. The following Django template:: {% for user in users %}
  • {{ user }}
  • {% endfor %} ...looks like this in Jinja2:: {% for user in users %}
  • {{ user }}
  • {% endfor %} There is no equivalent of ``{% cycle ... as variable %}``. Mako ---- .. highlight:: html+mako If you have used Mako so far and want to switch to Jinja2 you can configure Jinja2 to look more like Mako: .. sourcecode:: python env = Environment('<%', '%>', '${', '}', '<%doc>', '', '%', '##') With an environment configured like that, Jinja2 should be able to interpret a small subset of Mako templates. Jinja2 does not support embedded Python code, so you would have to move that out of the template. The syntax for defs (which are called macros in Jinja2) and template inheritance is different too. The following Mako template:: <%inherit file="layout.html" /> <%def name="title()">Page Title
      % for item in list:
    • ${item}
    • % endfor
    Looks like this in Jinja2 with the above configuration:: <% extends "layout.html" %> <% block title %>Page Title<% endblock %> <% block body %>
      % for item in list:
    • ${item}
    • % endfor
    <% endblock %> Jinja2-2.10/docs/nativetypes.rst0000644000175000017500000000303513176160444017015 0ustar daviddavid00000000000000.. 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 Jinja2-2.10/docs/cache_extension.py0000644000175000017500000000412113033733251017410 0ustar daviddavid00000000000000from jinja2 import nodes from jinja2.ext import Extension class FragmentCacheExtension(Extension): # a set of names that trigger the extension. tags = set(['cache']) def __init__(self, environment): super(FragmentCacheExtension, self).__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 Jinja2-2.10/docs/logo.pdf0000644000175000017500000001305513033733251015340 0ustar daviddavid00000000000000%PDF-1.4 % 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream x}Zˮ+֛YJ`5<TU=`sE~T7}kW:ՅAmߟߨB{iycl[queU8=p ܒľ9d&u =fL:pBûF[0q c@ r&>bLz\{%h7A\꾹7ɭ9뛠=ا{|Vg{1GJ{N~3~l`s~`, aO~l5wIaKp=FL2mϬMC@+I03 [>I?<> ly[+ݒ%b֛z~N&ieqzQ$3So8}<2_0OO/+i/#5F[~{r>(NyAY/[qp~.l7#Lì%yȠ\΄\^@Q}r@o1mR6M}R>uJ HoWըْQ.J<%(ABaF7o̘?!I3R@9$tna̶3>,4|Q!Sd"7*wCL Y̠ʜpAkfq"s@6-t`l{x*~BC)Q7|(XLKl5^ܰny5_(t^S'̹IiCP Sg(T+ ZmB(GVdq z2!K0 m52BdS S%`GmJ-go.P"Ӡ#srPsd[Ҽzjh.me9z˃:p_}=<̰ȅN wססƴuuhryF:w37x+o: +c~ֱ &kqĠfc0ݿ u5! 7p5~z\EY&B[]'YHQdR5bUkD&{c 6Ŧ.wu򄒕ʥnm`/J2`fYa7O-M$F'vK"2t0w ]!t䓘eM^Ex+R6(s ;..Slf3&iuբ+~n.Ӭ/{U 'BFm=zSk,o0*WWqXumeR5k)09f`fgo߇(V&`^YIR|S.qWLjb%ƼUB(޺i_lbײo}@`1D3UoO} MHI 3T fDU Bnx 52;fQ7ws ⺱odM+!MaU#:T I|7'f.COژ, } p_zVȮiNj螎cYKJD2Oobe|މ芟ǬfB㹓MW?y-Ǜ<( <չFe;ly]S+NAA?uP!a?Y1rV4Zy&#n368M=^ӧi\*:]7r6K9!IP]Nal3ܱ3YtgWVlG)WkE4]6!~X(vl^!i}igӖ>a+ ]l&͔~ n!gG ªXwvMAڪOI׻`zdfDupU~] 'I}Q> }_Ta8-+% &c+[Biu[CXϺRo0?H=I\N5jQ}V&?ǚ"1"XI1) f%]0ڮZ[u}nE\t]`>OBzzo>Vuo3d"(mZ0ܘzDkH D}_[&fʚ;53[6 T 2 ۼca+xJyx)i3{=3xiz~p?X\ endstream endobj 4 0 obj 4293 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Pattern << /p5 5 0 R >> >> endobj 6 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 240 96 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 2 0 R >> endobj 7 0 obj << /FunctionType 2 /Domain [ 0 1 ] /C0 [ 0.341176 0.341176 0.341176 ] /C1 [ 0.184314 0.184314 0.184314 ] /N 1 >> endobj 5 0 obj << /Type /Pattern /PatternType 2 /Matrix [ 0.8 0 0 -0.756083 0 93.325002 ] /Shading << /ShadingType 3 /ColorSpace /DeviceRGB /Coords [ 61.296875 60.910156 0 61.296875 60.910156 44.6875 ] /Function 7 0 R /Extend [ true true ] >> >> endobj 1 0 obj << /Type /Pages /Kids [ 6 0 R ] /Count 1 >> endobj 8 0 obj << /Creator (cairo 1.8.10 (http://cairographics.org)) /Producer (cairo 1.8.10 (http://cairographics.org)) >> endobj 9 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 10 0000000000 65535 f 0000005149 00000 n 0000004407 00000 n 0000000015 00000 n 0000004384 00000 n 0000004846 00000 n 0000004507 00000 n 0000004706 00000 n 0000005214 00000 n 0000005341 00000 n trailer << /Size 10 /Root 9 0 R /Info 8 0 R >> startxref 5393 %%EOF Jinja2-2.10/docs/Makefile0000644000175000017500000001007613033733251015345 0ustar daviddavid00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask" @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." latexpdf: latex $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo "Running LaTeX files through pdflatex..." make -C _build/latex all-pdf @echo "pdflatex finished; the PDF files are in _build/latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." Jinja2-2.10/docs/index.rst0000644000175000017500000000174313033733251015547 0ustar daviddavid00000000000000Welcome to Jinja2 ================= Jinja2 is a modern and designer-friendly templating language for Python, modelled after Django's templates. It is fast, widely used and secure with the optional sandboxed template execution environment: .. sourcecode:: html+jinja {% block title %}{% endblock %} **Features:** - sandboxed execution - powerful automatic HTML escaping system for XSS prevention - template inheritance - compiles down to the optimal python code just in time - optional ahead-of-time template compilation - easy to debug. Line numbers of exceptions directly point to the correct line in the template. - configurable syntax .. include:: contents.rst.inc If you can't find the information you're looking for, have a look at the index or try to find it using the search function: * :ref:`genindex` * :ref:`search` Jinja2-2.10/docs/changelog.rst0000644000175000017500000000006013200657500016354 0ustar daviddavid00000000000000.. module:: jinja2 .. include:: ../CHANGES.rst Jinja2-2.10/docs/jinjastyle.sty0000644000175000017500000000611513033733251016621 0ustar daviddavid00000000000000\definecolor{TitleColor}{rgb}{0,0,0} \definecolor{InnerLinkColor}{rgb}{0,0,0} \definecolor{OuterLinkColor}{rgb}{0.8,0,0} \renewcommand{\maketitle}{% \begin{titlepage}% \let\footnotesize\small \let\footnoterule\relax \ifsphinxpdfoutput \begingroup % This \def is required to deal with multi-line authors; it % changes \\ to ', ' (comma-space), making it pass muster for % generating document info in the PDF file. \def\\{, } \pdfinfo{ /Author (\@author) /Title (\@title) } \endgroup \fi \begin{flushright}% %\sphinxlogo% {\center \vspace*{3cm} \includegraphics{logo.pdf} \vspace{3cm} \par {\rm\Huge \@title \par}% {\em\LARGE \py@release\releaseinfo \par} {\large \@date \par \py@authoraddress \par }}% \end{flushright}%\par \@thanks \end{titlepage}% \cleardoublepage% \setcounter{footnote}{0}% \let\thanks\relax\let\maketitle\relax %\gdef\@thanks{}\gdef\@author{}\gdef\@title{} } \fancypagestyle{normal}{ \fancyhf{} \fancyfoot[LE,RO]{{\thepage}} \fancyfoot[LO]{{\nouppercase{\rightmark}}} \fancyfoot[RE]{{\nouppercase{\leftmark}}} \fancyhead[LE,RO]{{ \@title, \py@release}} \renewcommand{\headrulewidth}{0.4pt} \renewcommand{\footrulewidth}{0.4pt} } \fancypagestyle{plain}{ \fancyhf{} \fancyfoot[LE,RO]{{\thepage}} \renewcommand{\headrulewidth}{0pt} \renewcommand{\footrulewidth}{0.4pt} } \titleformat{\section}{\Large}% {\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor} \titleformat{\subsection}{\large}% {\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} \titleformat{\subsubsection}{}% {\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} \titleformat{\paragraph}{\large}% {\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor} \ChNameVar{\raggedleft\normalsize} \ChNumVar{\raggedleft \bfseries\Large} \ChTitleVar{\raggedleft \rm\Huge} \renewcommand\thepart{\@Roman\c@part} \renewcommand\part{% \pagestyle{plain} \if@noskipsec \leavevmode \fi \cleardoublepage \vspace*{6cm}% \@afterindentfalse \secdef\@part\@spart} \def\@part[#1]#2{% \ifnum \c@secnumdepth >\m@ne \refstepcounter{part}% \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% \else \addcontentsline{toc}{part}{#1}% \fi {\parindent \z@ %\center \interlinepenalty \@M \normalfont \ifnum \c@secnumdepth >\m@ne \rm\Large \partname~\thepart \par\nobreak \fi \MakeUppercase{\rm\Huge #2}% \markboth{}{}\par}% \nobreak \vskip 8ex \@afterheading} \def\@spart#1{% {\parindent \z@ %\center \interlinepenalty \@M \normalfont \huge \bfseries #1\par}% \nobreak \vskip 3ex \@afterheading} % use inconsolata font \usepackage{inconsolata} % fix single quotes, for inconsolata. (does not work) %%\usepackage{textcomp} %%\begingroup %% \catcode`'=\active %% \g@addto@macro\@noligs{\let'\textsinglequote} %% \endgroup %%\endinput Jinja2-2.10/ext/0000755000175000017500000000000013200660754013554 5ustar daviddavid00000000000000Jinja2-2.10/ext/inlinegettext.py0000644000175000017500000000507713033733251017017 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ Inline Gettext ~~~~~~~~~~~~~~ An example extension for Jinja2 that supports inline gettext calls. Requires the i18n extension to be loaded. :copyright: (c) 2009 by the Jinja Team. :license: BSD. """ import re from jinja2.ext import Extension from jinja2.lexer import Token, count_newlines from jinja2.exceptions import TemplateSyntaxError _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 is not 'data': yield token continue pos = 0 lineno = token.lineno while 1: 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 += gtok == ')' and -1 or 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) Jinja2-2.10/ext/django2jinja/0000755000175000017500000000000013200660754016114 5ustar daviddavid00000000000000Jinja2-2.10/ext/django2jinja/templates/0000755000175000017500000000000013200660754020112 5ustar daviddavid00000000000000Jinja2-2.10/ext/django2jinja/templates/subtemplate.html0000644000175000017500000000001513033733251023316 0ustar daviddavid00000000000000Hello World! Jinja2-2.10/ext/django2jinja/templates/index.html0000644000175000017500000000276413033733251022115 0ustar daviddavid00000000000000{% extends "layout.html" %} {% load i18n %} {% block title %}Foo{% endblock %} {% block page-body %} {{ block.super }} Hello {{ name|cut:"d" }}! {% for item in seq reversed %} {% if forloop.index|divisibleby:2 %}
  • {{ item }}
  • {% endif %} {% endfor %} {% ifequal foo bar %} haha {% else %} hmm {% endifequal %} {% filter upper %} {% include "subtemplate.html" %} {% include foo %} {% endfilter %} {% spaceless %} Hello World {{ foo }} Hmm {% endspaceless %} {% templatetag opencomment %}...{% templatetag closecomment %} {% url foo a, b, c=d %} {% url foo a, b, c=d as hmm %} {% with object.value as value %} {% endwith %}
    {% debug %}
    {% blocktrans with book|title as book_t and author|title as author_t %} This is {{ book_t }} by {{ author_t }} {% endblocktrans %} {% blocktrans count list|length as counter %} There is only one {{ name }} object. {% plural %} There are {{ counter }} {{ name }} objects. {% endblocktrans %} {% blocktrans with name|escape as name count list|length as counter %} There is only one {{ name }} object. {% plural %} There are {{ counter }} {{ name }} objects. {% endblocktrans %} {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}

    {% trans "This is the title." %}

    {% regroup people by gender as grouped %} {% endblock %} Jinja2-2.10/ext/django2jinja/templates/layout.html0000644000175000017500000000015713033733251022315 0ustar daviddavid00000000000000{% block title %}{% endblock %}
    {% block page-body %}{% endblock %}
    Jinja2-2.10/ext/django2jinja/example.py0000644000175000017500000000036413033733251020121 0ustar daviddavid00000000000000from django.conf import settings settings.configure(TEMPLATE_DIRS=['templates'], TEMPLATE_DEBUG=True) from django2jinja import convert_templates, Writer writer = Writer(use_jinja_autoescape=True) convert_templates('converted', writer=writer) Jinja2-2.10/ext/django2jinja/django2jinja.py0000644000175000017500000006024713033733251021034 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ Django to Jinja ~~~~~~~~~~~~~~~ Helper module that can convert django templates into Jinja2 templates. This file is not intended to be used as stand alone application but to be used as library. To convert templates you basically create your own writer, add extra conversion logic for your custom template tags, configure your django environment and run the `convert_templates` function. Here a simple example:: # configure django (or use settings.configure) import os os.environ['DJANGO_SETTINGS_MODULE'] = 'yourapplication.settings' from yourapplication.foo.templatetags.bar import MyNode from django2jinja import Writer, convert_templates def write_my_node(writer, node): writer.start_variable() writer.write('myfunc(') for idx, arg in enumerate(node.args): if idx: writer.write(', ') writer.node(arg) writer.write(')') writer.end_variable() writer = Writer() writer.node_handlers[MyNode] = write_my_node convert_templates('/path/to/output/folder', writer=writer) Here is an example hos to automatically translate your django variables to jinja2:: import re # List of tuple (Match pattern, Replace pattern, Exclusion pattern) var_re = ((re.compile(r"(u|user)\.is_authenticated"), r"\1.is_authenticated()", None), (re.compile(r"\.non_field_errors"), r".non_field_errors()", None), (re.compile(r"\.label_tag"), r".label_tag()", None), (re.compile(r"\.as_dl"), r".as_dl()", None), (re.compile(r"\.as_table"), r".as_table()", None), (re.compile(r"\.as_widget"), r".as_widget()", None), (re.compile(r"\.as_hidden"), r".as_hidden()", None), (re.compile(r"\.get_([0-9_\w]+)_url"), r".get_\1_url()", None), (re.compile(r"\.url"), r".url()", re.compile(r"(form|calendar).url")), (re.compile(r"\.get_([0-9_\w]+)_display"), r".get_\1_display()", None), (re.compile(r"loop\.counter"), r"loop.index", None), (re.compile(r"loop\.revcounter"), r"loop.revindex", None), (re.compile(r"request\.GET\.([0-9_\w]+)"), r"request.GET.get('\1', '')", None), (re.compile(r"request\.get_host"), r"request.get_host()", None), (re.compile(r"\.all(?!_)"), r".all()", None), (re.compile(r"\.all\.0"), r".all()[0]", None), (re.compile(r"\.([0-9])($|\s+)"), r"[\1]\2", None), (re.compile(r"\.items"), r".items()", None), ) writer = Writer(var_re=var_re) For details about the writing process have a look at the module code. :copyright: (c) 2009 by the Jinja Team. :license: BSD. """ import re import os import sys from jinja2.defaults import * from django.conf import settings from django.template import defaulttags as core_tags, loader, TextNode, \ FilterExpression, libraries, Variable, loader_tags, TOKEN_TEXT, \ TOKEN_VAR from django.template.debug import DebugVariableNode as VariableNode from django.templatetags import i18n as i18n_tags from StringIO import StringIO _node_handlers = {} _resolved_filters = {} _newline_re = re.compile(r'(?:\r\n|\r|\n)') # Django stores an itertools object on the cycle node. Not only is this # thread unsafe but also a problem for the converter which needs the raw # string values passed to the constructor to create a jinja loop.cycle() # call from it. _old_cycle_init = core_tags.CycleNode.__init__ def _fixed_cycle_init(self, cyclevars, variable_name=None): self.raw_cycle_vars = map(Variable, cyclevars) _old_cycle_init(self, cyclevars, variable_name) core_tags.CycleNode.__init__ = _fixed_cycle_init def node(cls): def proxy(f): _node_handlers[cls] = f return f return proxy def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None, callback=None): """Iterates over all templates in the template dirs configured and translates them and writes the new templates into the output directory. """ if writer is None: writer = Writer() def filter_templates(files): for filename in files: ifilename = filename.lower() for extension in extensions: if ifilename.endswith(extension): yield filename def translate(f, loadname): template = loader.get_template(loadname) original = writer.stream writer.stream = f writer.body(template.nodelist) writer.stream = original if callback is None: def callback(template): print template for directory in settings.TEMPLATE_DIRS: for dirname, _, files in os.walk(directory): dirname = dirname[len(directory) + 1:] for filename in filter_templates(files): source = os.path.normpath(os.path.join(dirname, filename)) target = os.path.join(output_dir, dirname, filename) basetarget = os.path.dirname(target) if not os.path.exists(basetarget): os.makedirs(basetarget) callback(source) f = file(target, 'w') try: translate(f, source) finally: f.close() class Writer(object): """The core writer class.""" def __init__(self, stream=None, error_stream=None, block_start_string=BLOCK_START_STRING, block_end_string=BLOCK_END_STRING, variable_start_string=VARIABLE_START_STRING, variable_end_string=VARIABLE_END_STRING, comment_start_string=COMMENT_START_STRING, comment_end_string=COMMENT_END_STRING, initial_autoescape=True, use_jinja_autoescape=False, custom_node_handlers=None, var_re=[], env=None): if stream is None: stream = sys.stdout if error_stream is None: error_stream = sys.stderr self.stream = stream self.error_stream = error_stream self.block_start_string = block_start_string self.block_end_string = block_end_string self.variable_start_string = variable_start_string self.variable_end_string = variable_end_string self.comment_start_string = comment_start_string self.comment_end_string = comment_end_string self.autoescape = initial_autoescape self.spaceless = False self.use_jinja_autoescape = use_jinja_autoescape self.node_handlers = dict(_node_handlers, **(custom_node_handlers or {})) self._loop_depth = 0 self._filters_warned = set() self.var_re = var_re self.env = env def enter_loop(self): """Increments the loop depth so that write functions know if they are in a loop. """ self._loop_depth += 1 def leave_loop(self): """Reverse of enter_loop.""" self._loop_depth -= 1 @property def in_loop(self): """True if we are in a loop.""" return self._loop_depth > 0 def write(self, s): """Writes stuff to the stream.""" self.stream.write(s.encode(settings.FILE_CHARSET)) def print_expr(self, expr): """Open a variable tag, write to the string to the stream and close.""" self.start_variable() self.write(expr) self.end_variable() def _post_open(self): if self.spaceless: self.write('- ') else: self.write(' ') def _pre_close(self): if self.spaceless: self.write(' -') else: self.write(' ') def start_variable(self): """Start a variable.""" self.write(self.variable_start_string) self._post_open() def end_variable(self, always_safe=False): """End a variable.""" if not always_safe and self.autoescape and \ not self.use_jinja_autoescape: self.write('|e') self._pre_close() self.write(self.variable_end_string) def start_block(self): """Starts a block.""" self.write(self.block_start_string) self._post_open() def end_block(self): """Ends a block.""" self._pre_close() self.write(self.block_end_string) def tag(self, name): """Like `print_expr` just for blocks.""" self.start_block() self.write(name) self.end_block() def variable(self, name): """Prints a variable. This performs variable name transformation.""" self.write(self.translate_variable_name(name)) def literal(self, value): """Writes a value as literal.""" value = repr(value) if value[:2] in ('u"', "u'"): value = value[1:] self.write(value) def filters(self, filters, is_block=False): """Dumps a list of filters.""" want_pipe = not is_block for filter, args in filters: name = self.get_filter_name(filter) if name is None: self.warn('Could not find filter %s' % name) continue if name not in DEFAULT_FILTERS and \ name not in self._filters_warned: self._filters_warned.add(name) self.warn('Filter %s probably doesn\'t exist in Jinja' % name) if not want_pipe: want_pipe = True else: self.write('|') self.write(name) if args: self.write('(') for idx, (is_var, value) in enumerate(args): if idx: self.write(', ') if is_var: self.node(value) else: self.literal(value) self.write(')') def get_location(self, origin, position): """Returns the location for an origin and position tuple as name and lineno. """ if hasattr(origin, 'source'): source = origin.source name = '' else: source = origin.loader(origin.loadname, origin.dirs)[0] name = origin.loadname lineno = len(_newline_re.findall(source[:position[0]])) + 1 return name, lineno def warn(self, message, node=None): """Prints a warning to the error stream.""" if node is not None and hasattr(node, 'source'): filename, lineno = self.get_location(*node.source) message = '[%s:%d] %s' % (filename, lineno, message) print >> self.error_stream, message def translate_variable_name(self, var): """Performs variable name translation.""" if self.in_loop and var == 'forloop' or var.startswith('forloop.'): var = var[3:] for reg, rep, unless in self.var_re: no_unless = unless and unless.search(var) or True if reg.search(var) and no_unless: var = reg.sub(rep, var) break return var def get_filter_name(self, filter): """Returns the filter name for a filter function or `None` if there is no such filter. """ if filter not in _resolved_filters: for library in libraries.values(): for key, value in library.filters.iteritems(): _resolved_filters[value] = key return _resolved_filters.get(filter, None) def node(self, node): """Invokes the node handler for a node.""" for cls, handler in self.node_handlers.iteritems(): if type(node) is cls or type(node).__name__ == cls: handler(self, node) break else: self.warn('Untranslatable node %s.%s found' % ( node.__module__, node.__class__.__name__ ), node) def body(self, nodes): """Calls node() for every node in the iterable passed.""" for node in nodes: self.node(node) @node(TextNode) def text_node(writer, node): writer.write(node.s) @node(Variable) def variable(writer, node): if node.translate: writer.warn('i18n system used, make sure to install translations', node) writer.write('_(') if node.literal is not None: writer.literal(node.literal) else: writer.variable(node.var) if node.translate: writer.write(')') @node(VariableNode) def variable_node(writer, node): writer.start_variable() if node.filter_expression.var.var == 'block.super' \ and not node.filter_expression.filters: writer.write('super()') else: writer.node(node.filter_expression) writer.end_variable() @node(FilterExpression) def filter_expression(writer, node): writer.node(node.var) writer.filters(node.filters) @node(core_tags.CommentNode) def comment_tag(writer, node): pass @node(core_tags.DebugNode) def comment_tag(writer, node): writer.warn('Debug tag detected. Make sure to add a global function ' 'called debug to the namespace.', node=node) writer.print_expr('debug()') @node(core_tags.ForNode) def for_loop(writer, node): writer.start_block() writer.write('for ') for idx, var in enumerate(node.loopvars): if idx: writer.write(', ') writer.variable(var) writer.write(' in ') if node.is_reversed: writer.write('(') writer.node(node.sequence) if node.is_reversed: writer.write(')|reverse') writer.end_block() writer.enter_loop() writer.body(node.nodelist_loop) writer.leave_loop() writer.tag('endfor') @node(core_tags.IfNode) def if_condition(writer, node): writer.start_block() writer.write('if ') join_with = 'and' if node.link_type == core_tags.IfNode.LinkTypes.or_: join_with = 'or' for idx, (ifnot, expr) in enumerate(node.bool_exprs): if idx: writer.write(' %s ' % join_with) if ifnot: writer.write('not ') writer.node(expr) writer.end_block() writer.body(node.nodelist_true) if node.nodelist_false: writer.tag('else') writer.body(node.nodelist_false) writer.tag('endif') @node(core_tags.IfEqualNode) def if_equal(writer, node): writer.start_block() writer.write('if ') writer.node(node.var1) if node.negate: writer.write(' != ') else: writer.write(' == ') writer.node(node.var2) writer.end_block() writer.body(node.nodelist_true) if node.nodelist_false: writer.tag('else') writer.body(node.nodelist_false) writer.tag('endif') @node(loader_tags.BlockNode) def block(writer, node): writer.tag('block ' + node.name.replace('-', '_').rstrip('_')) node = node while node.parent is not None: node = node.parent writer.body(node.nodelist) writer.tag('endblock') @node(loader_tags.ExtendsNode) def extends(writer, node): writer.start_block() writer.write('extends ') if node.parent_name_expr: writer.node(node.parent_name_expr) else: writer.literal(node.parent_name) writer.end_block() writer.body(node.nodelist) @node(loader_tags.ConstantIncludeNode) @node(loader_tags.IncludeNode) def include(writer, node): writer.start_block() writer.write('include ') if hasattr(node, 'template'): writer.literal(node.template.name) else: writer.node(node.template_name) writer.end_block() @node(core_tags.CycleNode) def cycle(writer, node): if not writer.in_loop: writer.warn('Untranslatable free cycle (cycle outside loop)', node=node) return if node.variable_name is not None: writer.start_block() writer.write('set %s = ' % node.variable_name) else: writer.start_variable() writer.write('loop.cycle(') for idx, var in enumerate(node.raw_cycle_vars): if idx: writer.write(', ') writer.node(var) writer.write(')') if node.variable_name is not None: writer.end_block() else: writer.end_variable() @node(core_tags.FilterNode) def filter(writer, node): writer.start_block() writer.write('filter ') writer.filters(node.filter_expr.filters, True) writer.end_block() writer.body(node.nodelist) writer.tag('endfilter') @node(core_tags.AutoEscapeControlNode) def autoescape_control(writer, node): original = writer.autoescape writer.autoescape = node.setting writer.body(node.nodelist) writer.autoescape = original @node(core_tags.SpacelessNode) def spaceless(writer, node): original = writer.spaceless writer.spaceless = True writer.warn('entering spaceless mode with different semantics', node) # do the initial stripping nodelist = list(node.nodelist) if nodelist: if isinstance(nodelist[0], TextNode): nodelist[0] = TextNode(nodelist[0].s.lstrip()) if isinstance(nodelist[-1], TextNode): nodelist[-1] = TextNode(nodelist[-1].s.rstrip()) writer.body(nodelist) writer.spaceless = original @node(core_tags.TemplateTagNode) def template_tag(writer, node): tag = { 'openblock': writer.block_start_string, 'closeblock': writer.block_end_string, 'openvariable': writer.variable_start_string, 'closevariable': writer.variable_end_string, 'opencomment': writer.comment_start_string, 'closecomment': writer.comment_end_string, 'openbrace': '{', 'closebrace': '}' }.get(node.tagtype) if tag: writer.start_variable() writer.literal(tag) writer.end_variable() @node(core_tags.URLNode) def url_tag(writer, node): writer.warn('url node used. make sure to provide a proper url() ' 'function', node) if node.asvar: writer.start_block() writer.write('set %s = ' % node.asvar) else: writer.start_variable() autoescape = writer.autoescape writer.write('url(') writer.literal(node.view_name) for arg in node.args: writer.write(', ') writer.node(arg) for key, arg in node.kwargs.items(): writer.write(', %s=' % key) writer.node(arg) writer.write(')') if node.asvar: writer.end_block() else: writer.end_variable() @node(core_tags.WidthRatioNode) def width_ratio(writer, node): writer.warn('widthratio expanded into formula. You may want to provide ' 'a helper function for this calculation', node) writer.start_variable() writer.write('(') writer.node(node.val_expr) writer.write(' / ') writer.node(node.max_expr) writer.write(' * ') writer.write(str(int(node.max_width))) writer.write(')|round|int') writer.end_variable(always_safe=True) @node(core_tags.WithNode) def with_block(writer, node): writer.warn('with block expanded into set statement. This could cause ' 'variables following that block to be overridden.', node) writer.start_block() writer.write('set %s = ' % node.name) writer.node(node.var) writer.end_block() writer.body(node.nodelist) @node(core_tags.RegroupNode) def regroup(writer, node): if node.expression.var.literal: writer.warn('literal in groupby filter used. Behavior in that ' 'situation is undefined and translation is skipped.', node) return elif node.expression.filters: writer.warn('filters in groupby filter used. Behavior in that ' 'situation is undefined which is most likely a bug ' 'in your code. Filters were ignored.', node) writer.start_block() writer.write('set %s = ' % node.var_name) writer.node(node.target) writer.write('|groupby(') writer.literal(node.expression.var.var) writer.write(')') writer.end_block() @node(core_tags.LoadNode) def warn_load(writer, node): writer.warn('load statement used which was ignored on conversion', node) @node(i18n_tags.GetAvailableLanguagesNode) def get_available_languages(writer, node): writer.warn('make sure to provide a get_available_languages function', node) writer.tag('set %s = get_available_languages()' % writer.translate_variable_name(node.variable)) @node(i18n_tags.GetCurrentLanguageNode) def get_current_language(writer, node): writer.warn('make sure to provide a get_current_language function', node) writer.tag('set %s = get_current_language()' % writer.translate_variable_name(node.variable)) @node(i18n_tags.GetCurrentLanguageBidiNode) def get_current_language_bidi(writer, node): writer.warn('make sure to provide a get_current_language_bidi function', node) writer.tag('set %s = get_current_language_bidi()' % writer.translate_variable_name(node.variable)) @node(i18n_tags.TranslateNode) def simple_gettext(writer, node): writer.warn('i18n system used, make sure to install translations', node) writer.start_variable() writer.write('_(') writer.node(node.value) writer.write(')') writer.end_variable() @node(i18n_tags.BlockTranslateNode) def translate_block(writer, node): first_var = [] variables = set() def touch_var(name): variables.add(name) if not first_var: first_var.append(name) def dump_token_list(tokens): for token in tokens: if token.token_type == TOKEN_TEXT: writer.write(token.contents) elif token.token_type == TOKEN_VAR: writer.print_expr(token.contents) touch_var(token.contents) writer.warn('i18n system used, make sure to install translations', node) writer.start_block() writer.write('trans') idx = -1 for idx, (key, var) in enumerate(node.extra_context.items()): if idx: writer.write(',') writer.write(' %s=' % key) touch_var(key) writer.node(var.filter_expression) have_plural = False plural_var = None if node.plural and node.countervar and node.counter: have_plural = True plural_var = node.countervar if plural_var not in variables: if idx > -1: writer.write(',') touch_var(plural_var) writer.write(' %s=' % plural_var) writer.node(node.counter) writer.end_block() dump_token_list(node.singular) if node.plural and node.countervar and node.counter: writer.start_block() writer.write('pluralize') if node.countervar != first_var[0]: writer.write(' ' + node.countervar) writer.end_block() dump_token_list(node.plural) writer.tag('endtrans') @node("SimpleNode") def simple_tag(writer, node): """Check if the simple tag exist as a filter in """ name = node.tag_name if writer.env and \ name not in writer.env.filters and \ name not in writer._filters_warned: writer._filters_warned.add(name) writer.warn('Filter %s probably doesn\'t exist in Jinja' % name) if not node.vars_to_resolve: # No argument, pass the request writer.start_variable() writer.write('request|') writer.write(name) writer.end_variable() return first_var = node.vars_to_resolve[0] args = node.vars_to_resolve[1:] writer.start_variable() # Copied from Writer.filters() writer.node(first_var) writer.write('|') writer.write(name) if args: writer.write('(') for idx, var in enumerate(args): if idx: writer.write(', ') if var.var: writer.node(var) else: writer.literal(var.literal) writer.write(')') writer.end_variable() # get rid of node now, it shouldn't be used normally del node Jinja2-2.10/ext/Vim/0000755000175000017500000000000013200660754014307 5ustar daviddavid00000000000000Jinja2-2.10/ext/Vim/jinja.vim0000644000175000017500000001302213130203104016075 0ustar daviddavid00000000000000" Vim syntax file " Language: Jinja template " Maintainer: Armin Ronacher " Last Change: 2008 May 9 " Version: 1.1 " " Known Bugs: " because of odd limitations dicts and the modulo operator " appear wrong in the template. " " Changes: " " 2008 May 9: Added support for Jinja2 changes (new keyword rules) " .vimrc variable to disable html highlighting if !exists('g:jinja_syntax_html') let g:jinja_syntax_html=1 endif " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded if !exists("main_syntax") if version < 600 syntax clear elseif exists("b:current_syntax") finish endif let main_syntax = 'jinja' endif " Pull in the HTML syntax. if g:jinja_syntax_html if version < 600 so :p:h/html.vim else runtime! syntax/html.vim unlet b:current_syntax endif endif syntax case match " Jinja template built-in tags and parameters (without filter, macro, is and raw, they " have special threatment) syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained and if else in not or recursive as import syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained is filter skipwhite nextgroup=jinjaFilter syn keyword jinjaStatement containedin=jinjaTagBlock contained macro skipwhite nextgroup=jinjaFunction syn keyword jinjaStatement containedin=jinjaTagBlock contained block skipwhite nextgroup=jinjaBlockName " Variable Names syn match jinjaVariable containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[a-zA-Z_][a-zA-Z0-9_]*/ syn keyword jinjaSpecial containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained false true none False True None loop super caller varargs kwargs " Filters syn match jinjaOperator "|" containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained skipwhite nextgroup=jinjaFilter syn match jinjaFilter contained /[a-zA-Z_][a-zA-Z0-9_]*/ syn match jinjaFunction contained /[a-zA-Z_][a-zA-Z0-9_]*/ syn match jinjaBlockName contained /[a-zA-Z_][a-zA-Z0-9_]*/ " Jinja template constants syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/"/ skip=/\(\\\)\@\)*\\"/ end=/"/ syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/'/ skip=/\(\\\)\@\)*\\'/ end=/'/ syn match jinjaNumber containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[0-9]\+\(\.[0-9]\+\)\?/ " Operators syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[+\-*\/<>=!,:]/ syn match jinjaPunctuation containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[()\[\]]/ syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /\./ nextgroup=jinjaAttribute syn match jinjaAttribute contained /[a-zA-Z_][a-zA-Z0-9_]*/ " Jinja template tag and variable blocks syn region jinjaNested matchgroup=jinjaOperator start="(" end=")" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained syn region jinjaNested matchgroup=jinjaOperator start="\[" end="\]" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained syn region jinjaNested matchgroup=jinjaOperator start="{" end="}" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment syn region jinjaVarBlock matchgroup=jinjaVarDelim start=/{{-\?/ end=/-\?}}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment " Jinja template 'raw' tag syn region jinjaRaw matchgroup=jinjaRawDelim start="{%\s*raw\s*%}" end="{%\s*endraw\s*%}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString,jinjaComment " Jinja comments syn region jinjaComment matchgroup=jinjaCommentDelim start="{#" end="#}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString,jinjaComment " Block start keywords. A bit tricker. We only highlight at the start of a " tag block and only if the name is not followed by a comma or equals sign " which usually means that we have to deal with an assignment. syn match jinjaStatement containedin=jinjaTagBlock contained /\({%-\?\s*\)\@<=\<[a-zA-Z_][a-zA-Z0-9_]*\>\(\s*[,=]\)\@!/ " and context modifiers syn match jinjaStatement containedin=jinjaTagBlock contained /\/ " Define the default highlighting. " For version 5.7 and earlier: only when not done already " For version 5.8 and later: only when an item doesn't have highlighting yet if version >= 508 || !exists("did_jinja_syn_inits") if version < 508 let did_jinja_syn_inits = 1 command -nargs=+ HiLink hi link else command -nargs=+ HiLink hi def link endif HiLink jinjaPunctuation jinjaOperator HiLink jinjaAttribute jinjaVariable HiLink jinjaFunction jinjaFilter HiLink jinjaTagDelim jinjaTagBlock HiLink jinjaVarDelim jinjaVarBlock HiLink jinjaCommentDelim jinjaComment HiLink jinjaRawDelim jinja HiLink jinjaSpecial Special HiLink jinjaOperator Normal HiLink jinjaRaw Normal HiLink jinjaTagBlock PreProc HiLink jinjaVarBlock PreProc HiLink jinjaStatement Statement HiLink jinjaFilter Function HiLink jinjaBlockName Function HiLink jinjaVariable Identifier HiLink jinjaString Constant HiLink jinjaNumber Constant HiLink jinjaComment Comment delcommand HiLink endif let b:current_syntax = "jinja" if main_syntax == 'jinja' unlet main_syntax endif Jinja2-2.10/ext/jinja.el0000644000175000017500000001117513033733251015173 0ustar daviddavid00000000000000;;; jinja.el --- Jinja mode highlighting ;; ;; Author: Georg Brandl ;; Copyright: (c) 2009 by the Jinja Team ;; Last modified: 2008-05-22 23:04 by gbr ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: ;; ;; Mostly ripped off django-mode by Lennart Borgman. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 2, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Code: (defconst jinja-font-lock-keywords (list ; (cons (rx "{% comment %}" (submatch (0+ anything)) ; "{% endcomment %}") (list 1 font-lock-comment-face)) '("{# ?\\(.*?\\) ?#}" . (1 font-lock-comment-face)) '("{%-?\\|-?%}\\|{{\\|}}" . font-lock-preprocessor-face) '("{#\\|#}" . font-lock-comment-delimiter-face) ;; first word in a block is a command '("{%-?[ \t\n]*\\([a-zA-Z_]+\\)" . (1 font-lock-keyword-face)) ;; variables '("\\({{ ?\\)\\([^|]*?\\)\\(|.*?\\)? ?}}" . (1 font-lock-variable-name-face)) ;; keywords and builtins (cons (rx word-start (or "in" "as" "recursive" "not" "and" "or" "if" "else" "import" "with" "without" "context") word-end) font-lock-keyword-face) (cons (rx word-start (or "true" "false" "none" "loop" "self" "super") word-end) font-lock-builtin-face) ;; tests '("\\(is\\)[ \t]*\\(not\\)[ \t]*\\([a-zA-Z_]+\\)" (1 font-lock-keyword-face) (2 font-lock-keyword-face) (3 font-lock-function-name-face)) ;; builtin filters (cons (rx "|" (* space) (submatch (or "abs" "batch" "capitalize" "capture" "center" "count" "default" "dformat" "dictsort" "e" "escape" "filesizeformat" "first" "float" "format" "getattribute" "getitem" "groupby" "indent" "int" "join" "jsonencode" "last" "length" "lower" "markdown" "pprint" "random" "replace" "reverse" "round" "rst" "slice" "sort" "string" "striptags" "sum" "textile" "title" "trim" "truncate" "upper" "urlencode" "urlize" "wordcount" "wordwrap" "xmlattr"))) (list 1 font-lock-builtin-face)) ) "Minimal highlighting expressions for Jinja mode") (define-derived-mode jinja-mode nil "Jinja" "Simple Jinja mode for use with `mumamo-mode'. This mode only provides syntax highlighting." ;;(set (make-local-variable 'comment-start) "{#") ;;(set (make-local-variable 'comment-end) "#}") (setq font-lock-defaults '(jinja-font-lock-keywords))) ;; mumamo stuff (when (require 'mumamo nil t) (defun mumamo-chunk-jinja3 (pos max) "Find {# ... #}" (mumamo-quick-chunk-forward pos max "{#" "#}" 'borders 'jinja-mode)) (defun mumamo-chunk-jinja2 (pos max) "Find {{ ... }}" (mumamo-quick-chunk-forward pos max "{{" "}}" 'borders 'jinja-mode)) (defun mumamo-chunk-jinja (pos max) "Find {% ... %}" (mumamo-quick-chunk-forward pos max "{%" "%}" 'borders 'jinja-mode)) ;;;###autoload (define-mumamo-multi-major-mode jinja-html-mumamo "Turn on multiple major modes for Jinja with main mode `html-mode'. This also covers inlined style and javascript." ("Jinja HTML Family" html-mode (mumamo-chunk-jinja mumamo-chunk-jinja2 mumamo-chunk-jinja3 mumamo-chunk-inlined-style mumamo-chunk-inlined-script mumamo-chunk-style= mumamo-chunk-onjs= ))) ;;;###autoload (define-mumamo-multi-major-mode jinja-nxhtml-mumamo "Turn on multiple major modes for Jinja with main mode `nxhtml-mode'. This also covers inlined style and javascript." ("Jinja nXhtml Family" nxhtml-mode (mumamo-chunk-jinja mumamo-chunk-jinja2 mumamo-chunk-jinja3 mumamo-chunk-inlined-style mumamo-chunk-inlined-script mumamo-chunk-style= mumamo-chunk-onjs= ))) ) (provide 'jinja) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; jinja.el ends here Jinja2-2.10/ext/djangojinja2.py0000644000175000017500000000566513033733251016477 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ djangojinja2 ~~~~~~~~~~~~ Adds support for Jinja2 to Django. Configuration variables: ======================= ============================================= Key Description ======================= ============================================= `JINJA2_TEMPLATE_DIRS` List of template folders `JINJA2_EXTENSIONS` List of Jinja2 extensions to use `JINJA2_CACHE_SIZE` The size of the Jinja2 template cache. ======================= ============================================= :copyright: (c) 2009 by the Jinja Team. :license: BSD. """ from itertools import chain from django.conf import settings from django.http import HttpResponse from django.core.exceptions import ImproperlyConfigured from django.template.context import get_standard_processors from django.template import TemplateDoesNotExist from jinja2 import Environment, FileSystemLoader, TemplateNotFound from jinja2.defaults import DEFAULT_NAMESPACE # the environment is unconfigured until the first template is loaded. _jinja_env = None def get_env(): """Get the Jinja2 env and initialize it if necessary.""" global _jinja_env if _jinja_env is None: _jinja_env = create_env() return _jinja_env def create_env(): """Create a new Jinja2 environment.""" searchpath = list(settings.JINJA2_TEMPLATE_DIRS) return Environment(loader=FileSystemLoader(searchpath), auto_reload=settings.TEMPLATE_DEBUG, cache_size=getattr(settings, 'JINJA2_CACHE_SIZE', 400), extensions=getattr(settings, 'JINJA2_EXTENSIONS', ())) def get_template(template_name, globals=None): """Load a template.""" try: return get_env().get_template(template_name, globals=globals) except TemplateNotFound, e: raise TemplateDoesNotExist(str(e)) def select_template(templates, globals=None): """Try to load one of the given templates.""" env = get_env() for template in templates: try: return env.get_template(template, globals=globals) except TemplateNotFound: continue raise TemplateDoesNotExist(', '.join(templates)) def render_to_string(template_name, context=None, request=None, processors=None): """Render a template into a string.""" context = dict(context or {}) if request is not None: context['request'] = request for processor in chain(get_standard_processors(), processors or ()): context.update(processor(request)) return get_template(template_name).render(context) def render_to_response(template_name, context=None, request=None, processors=None, mimetype=None): """Render a template into a response object.""" return HttpResponse(render_to_string(template_name, context, request, processors), mimetype=mimetype) Jinja2-2.10/AUTHORS0000644000175000017500000000111713130203104014004 0ustar daviddavid00000000000000Jinja is written and maintained by the Jinja Team and various contributors: Lead Developer: - Armin Ronacher Developers: - Christoph Hack - Georg Brandl Contributors: - Bryan McLemore - Mickaël Guérin - Cameron Knight - Lawrence Journal-World. - David Cramer - Adrian Mönnich (ThiefMaster) Patches and suggestions: - Ronny Pfannschmidt - Axel Böhm - Alexey Melchakov - Bryan McLemore - Clovis Fabricio (nosklo) - Cameron Knight - Peter van Dijk (Habbie) - Stefan Ebner - Rene Leonhardt - Thomas Waldmann - Cory Benfield (Lukasa) Jinja2-2.10/Jinja2.egg-info/0000755000175000017500000000000013200660754015563 5ustar daviddavid00000000000000Jinja2-2.10/Jinja2.egg-info/not-zip-safe0000644000175000017500000000000113200652216020003 0ustar daviddavid00000000000000 Jinja2-2.10/Jinja2.egg-info/entry_points.txt0000644000175000017500000000011013200660754021051 0ustar daviddavid00000000000000 [babel.extractors] jinja2 = jinja2.ext:babel_extract[i18n] Jinja2-2.10/Jinja2.egg-info/dependency_links.txt0000644000175000017500000000000113200660754021631 0ustar daviddavid00000000000000 Jinja2-2.10/Jinja2.egg-info/PKG-INFO0000644000175000017500000000461713200660754016670 0ustar daviddavid00000000000000Metadata-Version: 1.1 Name: Jinja2 Version: 2.10 Summary: A small but fast and easy to use stand-alone template engine written in pure python. Home-page: http://jinja.pocoo.org/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com License: BSD Description-Content-Type: UNKNOWN Description: Jinja2 ~~~~~~ Jinja2 is a template engine written in pure Python. It provides a `Django`_ inspired non-XML syntax but supports inline expressions and an optional `sandboxed`_ environment. Nutshell -------- Here a small example of a Jinja template:: {% extends 'base.html' %} {% block title %}Memberlist{% endblock %} {% block content %} {% endblock %} Philosophy ---------- Application logic is for the controller but don't try to make the life for the template designer too hard by giving him too few functionality. For more informations visit the new `Jinja2 webpage`_ and `documentation`_. .. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security) .. _Django: https://www.djangoproject.com/ .. _Jinja2 webpage: http://jinja.pocoo.org/ .. _documentation: http://jinja.pocoo.org/2/documentation/ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: HTML Jinja2-2.10/Jinja2.egg-info/top_level.txt0000644000175000017500000000000713200660754020312 0ustar daviddavid00000000000000jinja2 Jinja2-2.10/Jinja2.egg-info/requires.txt0000644000175000017500000000004413200660754020161 0ustar daviddavid00000000000000MarkupSafe>=0.23 [i18n] Babel>=0.8 Jinja2-2.10/Jinja2.egg-info/SOURCES.txt0000644000175000017500000000617713200660754017462 0ustar daviddavid00000000000000AUTHORS CHANGES.rst LICENSE MANIFEST.in README.rst setup.cfg setup.py Jinja2.egg-info/PKG-INFO Jinja2.egg-info/SOURCES.txt Jinja2.egg-info/dependency_links.txt Jinja2.egg-info/entry_points.txt Jinja2.egg-info/not-zip-safe Jinja2.egg-info/requires.txt Jinja2.egg-info/top_level.txt artwork/jinjalogo.svg docs/Makefile docs/api.rst docs/cache_extension.py docs/changelog.rst docs/conf.py docs/contents.rst.inc docs/extensions.rst docs/faq.rst docs/index.rst docs/integration.rst docs/intro.rst docs/jinjaext.py docs/jinjastyle.sty docs/latexindex.rst docs/logo.pdf docs/nativetypes.rst docs/sandbox.rst docs/switching.rst docs/templates.rst docs/tricks.rst docs/_static/.ignore docs/_static/jinja-small.png docs/_templates/sidebarintro.html docs/_templates/sidebarlogo.html docs/_themes/LICENSE docs/_themes/README docs/_themes/jinja/layout.html docs/_themes/jinja/relations.html docs/_themes/jinja/theme.conf docs/_themes/jinja/static/jinja.css_t examples/bench.py examples/profile.py examples/basic/cycle.py examples/basic/debugger.py examples/basic/inheritance.py examples/basic/test.py examples/basic/test_filter_and_linestatements.py examples/basic/test_loop_filter.py examples/basic/translate.py examples/basic/templates/broken.html examples/basic/templates/subbroken.html examples/rwbench/djangoext.py examples/rwbench/rwbench.py examples/rwbench/django/_form.html examples/rwbench/django/_input_field.html examples/rwbench/django/_textarea.html examples/rwbench/django/index.html examples/rwbench/django/layout.html examples/rwbench/genshi/helpers.html examples/rwbench/genshi/index.html examples/rwbench/genshi/layout.html examples/rwbench/jinja/helpers.html examples/rwbench/jinja/index.html examples/rwbench/jinja/layout.html examples/rwbench/mako/helpers.html examples/rwbench/mako/index.html examples/rwbench/mako/layout.html ext/djangojinja2.py ext/inlinegettext.py ext/jinja.el ext/Vim/jinja.vim ext/django2jinja/django2jinja.py ext/django2jinja/example.py ext/django2jinja/templates/index.html ext/django2jinja/templates/layout.html ext/django2jinja/templates/subtemplate.html jinja2/__init__.py jinja2/_compat.py jinja2/_identifier.py jinja2/asyncfilters.py jinja2/asyncsupport.py jinja2/bccache.py jinja2/compiler.py jinja2/constants.py jinja2/debug.py jinja2/defaults.py jinja2/environment.py jinja2/exceptions.py jinja2/ext.py jinja2/filters.py jinja2/idtracking.py jinja2/lexer.py jinja2/loaders.py jinja2/meta.py jinja2/nativetypes.py jinja2/nodes.py jinja2/optimizer.py jinja2/parser.py jinja2/runtime.py jinja2/sandbox.py jinja2/tests.py jinja2/utils.py jinja2/visitor.py tests/conftest.py tests/test_api.py tests/test_async.py tests/test_asyncfilters.py tests/test_bytecode_cache.py tests/test_core_tags.py tests/test_debug.py tests/test_ext.py tests/test_features.py tests/test_filters.py tests/test_idtracking.py tests/test_imports.py tests/test_inheritance.py tests/test_lexnparse.py tests/test_loader.py tests/test_nativetypes.py tests/test_regression.py tests/test_security.py tests/test_tests.py tests/test_utils.py tests/res/__init__.py tests/res/templates/broken.html tests/res/templates/syntaxerror.html tests/res/templates/test.html tests/res/templates/foo/test.htmlJinja2-2.10/jinja2/0000755000175000017500000000000013200660754014131 5ustar daviddavid00000000000000Jinja2-2.10/jinja2/lexer.py0000644000175000017500000006761713176154260015645 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.lexer ~~~~~~~~~~~~ This module implements a Jinja / Python combination lexer. The `Lexer` class provided by this module is used to do some preprocessing for Jinja. On the one hand it filters out invalid operators like the bitshift operators we don't allow in templates. On the other hand it separates template code and python code in expressions. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ import re from collections import deque from operator import itemgetter from jinja2._compat import implements_iterator, intern, iteritems, text_type from jinja2.exceptions import TemplateSyntaxError from jinja2.utils import LRUCache # cache for the lexers. Exists in order to be able to have multiple # environments with the same lexer _lexer_cache = LRUCache(50) # static regular expressions whitespace_re = re.compile(r'\s+', re.U) string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) integer_re = re.compile(r'\d+') try: # check if this Python supports Unicode identifiers compile('föö', '', 'eval') except SyntaxError: # no Unicode support, use ASCII identifiers name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*') check_ident = False else: # Unicode support, build a pattern to match valid characters, and set flag # to use str.isidentifier to validate during lexing from jinja2 import _identifier name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern)) check_ident = True # remove the pattern from memory after building the regex import sys del sys.modules['jinja2._identifier'] import jinja2 del jinja2._identifier del _identifier float_re = re.compile(r'(?': TOKEN_GT, '>=': TOKEN_GTEQ, '<': TOKEN_LT, '<=': TOKEN_LTEQ, '=': TOKEN_ASSIGN, '.': TOKEN_DOT, ':': TOKEN_COLON, '|': TOKEN_PIPE, ',': TOKEN_COMMA, ';': TOKEN_SEMICOLON } reverse_operators = dict([(v, k) for k, v in iteritems(operators)]) assert len(operators) == len(reverse_operators), 'operators dropped' operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))) ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT, TOKEN_COMMENT_END, TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT]) ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, TOKEN_COMMENT, TOKEN_LINECOMMENT]) def _describe_token_type(token_type): if token_type in reverse_operators: return reverse_operators[token_type] return { TOKEN_COMMENT_BEGIN: 'begin of comment', TOKEN_COMMENT_END: 'end of comment', TOKEN_COMMENT: 'comment', TOKEN_LINECOMMENT: 'comment', TOKEN_BLOCK_BEGIN: 'begin of statement block', TOKEN_BLOCK_END: 'end of statement block', TOKEN_VARIABLE_BEGIN: 'begin of print statement', TOKEN_VARIABLE_END: 'end of print statement', TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement', TOKEN_LINESTATEMENT_END: 'end of line statement', TOKEN_DATA: 'template data / text', TOKEN_EOF: 'end of template' }.get(token_type, token_type) def describe_token(token): """Returns a description of the token.""" if token.type == 'name': return token.value return _describe_token_type(token.type) def describe_token_expr(expr): """Like `describe_token` but for token expressions.""" if ':' in expr: type, value = expr.split(':', 1) if type == 'name': return value else: type = expr return _describe_token_type(type) def count_newlines(value): """Count the number of newline characters in the string. This is useful for extensions that filter a stream. """ return len(newline_re.findall(value)) def compile_rules(environment): """Compiles all the rules from the environment into a list of rules.""" e = re.escape rules = [ (len(environment.comment_start_string), 'comment', e(environment.comment_start_string)), (len(environment.block_start_string), 'block', e(environment.block_start_string)), (len(environment.variable_start_string), 'variable', e(environment.variable_start_string)) ] if environment.line_statement_prefix is not None: rules.append((len(environment.line_statement_prefix), 'linestatement', r'^[ \t\v]*' + e(environment.line_statement_prefix))) if environment.line_comment_prefix is not None: rules.append((len(environment.line_comment_prefix), 'linecomment', r'(?:^|(?<=\S))[^\S\r\n]*' + e(environment.line_comment_prefix))) return [x[1:] for x in sorted(rules, reverse=True)] class Failure(object): """Class that raises a `TemplateSyntaxError` if called. Used by the `Lexer` to specify known errors. """ def __init__(self, message, cls=TemplateSyntaxError): self.message = message self.error_class = cls def __call__(self, lineno, filename): raise self.error_class(self.message, lineno, filename) class Token(tuple): """Token class.""" __slots__ = () lineno, type, value = (property(itemgetter(x)) for x in range(3)) def __new__(cls, lineno, type, value): return tuple.__new__(cls, (lineno, intern(str(type)), value)) def __str__(self): if self.type in reverse_operators: return reverse_operators[self.type] elif self.type == 'name': return self.value return self.type def test(self, expr): """Test a token against a token expression. This can either be a token type or ``'token_type:token_value'``. This can only test against string values and types. """ # here we do a regular string equality check as test_any is usually # passed an iterable of not interned strings. if self.type == expr: return True elif ':' in expr: return expr.split(':', 1) == [self.type, self.value] return False def test_any(self, *iterable): """Test against multiple token expressions.""" for expr in iterable: if self.test(expr): return True return False def __repr__(self): return 'Token(%r, %r, %r)' % ( self.lineno, self.type, self.value ) @implements_iterator class TokenStreamIterator(object): """The iterator for tokenstreams. Iterate over the stream until the eof token is reached. """ def __init__(self, stream): self.stream = stream def __iter__(self): return self def __next__(self): token = self.stream.current if token.type is TOKEN_EOF: self.stream.close() raise StopIteration() next(self.stream) return token @implements_iterator class TokenStream(object): """A token stream is an iterable that yields :class:`Token`\\s. The parser however does not iterate over it but calls :meth:`next` to go one token ahead. The current active token is stored as :attr:`current`. """ def __init__(self, generator, name, filename): self._iter = iter(generator) self._pushed = deque() self.name = name self.filename = filename self.closed = False self.current = Token(1, TOKEN_INITIAL, '') next(self) def __iter__(self): return TokenStreamIterator(self) def __bool__(self): return bool(self._pushed) or self.current.type is not TOKEN_EOF __nonzero__ = __bool__ # py2 eos = property(lambda x: not x, doc="Are we at the end of the stream?") def push(self, token): """Push a token back to the stream.""" self._pushed.append(token) def look(self): """Look at the next token.""" old_token = next(self) result = self.current self.push(result) self.current = old_token return result def skip(self, n=1): """Got n tokens ahead.""" for x in range(n): next(self) def next_if(self, expr): """Perform the token test and return the token if it matched. Otherwise the return value is `None`. """ if self.current.test(expr): return next(self) def skip_if(self, expr): """Like :meth:`next_if` but only returns `True` or `False`.""" return self.next_if(expr) is not None def __next__(self): """Go one token ahead and return the old one. Use the built-in :func:`next` instead of calling this directly. """ rv = self.current if self._pushed: self.current = self._pushed.popleft() elif self.current.type is not TOKEN_EOF: try: self.current = next(self._iter) except StopIteration: self.close() return rv def close(self): """Close the stream.""" self.current = Token(self.current.lineno, TOKEN_EOF, '') self._iter = None self.closed = True def expect(self, expr): """Expect a given token type and return it. This accepts the same argument as :meth:`jinja2.lexer.Token.test`. """ if not self.current.test(expr): expr = describe_token_expr(expr) if self.current.type is TOKEN_EOF: raise TemplateSyntaxError('unexpected end of template, ' 'expected %r.' % expr, self.current.lineno, self.name, self.filename) raise TemplateSyntaxError("expected token %r, got %r" % (expr, describe_token(self.current)), self.current.lineno, self.name, self.filename) try: return self.current finally: next(self) def get_lexer(environment): """Return a lexer which is probably cached.""" key = (environment.block_start_string, environment.block_end_string, environment.variable_start_string, environment.variable_end_string, environment.comment_start_string, environment.comment_end_string, environment.line_statement_prefix, environment.line_comment_prefix, environment.trim_blocks, environment.lstrip_blocks, environment.newline_sequence, environment.keep_trailing_newline) lexer = _lexer_cache.get(key) if lexer is None: lexer = Lexer(environment) _lexer_cache[key] = lexer return lexer class Lexer(object): """Class that implements a lexer for a given environment. Automatically created by the environment class, usually you don't have to do that. Note that the lexer is not automatically bound to an environment. Multiple environments can share the same lexer. """ def __init__(self, environment): # shortcuts c = lambda x: re.compile(x, re.M | re.S) e = re.escape # lexing rules for tags tag_rules = [ (whitespace_re, TOKEN_WHITESPACE, None), (float_re, TOKEN_FLOAT, None), (integer_re, TOKEN_INTEGER, None), (name_re, TOKEN_NAME, None), (string_re, TOKEN_STRING, None), (operator_re, TOKEN_OPERATOR, None) ] # assemble the root lexing rule. because "|" is ungreedy # we have to sort by length so that the lexer continues working # as expected when we have parsing rules like <% for block and # <%= for variables. (if someone wants asp like syntax) # variables are just part of the rules if variable processing # is required. root_tag_rules = compile_rules(environment) # block suffix if trimming is enabled block_suffix_re = environment.trim_blocks and '\\n?' or '' # strip leading spaces if lstrip_blocks is enabled prefix_re = {} if environment.lstrip_blocks: # use '{%+' to manually disable lstrip_blocks behavior no_lstrip_re = e('+') # detect overlap between block and variable or comment strings block_diff = c(r'^%s(.*)' % e(environment.block_start_string)) # make sure we don't mistake a block for a variable or a comment m = block_diff.match(environment.comment_start_string) no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' m = block_diff.match(environment.variable_start_string) no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' # detect overlap between comment and variable strings comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string)) m = comment_diff.match(environment.variable_start_string) no_variable_re = m and r'(?!%s)' % e(m.group(1)) or '' lstrip_re = r'^[ \t]*' block_prefix_re = r'%s%s(?!%s)|%s\+?' % ( lstrip_re, e(environment.block_start_string), no_lstrip_re, e(environment.block_start_string), ) comment_prefix_re = r'%s%s%s|%s\+?' % ( lstrip_re, e(environment.comment_start_string), no_variable_re, e(environment.comment_start_string), ) prefix_re['block'] = block_prefix_re prefix_re['comment'] = comment_prefix_re else: block_prefix_re = '%s' % e(environment.block_start_string) self.newline_sequence = environment.newline_sequence self.keep_trailing_newline = environment.keep_trailing_newline # global lexing rules self.rules = { 'root': [ # directives (c('(.*?)(?:%s)' % '|'.join( [r'(?P(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % ( e(environment.block_start_string), block_prefix_re, e(environment.block_end_string), e(environment.block_end_string) )] + [ r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r)) for n, r in root_tag_rules ])), (TOKEN_DATA, '#bygroup'), '#bygroup'), # data (c('.+'), TOKEN_DATA, None) ], # comments TOKEN_COMMENT_BEGIN: [ (c(r'(.*?)((?:\-%s\s*|%s)%s)' % ( e(environment.comment_end_string), e(environment.comment_end_string), block_suffix_re )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'), (c('(.)'), (Failure('Missing end of comment tag'),), None) ], # blocks TOKEN_BLOCK_BEGIN: [ (c(r'(?:\-%s\s*|%s)%s' % ( e(environment.block_end_string), e(environment.block_end_string), block_suffix_re )), TOKEN_BLOCK_END, '#pop'), ] + tag_rules, # variables TOKEN_VARIABLE_BEGIN: [ (c(r'\-%s\s*|%s' % ( e(environment.variable_end_string), e(environment.variable_end_string) )), TOKEN_VARIABLE_END, '#pop') ] + tag_rules, # raw block TOKEN_RAW_BEGIN: [ (c(r'(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % ( e(environment.block_start_string), block_prefix_re, e(environment.block_end_string), e(environment.block_end_string), block_suffix_re )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'), (c('(.)'), (Failure('Missing end of raw directive'),), None) ], # line statements TOKEN_LINESTATEMENT_BEGIN: [ (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop') ] + tag_rules, # line comments TOKEN_LINECOMMENT_BEGIN: [ (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT, TOKEN_LINECOMMENT_END), '#pop') ] } def _normalize_newlines(self, value): """Called for strings and template data to normalize it to unicode.""" return newline_re.sub(self.newline_sequence, value) def tokenize(self, source, name=None, filename=None, state=None): """Calls tokeniter + tokenize and wraps it in a token stream. """ stream = self.tokeniter(source, name, filename, state) return TokenStream(self.wrap(stream, name, filename), name, filename) def wrap(self, stream, name=None, filename=None): """This is called with the stream as returned by `tokenize` and wraps every token in a :class:`Token` and converts the value. """ for lineno, token, value in stream: if token in ignored_tokens: continue elif token == 'linestatement_begin': token = 'block_begin' elif token == 'linestatement_end': token = 'block_end' # we are not interested in those tokens in the parser elif token in ('raw_begin', 'raw_end'): continue elif token == 'data': value = self._normalize_newlines(value) elif token == 'keyword': token = value elif token == 'name': value = str(value) if check_ident and not value.isidentifier(): raise TemplateSyntaxError( 'Invalid character in identifier', lineno, name, filename) elif token == 'string': # try to unescape string try: value = self._normalize_newlines(value[1:-1]) \ .encode('ascii', 'backslashreplace') \ .decode('unicode-escape') except Exception as e: msg = str(e).split(':')[-1].strip() raise TemplateSyntaxError(msg, lineno, name, filename) elif token == 'integer': value = int(value) elif token == 'float': value = float(value) elif token == 'operator': token = operators[value] yield Token(lineno, token, value) def tokeniter(self, source, name, filename=None, state=None): """This method tokenizes the text and returns the tokens in a generator. Use this method if you just want to tokenize a template. """ source = text_type(source) lines = source.splitlines() if self.keep_trailing_newline and source: for newline in ('\r\n', '\r', '\n'): if source.endswith(newline): lines.append('') break source = '\n'.join(lines) pos = 0 lineno = 1 stack = ['root'] if state is not None and state != 'root': assert state in ('variable', 'block'), 'invalid state' stack.append(state + '_begin') else: state = 'root' statetokens = self.rules[stack[-1]] source_length = len(source) balancing_stack = [] while 1: # tokenizer loop for regex, tokens, new_state in statetokens: m = regex.match(source, pos) # if no match we try again with the next rule if m is None: continue # we only match blocks and variables if braces / parentheses # are balanced. continue parsing with the lower rule which # is the operator rule. do this only if the end tags look # like operators if balancing_stack and \ tokens in ('variable_end', 'block_end', 'linestatement_end'): continue # tuples support more options if isinstance(tokens, tuple): for idx, token in enumerate(tokens): # failure group if token.__class__ is Failure: raise token(lineno, filename) # bygroup is a bit more complex, in that case we # yield for the current token the first named # group that matched elif token == '#bygroup': for key, value in iteritems(m.groupdict()): if value is not None: yield lineno, key, value lineno += value.count('\n') break else: raise RuntimeError('%r wanted to resolve ' 'the token dynamically' ' but no group matched' % regex) # normal group else: data = m.group(idx + 1) if data or token not in ignore_if_empty: yield lineno, token, data lineno += data.count('\n') # strings as token just are yielded as it. else: data = m.group() # update brace/parentheses balance if tokens == 'operator': if data == '{': balancing_stack.append('}') elif data == '(': balancing_stack.append(')') elif data == '[': balancing_stack.append(']') elif data in ('}', ')', ']'): if not balancing_stack: raise TemplateSyntaxError('unexpected \'%s\'' % data, lineno, name, filename) expected_op = balancing_stack.pop() if expected_op != data: raise TemplateSyntaxError('unexpected \'%s\', ' 'expected \'%s\'' % (data, expected_op), lineno, name, filename) # yield items if data or tokens not in ignore_if_empty: yield lineno, tokens, data lineno += data.count('\n') # fetch new position into new variable so that we can check # if there is a internal parsing error which would result # in an infinite loop pos2 = m.end() # handle state changes if new_state is not None: # remove the uppermost state if new_state == '#pop': stack.pop() # resolve the new state by group checking elif new_state == '#bygroup': for key, value in iteritems(m.groupdict()): if value is not None: stack.append(key) break else: raise RuntimeError('%r wanted to resolve the ' 'new state dynamically but' ' no group matched' % regex) # direct state name given else: stack.append(new_state) statetokens = self.rules[stack[-1]] # we are still at the same position and no stack change. # this means a loop without break condition, avoid that and # raise error elif pos2 == pos: raise RuntimeError('%r yielded empty string without ' 'stack change' % regex) # publish new function and start again pos = pos2 break # if loop terminated without break we haven't found a single match # either we are at the end of the file or we have a problem else: # end of text if pos >= source_length: return # something went wrong raise TemplateSyntaxError('unexpected char %r at %d' % (source[pos], pos), lineno, name, filename) Jinja2-2.10/jinja2/idtracking.py0000644000175000017500000002175513176154260016636 0ustar daviddavid00000000000000from jinja2.visitor import NodeVisitor from jinja2._compat import iteritems VAR_LOAD_PARAMETER = 'param' VAR_LOAD_RESOLVE = 'resolve' VAR_LOAD_ALIAS = 'alias' VAR_LOAD_UNDEFINED = 'undefined' def find_symbols(nodes, parent_symbols=None): sym = Symbols(parent=parent_symbols) visitor = FrameSymbolVisitor(sym) for node in nodes: visitor.visit(node) return sym def symbols_for_node(node, parent_symbols=None): sym = Symbols(parent=parent_symbols) sym.analyze_node(node) return sym class Symbols(object): def __init__(self, parent=None, level=None): if level is None: if parent is None: level = 0 else: level = parent.level + 1 self.level = level self.parent = parent self.refs = {} self.loads = {} self.stores = set() def analyze_node(self, node, **kwargs): visitor = RootVisitor(self) visitor.visit(node, **kwargs) def _define_ref(self, name, load=None): ident = 'l_%d_%s' % (self.level, name) self.refs[name] = ident if load is not None: self.loads[ident] = load return ident def find_load(self, target): if target in self.loads: return self.loads[target] if self.parent is not None: return self.parent.find_load(target) def find_ref(self, name): if name in self.refs: return self.refs[name] if self.parent is not None: return self.parent.find_ref(name) def ref(self, name): rv = self.find_ref(name) if rv is None: raise AssertionError('Tried to resolve a name to a reference that ' 'was unknown to the frame (%r)' % name) return rv def copy(self): rv = object.__new__(self.__class__) rv.__dict__.update(self.__dict__) rv.refs = self.refs.copy() rv.loads = self.loads.copy() rv.stores = self.stores.copy() return rv def store(self, name): self.stores.add(name) # If we have not see the name referenced yet, we need to figure # out what to set it to. if name not in self.refs: # If there is a parent scope we check if the name has a # reference there. If it does it means we might have to alias # to a variable there. if self.parent is not None: outer_ref = self.parent.find_ref(name) if outer_ref is not None: self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref)) return # Otherwise we can just set it to undefined. self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None)) def declare_parameter(self, name): self.stores.add(name) return self._define_ref(name, load=(VAR_LOAD_PARAMETER, None)) def load(self, name): target = self.find_ref(name) if target is None: self._define_ref(name, load=(VAR_LOAD_RESOLVE, name)) def branch_update(self, branch_symbols): stores = {} for branch in branch_symbols: for target in branch.stores: if target in self.stores: continue stores[target] = stores.get(target, 0) + 1 for sym in branch_symbols: self.refs.update(sym.refs) self.loads.update(sym.loads) self.stores.update(sym.stores) for name, branch_count in iteritems(stores): if branch_count == len(branch_symbols): continue target = self.find_ref(name) assert target is not None, 'should not happen' if self.parent is not None: outer_target = self.parent.find_ref(name) if outer_target is not None: self.loads[target] = (VAR_LOAD_ALIAS, outer_target) continue self.loads[target] = (VAR_LOAD_RESOLVE, name) def dump_stores(self): rv = {} node = self while node is not None: for name in node.stores: if name not in rv: rv[name] = self.find_ref(name) node = node.parent return rv def dump_param_targets(self): rv = set() node = self while node is not None: for target, (instr, _) in iteritems(self.loads): if instr == VAR_LOAD_PARAMETER: rv.add(target) node = node.parent return rv class RootVisitor(NodeVisitor): def __init__(self, symbols): self.sym_visitor = FrameSymbolVisitor(symbols) def _simple_visit(self, node, **kwargs): for child in node.iter_child_nodes(): self.sym_visitor.visit(child) visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \ visit_Scope = visit_If = visit_ScopedEvalContextModifier = \ _simple_visit def visit_AssignBlock(self, node, **kwargs): for child in node.body: self.sym_visitor.visit(child) def visit_CallBlock(self, node, **kwargs): for child in node.iter_child_nodes(exclude=('call',)): self.sym_visitor.visit(child) def visit_OverlayScope(self, node, **kwargs): for child in node.body: self.sym_visitor.visit(child) def visit_For(self, node, for_branch='body', **kwargs): if for_branch == 'body': self.sym_visitor.visit(node.target, store_as_param=True) branch = node.body elif for_branch == 'else': branch = node.else_ elif for_branch == 'test': self.sym_visitor.visit(node.target, store_as_param=True) if node.test is not None: self.sym_visitor.visit(node.test) return else: raise RuntimeError('Unknown for branch') for item in branch or (): self.sym_visitor.visit(item) def visit_With(self, node, **kwargs): for target in node.targets: self.sym_visitor.visit(target) for child in node.body: self.sym_visitor.visit(child) def generic_visit(self, node, *args, **kwargs): raise NotImplementedError('Cannot find symbols for %r' % node.__class__.__name__) class FrameSymbolVisitor(NodeVisitor): """A visitor for `Frame.inspect`.""" def __init__(self, symbols): self.symbols = symbols def visit_Name(self, node, store_as_param=False, **kwargs): """All assignments to names go through this function.""" if store_as_param or node.ctx == 'param': self.symbols.declare_parameter(node.name) elif node.ctx == 'store': self.symbols.store(node.name) elif node.ctx == 'load': self.symbols.load(node.name) def visit_NSRef(self, node, **kwargs): self.symbols.load(node.name) def visit_If(self, node, **kwargs): self.visit(node.test, **kwargs) original_symbols = self.symbols def inner_visit(nodes): self.symbols = rv = original_symbols.copy() for subnode in nodes: self.visit(subnode, **kwargs) self.symbols = original_symbols return rv body_symbols = inner_visit(node.body) elif_symbols = inner_visit(node.elif_) else_symbols = inner_visit(node.else_ or ()) self.symbols.branch_update([body_symbols, elif_symbols, else_symbols]) def visit_Macro(self, node, **kwargs): self.symbols.store(node.name) def visit_Import(self, node, **kwargs): self.generic_visit(node, **kwargs) self.symbols.store(node.target) def visit_FromImport(self, node, **kwargs): self.generic_visit(node, **kwargs) for name in node.names: if isinstance(name, tuple): self.symbols.store(name[1]) else: self.symbols.store(name) def visit_Assign(self, node, **kwargs): """Visit assignments in the correct order.""" self.visit(node.node, **kwargs) self.visit(node.target, **kwargs) def visit_For(self, node, **kwargs): """Visiting stops at for blocks. However the block sequence is visited as part of the outer scope. """ self.visit(node.iter, **kwargs) def visit_CallBlock(self, node, **kwargs): self.visit(node.call, **kwargs) def visit_FilterBlock(self, node, **kwargs): self.visit(node.filter, **kwargs) def visit_With(self, node, **kwargs): for target in node.values: self.visit(target) def visit_AssignBlock(self, node, **kwargs): """Stop visiting at block assigns.""" self.visit(node.target, **kwargs) def visit_Scope(self, node, **kwargs): """Stop visiting at scopes.""" def visit_Block(self, node, **kwargs): """Stop visiting at blocks.""" def visit_OverlayScope(self, node, **kwargs): """Do not visit into overlay scopes.""" Jinja2-2.10/jinja2/_identifier.py0000644000175000017500000000327613130203104016754 0ustar daviddavid00000000000000# generated by scripts/generate_identifier_pattern.py pattern = '·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯' Jinja2-2.10/jinja2/nodes.py0000644000175000017500000007420513176350145015625 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.nodes ~~~~~~~~~~~~ This module implements additional nodes derived from the ast base node. It also provides some node tree helper functions like `in_lineno` and `get_nodes` used by the parser and translator in order to normalize python and jinja nodes. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ import types import operator from collections import deque from jinja2.utils import Markup from jinja2._compat import izip, with_metaclass, text_type, PY2 #: the types we support for context functions _context_function_types = (types.FunctionType, types.MethodType) _binop_to_func = { '*': operator.mul, '/': operator.truediv, '//': operator.floordiv, '**': operator.pow, '%': operator.mod, '+': operator.add, '-': operator.sub } _uaop_to_func = { 'not': operator.not_, '+': operator.pos, '-': operator.neg } _cmpop_to_func = { 'eq': operator.eq, 'ne': operator.ne, 'gt': operator.gt, 'gteq': operator.ge, 'lt': operator.lt, 'lteq': operator.le, 'in': lambda a, b: a in b, 'notin': lambda a, b: a not in b } class Impossible(Exception): """Raised if the node could not perform a requested action.""" class NodeType(type): """A metaclass for nodes that handles the field and attribute inheritance. fields and attributes from the parent class are automatically forwarded to the child.""" def __new__(cls, name, bases, d): for attr in 'fields', 'attributes': storage = [] storage.extend(getattr(bases[0], attr, ())) storage.extend(d.get(attr, ())) assert len(bases) == 1, 'multiple inheritance not allowed' assert len(storage) == len(set(storage)), 'layout conflict' d[attr] = tuple(storage) d.setdefault('abstract', False) return type.__new__(cls, name, bases, d) class EvalContext(object): """Holds evaluation time information. Custom attributes can be attached to it in extensions. """ def __init__(self, environment, template_name=None): self.environment = environment if callable(environment.autoescape): self.autoescape = environment.autoescape(template_name) else: self.autoescape = environment.autoescape self.volatile = False def save(self): return self.__dict__.copy() def revert(self, old): self.__dict__.clear() self.__dict__.update(old) def get_eval_context(node, ctx): if ctx is None: if node.environment is None: raise RuntimeError('if no eval context is passed, the ' 'node must have an attached ' 'environment.') return EvalContext(node.environment) return ctx class Node(with_metaclass(NodeType, object)): """Baseclass for all Jinja2 nodes. There are a number of nodes available of different types. There are four major types: - :class:`Stmt`: statements - :class:`Expr`: expressions - :class:`Helper`: helper nodes - :class:`Template`: the outermost wrapper node All nodes have fields and attributes. Fields may be other nodes, lists, or arbitrary values. Fields are passed to the constructor as regular positional arguments, attributes as keyword arguments. Each node has two attributes: `lineno` (the line number of the node) and `environment`. The `environment` attribute is set at the end of the parsing process for all nodes automatically. """ fields = () attributes = ('lineno', 'environment') abstract = True def __init__(self, *fields, **attributes): if self.abstract: raise TypeError('abstract nodes are not instanciable') if fields: if len(fields) != len(self.fields): if not self.fields: raise TypeError('%r takes 0 arguments' % self.__class__.__name__) raise TypeError('%r takes 0 or %d argument%s' % ( self.__class__.__name__, len(self.fields), len(self.fields) != 1 and 's' or '' )) for name, arg in izip(self.fields, fields): setattr(self, name, arg) for attr in self.attributes: setattr(self, attr, attributes.pop(attr, None)) if attributes: raise TypeError('unknown attribute %r' % next(iter(attributes))) def iter_fields(self, exclude=None, only=None): """This method iterates over all fields that are defined and yields ``(key, value)`` tuples. Per default all fields are returned, but it's possible to limit that to some fields by providing the `only` parameter or to exclude some using the `exclude` parameter. Both should be sets or tuples of field names. """ for name in self.fields: if (exclude is only is None) or \ (exclude is not None and name not in exclude) or \ (only is not None and name in only): try: yield name, getattr(self, name) except AttributeError: pass def iter_child_nodes(self, exclude=None, only=None): """Iterates over all direct child nodes of the node. This iterates over all fields and yields the values of they are nodes. If the value of a field is a list all the nodes in that list are returned. """ for field, item in self.iter_fields(exclude, only): if isinstance(item, list): for n in item: if isinstance(n, Node): yield n elif isinstance(item, Node): yield item def find(self, node_type): """Find the first node of a given type. If no such node exists the return value is `None`. """ for result in self.find_all(node_type): return result def find_all(self, node_type): """Find all the nodes of a given type. If the type is a tuple, the check is performed for any of the tuple items. """ for child in self.iter_child_nodes(): if isinstance(child, node_type): yield child for result in child.find_all(node_type): yield result def set_ctx(self, ctx): """Reset the context of a node and all child nodes. Per default the parser will all generate nodes that have a 'load' context as it's the most common one. This method is used in the parser to set assignment targets and other nodes to a store context. """ todo = deque([self]) while todo: node = todo.popleft() if 'ctx' in node.fields: node.ctx = ctx todo.extend(node.iter_child_nodes()) return self def set_lineno(self, lineno, override=False): """Set the line numbers of the node and children.""" todo = deque([self]) while todo: node = todo.popleft() if 'lineno' in node.attributes: if node.lineno is None or override: node.lineno = lineno todo.extend(node.iter_child_nodes()) return self def set_environment(self, environment): """Set the environment for all nodes.""" todo = deque([self]) while todo: node = todo.popleft() node.environment = environment todo.extend(node.iter_child_nodes()) return self def __eq__(self, other): return type(self) is type(other) and \ tuple(self.iter_fields()) == tuple(other.iter_fields()) def __ne__(self, other): return not self.__eq__(other) # Restore Python 2 hashing behavior on Python 3 __hash__ = object.__hash__ def __repr__(self): return '%s(%s)' % ( self.__class__.__name__, ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for arg in self.fields) ) def dump(self): def _dump(node): if not isinstance(node, Node): buf.append(repr(node)) return buf.append('nodes.%s(' % node.__class__.__name__) if not node.fields: buf.append(')') return for idx, field in enumerate(node.fields): if idx: buf.append(', ') value = getattr(node, field) if isinstance(value, list): buf.append('[') for idx, item in enumerate(value): if idx: buf.append(', ') _dump(item) buf.append(']') else: _dump(value) buf.append(')') buf = [] _dump(self) return ''.join(buf) class Stmt(Node): """Base node for all statements.""" abstract = True class Helper(Node): """Nodes that exist in a specific context only.""" abstract = True class Template(Node): """Node that represents a template. This must be the outermost node that is passed to the compiler. """ fields = ('body',) class Output(Stmt): """A node that holds multiple expressions which are then printed out. This is used both for the `print` statement and the regular template data. """ fields = ('nodes',) class Extends(Stmt): """Represents an extends statement.""" fields = ('template',) class For(Stmt): """The for loop. `target` is the target for the iteration (usually a :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list of nodes that are used as loop-body, and `else_` a list of nodes for the `else` block. If no else node exists it has to be an empty list. For filtered nodes an expression can be stored as `test`, otherwise `None`. """ fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive') class If(Stmt): """If `test` is true, `body` is rendered, else `else_`.""" fields = ('test', 'body', 'elif_', 'else_') class Macro(Stmt): """A macro definition. `name` is the name of the macro, `args` a list of arguments and `defaults` a list of defaults if there are any. `body` is a list of nodes for the macro body. """ fields = ('name', 'args', 'defaults', 'body') class CallBlock(Stmt): """Like a macro without a name but a call instead. `call` is called with the unnamed macro as `caller` argument this node holds. """ fields = ('call', 'args', 'defaults', 'body') class FilterBlock(Stmt): """Node for filter sections.""" fields = ('body', 'filter') class With(Stmt): """Specific node for with statements. In older versions of Jinja the with statement was implemented on the base of the `Scope` node instead. .. versionadded:: 2.9.3 """ fields = ('targets', 'values', 'body') class Block(Stmt): """A node that represents a block.""" fields = ('name', 'body', 'scoped') class Include(Stmt): """A node that represents the include tag.""" fields = ('template', 'with_context', 'ignore_missing') class Import(Stmt): """A node that represents the import tag.""" fields = ('template', 'target', 'with_context') class FromImport(Stmt): """A node that represents the from import tag. It's important to not pass unsafe names to the name attribute. The compiler translates the attribute lookups directly into getattr calls and does *not* use the subscript callback of the interface. As exported variables may not start with double underscores (which the parser asserts) this is not a problem for regular Jinja code, but if this node is used in an extension extra care must be taken. The list of names may contain tuples if aliases are wanted. """ fields = ('template', 'names', 'with_context') class ExprStmt(Stmt): """A statement that evaluates an expression and discards the result.""" fields = ('node',) class Assign(Stmt): """Assigns an expression to a target.""" fields = ('target', 'node') class AssignBlock(Stmt): """Assigns a block to a target.""" fields = ('target', 'filter', 'body') class Expr(Node): """Baseclass for all expressions.""" abstract = True def as_const(self, eval_ctx=None): """Return the value of the expression as constant or raise :exc:`Impossible` if this was not possible. An :class:`EvalContext` can be provided, if none is given a default context is created which requires the nodes to have an attached environment. .. versionchanged:: 2.4 the `eval_ctx` parameter was added. """ raise Impossible() def can_assign(self): """Check if it's possible to assign something to this node.""" return False class BinExpr(Expr): """Baseclass for all binary expressions.""" fields = ('left', 'right') operator = None abstract = True def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) # intercepted operators cannot be folded at compile time if self.environment.sandboxed and \ self.operator in self.environment.intercepted_binops: raise Impossible() f = _binop_to_func[self.operator] try: return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx)) except Exception: raise Impossible() class UnaryExpr(Expr): """Baseclass for all unary expressions.""" fields = ('node',) operator = None abstract = True def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) # intercepted operators cannot be folded at compile time if self.environment.sandboxed and \ self.operator in self.environment.intercepted_unops: raise Impossible() f = _uaop_to_func[self.operator] try: return f(self.node.as_const(eval_ctx)) except Exception: raise Impossible() class Name(Expr): """Looks up a name or stores a value in a name. The `ctx` of the node can be one of the following values: - `store`: store a value in the name - `load`: load that name - `param`: like `store` but if the name was defined as function parameter. """ fields = ('name', 'ctx') def can_assign(self): return self.name not in ('true', 'false', 'none', 'True', 'False', 'None') class NSRef(Expr): """Reference to a namespace value assignment""" fields = ('name', 'attr') def can_assign(self): # We don't need any special checks here; NSRef assignments have a # runtime check to ensure the target is a namespace object which will # have been checked already as it is created using a normal assignment # which goes through a `Name` node. return True class Literal(Expr): """Baseclass for literals.""" abstract = True class Const(Literal): """All constant values. The parser will return this node for simple constants such as ``42`` or ``"foo"`` but it can be used to store more complex values such as lists too. Only constants with a safe representation (objects where ``eval(repr(x)) == x`` is true). """ fields = ('value',) def as_const(self, eval_ctx=None): rv = self.value if PY2 and type(rv) is text_type and \ self.environment.policies['compiler.ascii_str']: try: rv = rv.encode('ascii') except UnicodeError: pass return rv @classmethod def from_untrusted(cls, value, lineno=None, environment=None): """Return a const object if the value is representable as constant value in the generated code, otherwise it will raise an `Impossible` exception. """ from .compiler import has_safe_repr if not has_safe_repr(value): raise Impossible() return cls(value, lineno=lineno, environment=environment) class TemplateData(Literal): """A constant template string.""" fields = ('data',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) if eval_ctx.volatile: raise Impossible() if eval_ctx.autoescape: return Markup(self.data) return self.data class Tuple(Literal): """For loop unpacking and some other things like multiple arguments for subscripts. Like for :class:`Name` `ctx` specifies if the tuple is used for loading the names or storing. """ fields = ('items', 'ctx') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return tuple(x.as_const(eval_ctx) for x in self.items) def can_assign(self): for item in self.items: if not item.can_assign(): return False return True class List(Literal): """Any list literal such as ``[1, 2, 3]``""" fields = ('items',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return [x.as_const(eval_ctx) for x in self.items] class Dict(Literal): """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of :class:`Pair` nodes. """ fields = ('items',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return dict(x.as_const(eval_ctx) for x in self.items) class Pair(Helper): """A key, value pair for dicts.""" fields = ('key', 'value') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx) class Keyword(Helper): """A key, value pair for keyword arguments where key is a string.""" fields = ('key', 'value') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return self.key, self.value.as_const(eval_ctx) class CondExpr(Expr): """A conditional expression (inline if expression). (``{{ foo if bar else baz }}``) """ fields = ('test', 'expr1', 'expr2') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) if self.test.as_const(eval_ctx): return self.expr1.as_const(eval_ctx) # if we evaluate to an undefined object, we better do that at runtime if self.expr2 is None: raise Impossible() return self.expr2.as_const(eval_ctx) def args_as_const(node, eval_ctx): args = [x.as_const(eval_ctx) for x in node.args] kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs) if node.dyn_args is not None: try: args.extend(node.dyn_args.as_const(eval_ctx)) except Exception: raise Impossible() if node.dyn_kwargs is not None: try: kwargs.update(node.dyn_kwargs.as_const(eval_ctx)) except Exception: raise Impossible() return args, kwargs class Filter(Expr): """This node applies a filter on an expression. `name` is the name of the filter, the rest of the fields are the same as for :class:`Call`. If the `node` of a filter is `None` the contents of the last buffer are filtered. Buffers are created by macros and filter blocks. """ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) if eval_ctx.volatile or self.node is None: raise Impossible() # we have to be careful here because we call filter_ below. # if this variable would be called filter, 2to3 would wrap the # call in a list beause it is assuming we are talking about the # builtin filter function here which no longer returns a list in # python 3. because of that, do not rename filter_ to filter! filter_ = self.environment.filters.get(self.name) if filter_ is None or getattr(filter_, 'contextfilter', False): raise Impossible() # We cannot constant handle async filters, so we need to make sure # to not go down this path. if ( eval_ctx.environment.is_async and getattr(filter_, 'asyncfiltervariant', False) ): raise Impossible() args, kwargs = args_as_const(self, eval_ctx) args.insert(0, self.node.as_const(eval_ctx)) if getattr(filter_, 'evalcontextfilter', False): args.insert(0, eval_ctx) elif getattr(filter_, 'environmentfilter', False): args.insert(0, self.environment) try: return filter_(*args, **kwargs) except Exception: raise Impossible() class Test(Expr): """Applies a test on an expression. `name` is the name of the test, the rest of the fields are the same as for :class:`Call`. """ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') def as_const(self, eval_ctx=None): test = self.environment.tests.get(self.name) if test is None: raise Impossible() eval_ctx = get_eval_context(self, eval_ctx) args, kwargs = args_as_const(self, eval_ctx) args.insert(0, self.node.as_const(eval_ctx)) try: return test(*args, **kwargs) except Exception: raise Impossible() class Call(Expr): """Calls an expression. `args` is a list of arguments, `kwargs` a list of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args` and `dyn_kwargs` has to be either `None` or a node that is used as node for dynamic positional (``*args``) or keyword (``**kwargs``) arguments. """ fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') class Getitem(Expr): """Get an attribute or item from an expression and prefer the item.""" fields = ('node', 'arg', 'ctx') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) if self.ctx != 'load': raise Impossible() try: return self.environment.getitem(self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)) except Exception: raise Impossible() def can_assign(self): return False class Getattr(Expr): """Get an attribute or item from an expression that is a ascii-only bytestring and prefer the attribute. """ fields = ('node', 'attr', 'ctx') def as_const(self, eval_ctx=None): if self.ctx != 'load': raise Impossible() try: eval_ctx = get_eval_context(self, eval_ctx) return self.environment.getattr(self.node.as_const(eval_ctx), self.attr) except Exception: raise Impossible() def can_assign(self): return False class Slice(Expr): """Represents a slice object. This must only be used as argument for :class:`Subscript`. """ fields = ('start', 'stop', 'step') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) def const(obj): if obj is None: return None return obj.as_const(eval_ctx) return slice(const(self.start), const(self.stop), const(self.step)) class Concat(Expr): """Concatenates the list of expressions provided after converting them to unicode. """ fields = ('nodes',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes) class Compare(Expr): """Compares an expression with some other expressions. `ops` must be a list of :class:`Operand`\\s. """ fields = ('expr', 'ops') def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) result = value = self.expr.as_const(eval_ctx) try: for op in self.ops: new_value = op.expr.as_const(eval_ctx) result = _cmpop_to_func[op.op](value, new_value) value = new_value except Exception: raise Impossible() return result class Operand(Helper): """Holds an operator and an expression.""" fields = ('op', 'expr') if __debug__: Operand.__doc__ += '\nThe following operators are available: ' + \ ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) | set(_uaop_to_func) | set(_cmpop_to_func))) class Mul(BinExpr): """Multiplies the left with the right node.""" operator = '*' class Div(BinExpr): """Divides the left by the right node.""" operator = '/' class FloorDiv(BinExpr): """Divides the left by the right node and truncates conver the result into an integer by truncating. """ operator = '//' class Add(BinExpr): """Add the left to the right node.""" operator = '+' class Sub(BinExpr): """Subtract the right from the left node.""" operator = '-' class Mod(BinExpr): """Left modulo right.""" operator = '%' class Pow(BinExpr): """Left to the power of right.""" operator = '**' class And(BinExpr): """Short circuited AND.""" operator = 'and' def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx) class Or(BinExpr): """Short circuited OR.""" operator = 'or' def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx) class Not(UnaryExpr): """Negate the expression.""" operator = 'not' class Neg(UnaryExpr): """Make the expression negative.""" operator = '-' class Pos(UnaryExpr): """Make the expression positive (noop for most expressions)""" operator = '+' # Helpers for extensions class EnvironmentAttribute(Expr): """Loads an attribute from the environment object. This is useful for extensions that want to call a callback stored on the environment. """ fields = ('name',) class ExtensionAttribute(Expr): """Returns the attribute of an extension bound to the environment. The identifier is the identifier of the :class:`Extension`. This node is usually constructed by calling the :meth:`~jinja2.ext.Extension.attr` method on an extension. """ fields = ('identifier', 'name') class ImportedName(Expr): """If created with an import name the import name is returned on node access. For example ``ImportedName('cgi.escape')`` returns the `escape` function from the cgi module on evaluation. Imports are optimized by the compiler so there is no need to assign them to local variables. """ fields = ('importname',) class InternalName(Expr): """An internal name in the compiler. You cannot create these nodes yourself but the parser provides a :meth:`~jinja2.parser.Parser.free_identifier` method that creates a new identifier for you. This identifier is not available from the template and is not threated specially by the compiler. """ fields = ('name',) def __init__(self): raise TypeError('Can\'t create internal names. Use the ' '`free_identifier` method on a parser.') class MarkSafe(Expr): """Mark the wrapped expression as safe (wrap it as `Markup`).""" fields = ('expr',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) return Markup(self.expr.as_const(eval_ctx)) class MarkSafeIfAutoescape(Expr): """Mark the wrapped expression as safe (wrap it as `Markup`) but only if autoescaping is active. .. versionadded:: 2.5 """ fields = ('expr',) def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) if eval_ctx.volatile: raise Impossible() expr = self.expr.as_const(eval_ctx) if eval_ctx.autoescape: return Markup(expr) return expr class ContextReference(Expr): """Returns the current template context. It can be used like a :class:`Name` node, with a ``'load'`` ctx and will return the current :class:`~jinja2.runtime.Context` object. Here an example that assigns the current template name to a variable named `foo`:: Assign(Name('foo', ctx='store'), Getattr(ContextReference(), 'name')) """ class Continue(Stmt): """Continue a loop.""" class Break(Stmt): """Break a loop.""" class Scope(Stmt): """An artificial scope.""" fields = ('body',) class OverlayScope(Stmt): """An overlay scope for extensions. This is a largely unoptimized scope that however can be used to introduce completely arbitrary variables into a sub scope from a dictionary or dictionary like object. The `context` field has to evaluate to a dictionary object. Example usage:: OverlayScope(context=self.call_method('get_context'), body=[...]) .. versionadded:: 2.10 """ fields = ('context', 'body') class EvalContextModifier(Stmt): """Modifies the eval context. For each option that should be modified, a :class:`Keyword` has to be added to the :attr:`options` list. Example to change the `autoescape` setting:: EvalContextModifier(options=[Keyword('autoescape', Const(True))]) """ fields = ('options',) class ScopedEvalContextModifier(EvalContextModifier): """Modifies the eval context and reverts it later. Works exactly like :class:`EvalContextModifier` but will only modify the :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`. """ fields = ('body',) # make sure nobody creates custom nodes def _failing_new(*args, **kwargs): raise TypeError('can\'t create custom node types') NodeType.__new__ = staticmethod(_failing_new); del _failing_new Jinja2-2.10/jinja2/asyncfilters.py0000644000175000017500000001006013127761734017216 0ustar daviddavid00000000000000from functools import wraps from jinja2.asyncsupport import auto_aiter from jinja2 import filters async def auto_to_seq(value): seq = [] if hasattr(value, '__aiter__'): async for item in value: seq.append(item) else: for item in value: seq.append(item) return seq async def async_select_or_reject(args, kwargs, modfunc, lookup_attr): seq, func = filters.prepare_select_or_reject( args, kwargs, modfunc, lookup_attr) if seq: async for item in auto_aiter(seq): if func(item): yield item def dualfilter(normal_filter, async_filter): wrap_evalctx = False if getattr(normal_filter, 'environmentfilter', False): is_async = lambda args: args[0].is_async wrap_evalctx = False else: if not getattr(normal_filter, 'evalcontextfilter', False) and \ not getattr(normal_filter, 'contextfilter', False): wrap_evalctx = True is_async = lambda args: args[0].environment.is_async @wraps(normal_filter) def wrapper(*args, **kwargs): b = is_async(args) if wrap_evalctx: args = args[1:] if b: return async_filter(*args, **kwargs) return normal_filter(*args, **kwargs) if wrap_evalctx: wrapper.evalcontextfilter = True wrapper.asyncfiltervariant = True return wrapper def asyncfiltervariant(original): def decorator(f): return dualfilter(original, f) return decorator @asyncfiltervariant(filters.do_first) async def do_first(environment, seq): try: return await auto_aiter(seq).__anext__() except StopAsyncIteration: return environment.undefined('No first item, sequence was empty.') @asyncfiltervariant(filters.do_groupby) async def do_groupby(environment, value, attribute): expr = filters.make_attrgetter(environment, attribute) return [filters._GroupTuple(key, await auto_to_seq(values)) for key, values in filters.groupby(sorted( await auto_to_seq(value), key=expr), expr)] @asyncfiltervariant(filters.do_join) async def do_join(eval_ctx, value, d=u'', attribute=None): return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute) @asyncfiltervariant(filters.do_list) async def do_list(value): return await auto_to_seq(value) @asyncfiltervariant(filters.do_reject) async def do_reject(*args, **kwargs): return async_select_or_reject(args, kwargs, lambda x: not x, False) @asyncfiltervariant(filters.do_rejectattr) async def do_rejectattr(*args, **kwargs): return async_select_or_reject(args, kwargs, lambda x: not x, True) @asyncfiltervariant(filters.do_select) async def do_select(*args, **kwargs): return async_select_or_reject(args, kwargs, lambda x: x, False) @asyncfiltervariant(filters.do_selectattr) async def do_selectattr(*args, **kwargs): return async_select_or_reject(args, kwargs, lambda x: x, True) @asyncfiltervariant(filters.do_map) async def do_map(*args, **kwargs): seq, func = filters.prepare_map(args, kwargs) if seq: async for item in auto_aiter(seq): yield func(item) @asyncfiltervariant(filters.do_sum) async def do_sum(environment, iterable, attribute=None, start=0): rv = start if attribute is not None: func = filters.make_attrgetter(environment, attribute) else: func = lambda x: x async for item in auto_aiter(iterable): rv += func(item) return rv @asyncfiltervariant(filters.do_slice) async def do_slice(value, slices, fill_with=None): return filters.do_slice(await auto_to_seq(value), slices, fill_with) ASYNC_FILTERS = { 'first': do_first, 'groupby': do_groupby, 'join': do_join, 'list': do_list, # we intentionally do not support do_last because that would be # ridiculous 'reject': do_reject, 'rejectattr': do_rejectattr, 'map': do_map, 'select': do_select, 'selectattr': do_selectattr, 'sum': do_sum, 'slice': do_slice, } Jinja2-2.10/jinja2/loaders.py0000644000175000017500000004174613127761734016160 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.loaders ~~~~~~~~~~~~~~ Jinja loader classes. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ import os import sys import weakref from types import ModuleType from os import path from hashlib import sha1 from jinja2.exceptions import TemplateNotFound from jinja2.utils import open_if_exists, internalcode from jinja2._compat import string_types, iteritems def split_template_path(template): """Split a path into segments and perform a sanity check. If it detects '..' in the path it will raise a `TemplateNotFound` error. """ pieces = [] for piece in template.split('/'): if path.sep in piece \ or (path.altsep and path.altsep in piece) or \ piece == path.pardir: raise TemplateNotFound(template) elif piece and piece != '.': pieces.append(piece) return pieces class BaseLoader(object): """Baseclass for all loaders. Subclass this and override `get_source` to implement a custom loading mechanism. The environment provides a `get_template` method that calls the loader's `load` method to get the :class:`Template` object. A very basic example for a loader that looks up templates on the file system could look like this:: from jinja2 import BaseLoader, TemplateNotFound from os.path import join, exists, getmtime class MyLoader(BaseLoader): def __init__(self, path): self.path = path def get_source(self, environment, template): path = join(self.path, template) if not exists(path): raise TemplateNotFound(template) mtime = getmtime(path) with file(path) as f: source = f.read().decode('utf-8') return source, path, lambda: mtime == getmtime(path) """ #: if set to `False` it indicates that the loader cannot provide access #: to the source of templates. #: #: .. versionadded:: 2.4 has_source_access = True def get_source(self, environment, template): """Get the template source, filename and reload helper for a template. It's passed the environment and template name and has to return a tuple in the form ``(source, filename, uptodate)`` or raise a `TemplateNotFound` error if it can't locate the template. The source part of the returned tuple must be the source of the template as unicode string or a ASCII bytestring. The filename should be the name of the file on the filesystem if it was loaded from there, otherwise `None`. The filename is used by python for the tracebacks if no loader extension is used. The last item in the tuple is the `uptodate` function. If auto reloading is enabled it's always called to check if the template changed. No arguments are passed so the function must store the old state somewhere (for example in a closure). If it returns `False` the template will be reloaded. """ if not self.has_source_access: raise RuntimeError('%s cannot provide access to the source' % self.__class__.__name__) raise TemplateNotFound(template) def list_templates(self): """Iterates over all templates. If the loader does not support that it should raise a :exc:`TypeError` which is the default behavior. """ raise TypeError('this loader cannot iterate over all templates') @internalcode def load(self, environment, name, globals=None): """Loads a template. This method looks up the template in the cache or loads one by calling :meth:`get_source`. Subclasses should not override this method as loaders working on collections of other loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) will not call this method but `get_source` directly. """ code = None if globals is None: globals = {} # first we try to get the source for this template together # with the filename and the uptodate function. source, filename, uptodate = self.get_source(environment, name) # try to load the code from the bytecode cache if there is a # bytecode cache configured. bcc = environment.bytecode_cache if bcc is not None: bucket = bcc.get_bucket(environment, name, filename, source) code = bucket.code # if we don't have code so far (not cached, no longer up to # date) etc. we compile the template if code is None: code = environment.compile(source, name, filename) # if the bytecode cache is available and the bucket doesn't # have a code so far, we give the bucket the new code and put # it back to the bytecode cache. if bcc is not None and bucket.code is None: bucket.code = code bcc.set_bucket(bucket) return environment.template_class.from_code(environment, code, globals, uptodate) class FileSystemLoader(BaseLoader): """Loads templates from the file system. This loader can find templates in folders on the file system and is the preferred way to load them. The loader takes the path to the templates as string, or if multiple locations are wanted a list of them which is then looked up in the given order:: >>> loader = FileSystemLoader('/path/to/templates') >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) Per default the template encoding is ``'utf-8'`` which can be changed by setting the `encoding` parameter to something else. To follow symbolic links, set the *followlinks* parameter to ``True``:: >>> loader = FileSystemLoader('/path/to/templates', followlinks=True) .. versionchanged:: 2.8+ The *followlinks* parameter was added. """ def __init__(self, searchpath, encoding='utf-8', followlinks=False): if isinstance(searchpath, string_types): searchpath = [searchpath] self.searchpath = list(searchpath) self.encoding = encoding self.followlinks = followlinks def get_source(self, environment, template): pieces = split_template_path(template) for searchpath in self.searchpath: filename = path.join(searchpath, *pieces) f = open_if_exists(filename) if f is None: continue try: contents = f.read().decode(self.encoding) finally: f.close() mtime = path.getmtime(filename) def uptodate(): try: return path.getmtime(filename) == mtime except OSError: return False return contents, filename, uptodate raise TemplateNotFound(template) def list_templates(self): found = set() for searchpath in self.searchpath: walk_dir = os.walk(searchpath, followlinks=self.followlinks) for dirpath, dirnames, filenames in walk_dir: for filename in filenames: template = os.path.join(dirpath, filename) \ [len(searchpath):].strip(os.path.sep) \ .replace(os.path.sep, '/') if template[:2] == './': template = template[2:] if template not in found: found.add(template) return sorted(found) class PackageLoader(BaseLoader): """Load templates from python eggs or packages. It is constructed with the name of the python package and the path to the templates in that package:: loader = PackageLoader('mypackage', 'views') If the package path is not given, ``'templates'`` is assumed. Per default the template encoding is ``'utf-8'`` which can be changed by setting the `encoding` parameter to something else. Due to the nature of eggs it's only possible to reload templates if the package was loaded from the file system and not a zip file. """ def __init__(self, package_name, package_path='templates', encoding='utf-8'): from pkg_resources import DefaultProvider, ResourceManager, \ get_provider provider = get_provider(package_name) self.encoding = encoding self.manager = ResourceManager() self.filesystem_bound = isinstance(provider, DefaultProvider) self.provider = provider self.package_path = package_path def get_source(self, environment, template): pieces = split_template_path(template) p = '/'.join((self.package_path,) + tuple(pieces)) if not self.provider.has_resource(p): raise TemplateNotFound(template) filename = uptodate = None if self.filesystem_bound: filename = self.provider.get_resource_filename(self.manager, p) mtime = path.getmtime(filename) def uptodate(): try: return path.getmtime(filename) == mtime except OSError: return False source = self.provider.get_resource_string(self.manager, p) return source.decode(self.encoding), filename, uptodate def list_templates(self): path = self.package_path if path[:2] == './': path = path[2:] elif path == '.': path = '' offset = len(path) results = [] def _walk(path): for filename in self.provider.resource_listdir(path): fullname = path + '/' + filename if self.provider.resource_isdir(fullname): _walk(fullname) else: results.append(fullname[offset:].lstrip('/')) _walk(path) results.sort() return results class DictLoader(BaseLoader): """Loads a template from a python dict. It's passed a dict of unicode strings bound to template names. This loader is useful for unittesting: >>> loader = DictLoader({'index.html': 'source here'}) Because auto reloading is rarely useful this is disabled per default. """ def __init__(self, mapping): self.mapping = mapping def get_source(self, environment, template): if template in self.mapping: source = self.mapping[template] return source, None, lambda: source == self.mapping.get(template) raise TemplateNotFound(template) def list_templates(self): return sorted(self.mapping) class FunctionLoader(BaseLoader): """A loader that is passed a function which does the loading. The function receives the name of the template and has to return either an unicode string with the template source, a tuple in the form ``(source, filename, uptodatefunc)`` or `None` if the template does not exist. >>> def load_template(name): ... if name == 'index.html': ... return '...' ... >>> loader = FunctionLoader(load_template) The `uptodatefunc` is a function that is called if autoreload is enabled and has to return `True` if the template is still up to date. For more details have a look at :meth:`BaseLoader.get_source` which has the same return value. """ def __init__(self, load_func): self.load_func = load_func def get_source(self, environment, template): rv = self.load_func(template) if rv is None: raise TemplateNotFound(template) elif isinstance(rv, string_types): return rv, None, None return rv class PrefixLoader(BaseLoader): """A loader that is passed a dict of loaders where each loader is bound to a prefix. The prefix is delimited from the template by a slash per default, which can be changed by setting the `delimiter` argument to something else:: loader = PrefixLoader({ 'app1': PackageLoader('mypackage.app1'), 'app2': PackageLoader('mypackage.app2') }) By loading ``'app1/index.html'`` the file from the app1 package is loaded, by loading ``'app2/index.html'`` the file from the second. """ def __init__(self, mapping, delimiter='/'): self.mapping = mapping self.delimiter = delimiter def get_loader(self, template): try: prefix, name = template.split(self.delimiter, 1) loader = self.mapping[prefix] except (ValueError, KeyError): raise TemplateNotFound(template) return loader, name def get_source(self, environment, template): loader, name = self.get_loader(template) try: return loader.get_source(environment, name) except TemplateNotFound: # re-raise the exception with the correct filename here. # (the one that includes the prefix) raise TemplateNotFound(template) @internalcode def load(self, environment, name, globals=None): loader, local_name = self.get_loader(name) try: return loader.load(environment, local_name, globals) except TemplateNotFound: # re-raise the exception with the correct filename here. # (the one that includes the prefix) raise TemplateNotFound(name) def list_templates(self): result = [] for prefix, loader in iteritems(self.mapping): for template in loader.list_templates(): result.append(prefix + self.delimiter + template) return result class ChoiceLoader(BaseLoader): """This loader works like the `PrefixLoader` just that no prefix is specified. If a template could not be found by one loader the next one is tried. >>> loader = ChoiceLoader([ ... FileSystemLoader('/path/to/user/templates'), ... FileSystemLoader('/path/to/system/templates') ... ]) This is useful if you want to allow users to override builtin templates from a different location. """ def __init__(self, loaders): self.loaders = loaders def get_source(self, environment, template): for loader in self.loaders: try: return loader.get_source(environment, template) except TemplateNotFound: pass raise TemplateNotFound(template) @internalcode def load(self, environment, name, globals=None): for loader in self.loaders: try: return loader.load(environment, name, globals) except TemplateNotFound: pass raise TemplateNotFound(name) def list_templates(self): found = set() for loader in self.loaders: found.update(loader.list_templates()) return sorted(found) class _TemplateModule(ModuleType): """Like a normal module but with support for weak references""" class ModuleLoader(BaseLoader): """This loader loads templates from precompiled templates. Example usage: >>> loader = ChoiceLoader([ ... ModuleLoader('/path/to/compiled/templates'), ... FileSystemLoader('/path/to/templates') ... ]) Templates can be precompiled with :meth:`Environment.compile_templates`. """ has_source_access = False def __init__(self, path): package_name = '_jinja2_module_templates_%x' % id(self) # create a fake module that looks for the templates in the # path given. mod = _TemplateModule(package_name) if isinstance(path, string_types): path = [path] else: path = list(path) mod.__path__ = path sys.modules[package_name] = weakref.proxy(mod, lambda x: sys.modules.pop(package_name, None)) # the only strong reference, the sys.modules entry is weak # so that the garbage collector can remove it once the # loader that created it goes out of business. self.module = mod self.package_name = package_name @staticmethod def get_template_key(name): return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() @staticmethod def get_module_filename(name): return ModuleLoader.get_template_key(name) + '.py' @internalcode def load(self, environment, name, globals=None): key = self.get_template_key(name) module = '%s.%s' % (self.package_name, key) mod = getattr(self.module, module, None) if mod is None: try: mod = __import__(module, None, None, ['root']) except ImportError: raise TemplateNotFound(name) # remove the entry from sys.modules, we only want the attribute # on the module object we have stored on the loader. sys.modules.pop(module, None) return environment.template_class.from_module_dict( environment, mod.__dict__, globals) Jinja2-2.10/jinja2/defaults.py0000644000175000017500000000257013130203104016276 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.defaults ~~~~~~~~~~~~~~~ Jinja default filters and tags. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ from jinja2._compat import range_type from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner, Namespace # defaults for the parser / lexer BLOCK_START_STRING = '{%' BLOCK_END_STRING = '%}' VARIABLE_START_STRING = '{{' VARIABLE_END_STRING = '}}' COMMENT_START_STRING = '{#' COMMENT_END_STRING = '#}' LINE_STATEMENT_PREFIX = None LINE_COMMENT_PREFIX = None TRIM_BLOCKS = False LSTRIP_BLOCKS = False NEWLINE_SEQUENCE = '\n' KEEP_TRAILING_NEWLINE = False # default filters, tests and namespace from jinja2.filters import FILTERS as DEFAULT_FILTERS from jinja2.tests import TESTS as DEFAULT_TESTS DEFAULT_NAMESPACE = { 'range': range_type, 'dict': dict, 'lipsum': generate_lorem_ipsum, 'cycler': Cycler, 'joiner': Joiner, 'namespace': Namespace } # default policies DEFAULT_POLICIES = { 'compiler.ascii_str': True, 'urlize.rel': 'noopener', 'urlize.target': None, 'truncate.leeway': 5, 'json.dumps_function': None, 'json.dumps_kwargs': {'sort_keys': True}, 'ext.i18n.trimmed': False, } # export all constants __all__ = tuple(x for x in locals().keys() if x.isupper()) Jinja2-2.10/jinja2/meta.py0000644000175000017500000001036413127761734015445 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.meta ~~~~~~~~~~~ This module implements various functions that exposes information about templates that might be interesting for various kinds of applications. :copyright: (c) 2017 by the Jinja Team, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from jinja2 import nodes from jinja2.compiler import CodeGenerator from jinja2._compat import string_types, iteritems class TrackingCodeGenerator(CodeGenerator): """We abuse the code generator for introspection.""" def __init__(self, environment): CodeGenerator.__init__(self, environment, '', '') self.undeclared_identifiers = set() def write(self, x): """Don't write.""" def enter_frame(self, frame): """Remember all undeclared identifiers.""" CodeGenerator.enter_frame(self, frame) for _, (action, param) in iteritems(frame.symbols.loads): if action == 'resolve': self.undeclared_identifiers.add(param) def find_undeclared_variables(ast): """Returns a set of all variables in the AST that will be looked up from the context at runtime. Because at compile time it's not known which variables will be used depending on the path the execution takes at runtime, all variables are returned. >>> from jinja2 import Environment, meta >>> env = Environment() >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') >>> meta.find_undeclared_variables(ast) == set(['bar']) True .. admonition:: Implementation Internally the code generator is used for finding undeclared variables. This is good to know because the code generator might raise a :exc:`TemplateAssertionError` during compilation and as a matter of fact this function can currently raise that exception as well. """ codegen = TrackingCodeGenerator(ast.environment) codegen.visit(ast) return codegen.undeclared_identifiers def find_referenced_templates(ast): """Finds all the referenced templates from the AST. This will return an iterator over all the hardcoded template extensions, inclusions and imports. If dynamic inheritance or inclusion is used, `None` will be yielded. >>> from jinja2 import Environment, meta >>> env = Environment() >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') >>> list(meta.find_referenced_templates(ast)) ['layout.html', None] This function is useful for dependency tracking. For example if you want to rebuild parts of the website after a layout template has changed. """ for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)): if not isinstance(node.template, nodes.Const): # a tuple with some non consts in there if isinstance(node.template, (nodes.Tuple, nodes.List)): for template_name in node.template.items: # something const, only yield the strings and ignore # non-string consts that really just make no sense if isinstance(template_name, nodes.Const): if isinstance(template_name.value, string_types): yield template_name.value # something dynamic in there else: yield None # something dynamic we don't know about here else: yield None continue # constant is a basestring, direct template name if isinstance(node.template.value, string_types): yield node.template.value # a tuple or list (latter *should* not happen) made of consts, # yield the consts that are strings. We could warn here for # non string values elif isinstance(node, nodes.Include) and \ isinstance(node.template.value, (tuple, list)): for template_name in node.template.value: if isinstance(template_name, string_types): yield template_name # something else we don't care about, we could warn here else: yield None Jinja2-2.10/jinja2/compiler.py0000644000175000017500000017755213176350145016340 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.compiler ~~~~~~~~~~~~~~~ Compiles nodes into python code. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ from itertools import chain from copy import deepcopy from keyword import iskeyword as is_python_keyword from functools import update_wrapper from jinja2 import nodes from jinja2.nodes import EvalContext from jinja2.visitor import NodeVisitor from jinja2.optimizer import Optimizer from jinja2.exceptions import TemplateAssertionError from jinja2.utils import Markup, concat, escape from jinja2._compat import range_type, text_type, string_types, \ iteritems, NativeStringIO, imap, izip from jinja2.idtracking import Symbols, VAR_LOAD_PARAMETER, \ VAR_LOAD_RESOLVE, VAR_LOAD_ALIAS, VAR_LOAD_UNDEFINED operators = { 'eq': '==', 'ne': '!=', 'gt': '>', 'gteq': '>=', 'lt': '<', 'lteq': '<=', 'in': 'in', 'notin': 'not in' } # what method to iterate over items do we want to use for dict iteration # in generated code? on 2.x let's go with iteritems, on 3.x with items if hasattr(dict, 'iteritems'): dict_item_iter = 'iteritems' else: dict_item_iter = 'items' code_features = ['division'] # does this python version support generator stops? (PEP 0479) try: exec('from __future__ import generator_stop') code_features.append('generator_stop') except SyntaxError: pass # does this python version support yield from? try: exec('def f(): yield from x()') except SyntaxError: supports_yield_from = False else: supports_yield_from = True def optimizeconst(f): def new_func(self, node, frame, **kwargs): # Only optimize if the frame is not volatile if self.optimized and not frame.eval_ctx.volatile: new_node = self.optimizer.visit(node, frame.eval_ctx) if new_node != node: return self.visit(new_node, frame) return f(self, node, frame, **kwargs) return update_wrapper(new_func, f) def generate(node, environment, name, filename, stream=None, defer_init=False, optimized=True): """Generate the python source for a node tree.""" if not isinstance(node, nodes.Template): raise TypeError('Can\'t compile non template nodes') generator = environment.code_generator_class(environment, name, filename, stream, defer_init, optimized) generator.visit(node) if stream is None: return generator.stream.getvalue() def has_safe_repr(value): """Does the node have a safe representation?""" if value is None or value is NotImplemented or value is Ellipsis: return True if type(value) in (bool, int, float, complex, range_type, Markup) + string_types: return True if type(value) in (tuple, list, set, frozenset): for item in value: if not has_safe_repr(item): return False return True elif type(value) is dict: for key, value in iteritems(value): if not has_safe_repr(key): return False if not has_safe_repr(value): return False return True return False def find_undeclared(nodes, names): """Check if the names passed are accessed undeclared. The return value is a set of all the undeclared names from the sequence of names found. """ visitor = UndeclaredNameVisitor(names) try: for node in nodes: visitor.visit(node) except VisitorExit: pass return visitor.undeclared class MacroRef(object): def __init__(self, node): self.node = node self.accesses_caller = False self.accesses_kwargs = False self.accesses_varargs = False class Frame(object): """Holds compile time information for us.""" def __init__(self, eval_ctx, parent=None, level=None): self.eval_ctx = eval_ctx self.symbols = Symbols(parent and parent.symbols or None, level=level) # a toplevel frame is the root + soft frames such as if conditions. self.toplevel = False # the root frame is basically just the outermost frame, so no if # conditions. This information is used to optimize inheritance # situations. self.rootlevel = False # in some dynamic inheritance situations the compiler needs to add # write tests around output statements. self.require_output_check = parent and parent.require_output_check # inside some tags we are using a buffer rather than yield statements. # this for example affects {% filter %} or {% macro %}. If a frame # is buffered this variable points to the name of the list used as # buffer. self.buffer = None # the name of the block we're in, otherwise None. self.block = parent and parent.block or None # the parent of this frame self.parent = parent if parent is not None: self.buffer = parent.buffer def copy(self): """Create a copy of the current one.""" rv = object.__new__(self.__class__) rv.__dict__.update(self.__dict__) rv.symbols = self.symbols.copy() return rv def inner(self, isolated=False): """Return an inner frame.""" if isolated: return Frame(self.eval_ctx, level=self.symbols.level + 1) return Frame(self.eval_ctx, self) def soft(self): """Return a soft frame. A soft frame may not be modified as standalone thing as it shares the resources with the frame it was created of, but it's not a rootlevel frame any longer. This is only used to implement if-statements. """ rv = self.copy() rv.rootlevel = False return rv __copy__ = copy class VisitorExit(RuntimeError): """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" class DependencyFinderVisitor(NodeVisitor): """A visitor that collects filter and test calls.""" def __init__(self): self.filters = set() self.tests = set() def visit_Filter(self, node): self.generic_visit(node) self.filters.add(node.name) def visit_Test(self, node): self.generic_visit(node) self.tests.add(node.name) def visit_Block(self, node): """Stop visiting at blocks.""" class UndeclaredNameVisitor(NodeVisitor): """A visitor that checks if a name is accessed without being declared. This is different from the frame visitor as it will not stop at closure frames. """ def __init__(self, names): self.names = set(names) self.undeclared = set() def visit_Name(self, node): if node.ctx == 'load' and node.name in self.names: self.undeclared.add(node.name) if self.undeclared == self.names: raise VisitorExit() else: self.names.discard(node.name) def visit_Block(self, node): """Stop visiting a blocks.""" class CompilerExit(Exception): """Raised if the compiler encountered a situation where it just doesn't make sense to further process the code. Any block that raises such an exception is not further processed. """ class CodeGenerator(NodeVisitor): def __init__(self, environment, name, filename, stream=None, defer_init=False, optimized=True): if stream is None: stream = NativeStringIO() self.environment = environment self.name = name self.filename = filename self.stream = stream self.created_block_context = False self.defer_init = defer_init self.optimized = optimized if optimized: self.optimizer = Optimizer(environment) # aliases for imports self.import_aliases = {} # a registry for all blocks. Because blocks are moved out # into the global python scope they are registered here self.blocks = {} # the number of extends statements so far self.extends_so_far = 0 # some templates have a rootlevel extends. In this case we # can safely assume that we're a child template and do some # more optimizations. self.has_known_extends = False # the current line number self.code_lineno = 1 # registry of all filters and tests (global, not block local) self.tests = {} self.filters = {} # the debug information self.debug_info = [] self._write_debug_info = None # the number of new lines before the next write() self._new_lines = 0 # the line number of the last written statement self._last_line = 0 # true if nothing was written so far. self._first_write = True # used by the `temporary_identifier` method to get new # unique, temporary identifier self._last_identifier = 0 # the current indentation self._indentation = 0 # Tracks toplevel assignments self._assign_stack = [] # Tracks parameter definition blocks self._param_def_block = [] # Tracks the current context. self._context_reference_stack = ['context'] # -- Various compilation helpers def fail(self, msg, lineno): """Fail with a :exc:`TemplateAssertionError`.""" raise TemplateAssertionError(msg, lineno, self.name, self.filename) def temporary_identifier(self): """Get a new unique identifier.""" self._last_identifier += 1 return 't_%d' % self._last_identifier def buffer(self, frame): """Enable buffering for the frame from that point onwards.""" frame.buffer = self.temporary_identifier() self.writeline('%s = []' % frame.buffer) def return_buffer_contents(self, frame, force_unescaped=False): """Return the buffer contents of the frame.""" if not force_unescaped: if frame.eval_ctx.volatile: self.writeline('if context.eval_ctx.autoescape:') self.indent() self.writeline('return Markup(concat(%s))' % frame.buffer) self.outdent() self.writeline('else:') self.indent() self.writeline('return concat(%s)' % frame.buffer) self.outdent() return elif frame.eval_ctx.autoescape: self.writeline('return Markup(concat(%s))' % frame.buffer) return self.writeline('return concat(%s)' % frame.buffer) def indent(self): """Indent by one.""" self._indentation += 1 def outdent(self, step=1): """Outdent by step.""" self._indentation -= step def start_write(self, frame, node=None): """Yield or write into the frame buffer.""" if frame.buffer is None: self.writeline('yield ', node) else: self.writeline('%s.append(' % frame.buffer, node) def end_write(self, frame): """End the writing process started by `start_write`.""" if frame.buffer is not None: self.write(')') def simple_write(self, s, frame, node=None): """Simple shortcut for start_write + write + end_write.""" self.start_write(frame, node) self.write(s) self.end_write(frame) def blockvisit(self, nodes, frame): """Visit a list of nodes as block in a frame. If the current frame is no buffer a dummy ``if 0: yield None`` is written automatically. """ try: self.writeline('pass') for node in nodes: self.visit(node, frame) except CompilerExit: pass def write(self, x): """Write a string into the output stream.""" if self._new_lines: if not self._first_write: self.stream.write('\n' * self._new_lines) self.code_lineno += self._new_lines if self._write_debug_info is not None: self.debug_info.append((self._write_debug_info, self.code_lineno)) self._write_debug_info = None self._first_write = False self.stream.write(' ' * self._indentation) self._new_lines = 0 self.stream.write(x) def writeline(self, x, node=None, extra=0): """Combination of newline and write.""" self.newline(node, extra) self.write(x) def newline(self, node=None, extra=0): """Add one or more newlines before the next write.""" self._new_lines = max(self._new_lines, 1 + extra) if node is not None and node.lineno != self._last_line: self._write_debug_info = node.lineno self._last_line = node.lineno def signature(self, node, frame, extra_kwargs=None): """Writes a function call to the stream for the current node. A leading comma is added automatically. The extra keyword arguments may not include python keywords otherwise a syntax error could occour. The extra keyword arguments should be given as python dict. """ # if any of the given keyword arguments is a python keyword # we have to make sure that no invalid call is created. kwarg_workaround = False for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()): if is_python_keyword(kwarg): kwarg_workaround = True break for arg in node.args: self.write(', ') self.visit(arg, frame) if not kwarg_workaround: for kwarg in node.kwargs: self.write(', ') self.visit(kwarg, frame) if extra_kwargs is not None: for key, value in iteritems(extra_kwargs): self.write(', %s=%s' % (key, value)) if node.dyn_args: self.write(', *') self.visit(node.dyn_args, frame) if kwarg_workaround: if node.dyn_kwargs is not None: self.write(', **dict({') else: self.write(', **{') for kwarg in node.kwargs: self.write('%r: ' % kwarg.key) self.visit(kwarg.value, frame) self.write(', ') if extra_kwargs is not None: for key, value in iteritems(extra_kwargs): self.write('%r: %s, ' % (key, value)) if node.dyn_kwargs is not None: self.write('}, **') self.visit(node.dyn_kwargs, frame) self.write(')') else: self.write('}') elif node.dyn_kwargs is not None: self.write(', **') self.visit(node.dyn_kwargs, frame) def pull_dependencies(self, nodes): """Pull all the dependencies.""" visitor = DependencyFinderVisitor() for node in nodes: visitor.visit(node) for dependency in 'filters', 'tests': mapping = getattr(self, dependency) for name in getattr(visitor, dependency): if name not in mapping: mapping[name] = self.temporary_identifier() self.writeline('%s = environment.%s[%r]' % (mapping[name], dependency, name)) def enter_frame(self, frame): undefs = [] for target, (action, param) in iteritems(frame.symbols.loads): if action == VAR_LOAD_PARAMETER: pass elif action == VAR_LOAD_RESOLVE: self.writeline('%s = %s(%r)' % (target, self.get_resolve_func(), param)) elif action == VAR_LOAD_ALIAS: self.writeline('%s = %s' % (target, param)) elif action == VAR_LOAD_UNDEFINED: undefs.append(target) else: raise NotImplementedError('unknown load instruction') if undefs: self.writeline('%s = missing' % ' = '.join(undefs)) def leave_frame(self, frame, with_python_scope=False): if not with_python_scope: undefs = [] for target, _ in iteritems(frame.symbols.loads): undefs.append(target) if undefs: self.writeline('%s = missing' % ' = '.join(undefs)) def func(self, name): if self.environment.is_async: return 'async def %s' % name return 'def %s' % name def macro_body(self, node, frame): """Dump the function def of a macro or call block.""" frame = frame.inner() frame.symbols.analyze_node(node) macro_ref = MacroRef(node) explicit_caller = None skip_special_params = set() args = [] for idx, arg in enumerate(node.args): if arg.name == 'caller': explicit_caller = idx if arg.name in ('kwargs', 'varargs'): skip_special_params.add(arg.name) args.append(frame.symbols.ref(arg.name)) undeclared = find_undeclared(node.body, ('caller', 'kwargs', 'varargs')) if 'caller' in undeclared: # In older Jinja2 versions there was a bug that allowed caller # to retain the special behavior even if it was mentioned in # the argument list. However thankfully this was only really # working if it was the last argument. So we are explicitly # checking this now and error out if it is anywhere else in # the argument list. if explicit_caller is not None: try: node.defaults[explicit_caller - len(node.args)] except IndexError: self.fail('When defining macros or call blocks the ' 'special "caller" argument must be omitted ' 'or be given a default.', node.lineno) else: args.append(frame.symbols.declare_parameter('caller')) macro_ref.accesses_caller = True if 'kwargs' in undeclared and not 'kwargs' in skip_special_params: args.append(frame.symbols.declare_parameter('kwargs')) macro_ref.accesses_kwargs = True if 'varargs' in undeclared and not 'varargs' in skip_special_params: args.append(frame.symbols.declare_parameter('varargs')) macro_ref.accesses_varargs = True # macros are delayed, they never require output checks frame.require_output_check = False frame.symbols.analyze_node(node) self.writeline('%s(%s):' % (self.func('macro'), ', '.join(args)), node) self.indent() self.buffer(frame) self.enter_frame(frame) self.push_parameter_definitions(frame) for idx, arg in enumerate(node.args): ref = frame.symbols.ref(arg.name) self.writeline('if %s is missing:' % ref) self.indent() try: default = node.defaults[idx - len(node.args)] except IndexError: self.writeline('%s = undefined(%r, name=%r)' % ( ref, 'parameter %r was not provided' % arg.name, arg.name)) else: self.writeline('%s = ' % ref) self.visit(default, frame) self.mark_parameter_stored(ref) self.outdent() self.pop_parameter_definitions() self.blockvisit(node.body, frame) self.return_buffer_contents(frame, force_unescaped=True) self.leave_frame(frame, with_python_scope=True) self.outdent() return frame, macro_ref def macro_def(self, macro_ref, frame): """Dump the macro definition for the def created by macro_body.""" arg_tuple = ', '.join(repr(x.name) for x in macro_ref.node.args) name = getattr(macro_ref.node, 'name', None) if len(macro_ref.node.args) == 1: arg_tuple += ',' self.write('Macro(environment, macro, %r, (%s), %r, %r, %r, ' 'context.eval_ctx.autoescape)' % (name, arg_tuple, macro_ref.accesses_kwargs, macro_ref.accesses_varargs, macro_ref.accesses_caller)) def position(self, node): """Return a human readable position for the node.""" rv = 'line %d' % node.lineno if self.name is not None: rv += ' in ' + repr(self.name) return rv def dump_local_context(self, frame): return '{%s}' % ', '.join( '%r: %s' % (name, target) for name, target in iteritems(frame.symbols.dump_stores())) def write_commons(self): """Writes a common preamble that is used by root and block functions. Primarily this sets up common local helpers and enforces a generator through a dead branch. """ self.writeline('resolve = context.resolve_or_missing') self.writeline('undefined = environment.undefined') self.writeline('if 0: yield None') def push_parameter_definitions(self, frame): """Pushes all parameter targets from the given frame into a local stack that permits tracking of yet to be assigned parameters. In particular this enables the optimization from `visit_Name` to skip undefined expressions for parameters in macros as macros can reference otherwise unbound parameters. """ self._param_def_block.append(frame.symbols.dump_param_targets()) def pop_parameter_definitions(self): """Pops the current parameter definitions set.""" self._param_def_block.pop() def mark_parameter_stored(self, target): """Marks a parameter in the current parameter definitions as stored. This will skip the enforced undefined checks. """ if self._param_def_block: self._param_def_block[-1].discard(target) def push_context_reference(self, target): self._context_reference_stack.append(target) def pop_context_reference(self): self._context_reference_stack.pop() def get_context_ref(self): return self._context_reference_stack[-1] def get_resolve_func(self): target = self._context_reference_stack[-1] if target == 'context': return 'resolve' return '%s.resolve' % target def derive_context(self, frame): return '%s.derived(%s)' % ( self.get_context_ref(), self.dump_local_context(frame), ) def parameter_is_undeclared(self, target): """Checks if a given target is an undeclared parameter.""" if not self._param_def_block: return False return target in self._param_def_block[-1] def push_assign_tracking(self): """Pushes a new layer for assignment tracking.""" self._assign_stack.append(set()) def pop_assign_tracking(self, frame): """Pops the topmost level for assignment tracking and updates the context variables if necessary. """ vars = self._assign_stack.pop() if not frame.toplevel or not vars: return public_names = [x for x in vars if x[:1] != '_'] if len(vars) == 1: name = next(iter(vars)) ref = frame.symbols.ref(name) self.writeline('context.vars[%r] = %s' % (name, ref)) else: self.writeline('context.vars.update({') for idx, name in enumerate(vars): if idx: self.write(', ') ref = frame.symbols.ref(name) self.write('%r: %s' % (name, ref)) self.write('})') if public_names: if len(public_names) == 1: self.writeline('context.exported_vars.add(%r)' % public_names[0]) else: self.writeline('context.exported_vars.update((%s))' % ', '.join(imap(repr, public_names))) # -- Statement Visitors def visit_Template(self, node, frame=None): assert frame is None, 'no root frame allowed' eval_ctx = EvalContext(self.environment, self.name) from jinja2.runtime import __all__ as exported self.writeline('from __future__ import %s' % ', '.join(code_features)) self.writeline('from jinja2.runtime import ' + ', '.join(exported)) if self.environment.is_async: self.writeline('from jinja2.asyncsupport import auto_await, ' 'auto_aiter, make_async_loop_context') # if we want a deferred initialization we cannot move the # environment into a local name envenv = not self.defer_init and ', environment=environment' or '' # do we have an extends tag at all? If not, we can save some # overhead by just not processing any inheritance code. have_extends = node.find(nodes.Extends) is not None # find all blocks for block in node.find_all(nodes.Block): if block.name in self.blocks: self.fail('block %r defined twice' % block.name, block.lineno) self.blocks[block.name] = block # find all imports and import them for import_ in node.find_all(nodes.ImportedName): if import_.importname not in self.import_aliases: imp = import_.importname self.import_aliases[imp] = alias = self.temporary_identifier() if '.' in imp: module, obj = imp.rsplit('.', 1) self.writeline('from %s import %s as %s' % (module, obj, alias)) else: self.writeline('import %s as %s' % (imp, alias)) # add the load name self.writeline('name = %r' % self.name) # generate the root render function. self.writeline('%s(context, missing=missing%s):' % (self.func('root'), envenv), extra=1) self.indent() self.write_commons() # process the root frame = Frame(eval_ctx) if 'self' in find_undeclared(node.body, ('self',)): ref = frame.symbols.declare_parameter('self') self.writeline('%s = TemplateReference(context)' % ref) frame.symbols.analyze_node(node) frame.toplevel = frame.rootlevel = True frame.require_output_check = have_extends and not self.has_known_extends if have_extends: self.writeline('parent_template = None') self.enter_frame(frame) self.pull_dependencies(node.body) self.blockvisit(node.body, frame) self.leave_frame(frame, with_python_scope=True) self.outdent() # make sure that the parent root is called. if have_extends: if not self.has_known_extends: self.indent() self.writeline('if parent_template is not None:') self.indent() if supports_yield_from and not self.environment.is_async: self.writeline('yield from parent_template.' 'root_render_func(context)') else: self.writeline('%sfor event in parent_template.' 'root_render_func(context):' % (self.environment.is_async and 'async ' or '')) self.indent() self.writeline('yield event') self.outdent() self.outdent(1 + (not self.has_known_extends)) # at this point we now have the blocks collected and can visit them too. for name, block in iteritems(self.blocks): self.writeline('%s(context, missing=missing%s):' % (self.func('block_' + name), envenv), block, 1) self.indent() self.write_commons() # It's important that we do not make this frame a child of the # toplevel template. This would cause a variety of # interesting issues with identifier tracking. block_frame = Frame(eval_ctx) undeclared = find_undeclared(block.body, ('self', 'super')) if 'self' in undeclared: ref = block_frame.symbols.declare_parameter('self') self.writeline('%s = TemplateReference(context)' % ref) if 'super' in undeclared: ref = block_frame.symbols.declare_parameter('super') self.writeline('%s = context.super(%r, ' 'block_%s)' % (ref, name, name)) block_frame.symbols.analyze_node(block) block_frame.block = name self.enter_frame(block_frame) self.pull_dependencies(block.body) self.blockvisit(block.body, block_frame) self.leave_frame(block_frame, with_python_scope=True) self.outdent() self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x) for x in self.blocks), extra=1) # add a function that returns the debug info self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x in self.debug_info)) def visit_Block(self, node, frame): """Call a block and register it for the template.""" level = 0 if frame.toplevel: # if we know that we are a child template, there is no need to # check if we are one if self.has_known_extends: return if self.extends_so_far > 0: self.writeline('if parent_template is None:') self.indent() level += 1 if node.scoped: context = self.derive_context(frame) else: context = self.get_context_ref() if supports_yield_from and not self.environment.is_async and \ frame.buffer is None: self.writeline('yield from context.blocks[%r][0](%s)' % ( node.name, context), node) else: loop = self.environment.is_async and 'async for' or 'for' self.writeline('%s event in context.blocks[%r][0](%s):' % ( loop, node.name, context), node) self.indent() self.simple_write('event', frame) self.outdent() self.outdent(level) def visit_Extends(self, node, frame): """Calls the extender.""" if not frame.toplevel: self.fail('cannot use extend from a non top-level scope', node.lineno) # if the number of extends statements in general is zero so # far, we don't have to add a check if something extended # the template before this one. if self.extends_so_far > 0: # if we have a known extends we just add a template runtime # error into the generated code. We could catch that at compile # time too, but i welcome it not to confuse users by throwing the # same error at different times just "because we can". if not self.has_known_extends: self.writeline('if parent_template is not None:') self.indent() self.writeline('raise TemplateRuntimeError(%r)' % 'extended multiple times') # if we have a known extends already we don't need that code here # as we know that the template execution will end here. if self.has_known_extends: raise CompilerExit() else: self.outdent() self.writeline('parent_template = environment.get_template(', node) self.visit(node.template, frame) self.write(', %r)' % self.name) self.writeline('for name, parent_block in parent_template.' 'blocks.%s():' % dict_item_iter) self.indent() self.writeline('context.blocks.setdefault(name, []).' 'append(parent_block)') self.outdent() # if this extends statement was in the root level we can take # advantage of that information and simplify the generated code # in the top level from this point onwards if frame.rootlevel: self.has_known_extends = True # and now we have one more self.extends_so_far += 1 def visit_Include(self, node, frame): """Handles includes.""" if node.ignore_missing: self.writeline('try:') self.indent() func_name = 'get_or_select_template' if isinstance(node.template, nodes.Const): if isinstance(node.template.value, string_types): func_name = 'get_template' elif isinstance(node.template.value, (tuple, list)): func_name = 'select_template' elif isinstance(node.template, (nodes.Tuple, nodes.List)): func_name = 'select_template' self.writeline('template = environment.%s(' % func_name, node) self.visit(node.template, frame) self.write(', %r)' % self.name) if node.ignore_missing: self.outdent() self.writeline('except TemplateNotFound:') self.indent() self.writeline('pass') self.outdent() self.writeline('else:') self.indent() skip_event_yield = False if node.with_context: loop = self.environment.is_async and 'async for' or 'for' self.writeline('%s event in template.root_render_func(' 'template.new_context(context.get_all(), True, ' '%s)):' % (loop, self.dump_local_context(frame))) elif self.environment.is_async: self.writeline('for event in (await ' 'template._get_default_module_async())' '._body_stream:') else: if supports_yield_from: self.writeline('yield from template._get_default_module()' '._body_stream') skip_event_yield = True else: self.writeline('for event in template._get_default_module()' '._body_stream:') if not skip_event_yield: self.indent() self.simple_write('event', frame) self.outdent() if node.ignore_missing: self.outdent() def visit_Import(self, node, frame): """Visit regular imports.""" self.writeline('%s = ' % frame.symbols.ref(node.target), node) if frame.toplevel: self.write('context.vars[%r] = ' % node.target) if self.environment.is_async: self.write('await ') self.write('environment.get_template(') self.visit(node.template, frame) self.write(', %r).' % self.name) if node.with_context: self.write('make_module%s(context.get_all(), True, %s)' % (self.environment.is_async and '_async' or '', self.dump_local_context(frame))) elif self.environment.is_async: self.write('_get_default_module_async()') else: self.write('_get_default_module()') if frame.toplevel and not node.target.startswith('_'): self.writeline('context.exported_vars.discard(%r)' % node.target) def visit_FromImport(self, node, frame): """Visit named imports.""" self.newline(node) self.write('included_template = %senvironment.get_template(' % (self.environment.is_async and 'await ' or '')) self.visit(node.template, frame) self.write(', %r).' % self.name) if node.with_context: self.write('make_module%s(context.get_all(), True, %s)' % (self.environment.is_async and '_async' or '', self.dump_local_context(frame))) elif self.environment.is_async: self.write('_get_default_module_async()') else: self.write('_get_default_module()') var_names = [] discarded_names = [] for name in node.names: if isinstance(name, tuple): name, alias = name else: alias = name self.writeline('%s = getattr(included_template, ' '%r, missing)' % (frame.symbols.ref(alias), name)) self.writeline('if %s is missing:' % frame.symbols.ref(alias)) self.indent() self.writeline('%s = undefined(%r %% ' 'included_template.__name__, ' 'name=%r)' % (frame.symbols.ref(alias), 'the template %%r (imported on %s) does ' 'not export the requested name %s' % ( self.position(node), repr(name) ), name)) self.outdent() if frame.toplevel: var_names.append(alias) if not alias.startswith('_'): discarded_names.append(alias) if var_names: if len(var_names) == 1: name = var_names[0] self.writeline('context.vars[%r] = %s' % (name, frame.symbols.ref(name))) else: self.writeline('context.vars.update({%s})' % ', '.join( '%r: %s' % (name, frame.symbols.ref(name)) for name in var_names )) if discarded_names: if len(discarded_names) == 1: self.writeline('context.exported_vars.discard(%r)' % discarded_names[0]) else: self.writeline('context.exported_vars.difference_' 'update((%s))' % ', '.join(imap(repr, discarded_names))) def visit_For(self, node, frame): loop_frame = frame.inner() test_frame = frame.inner() else_frame = frame.inner() # try to figure out if we have an extended loop. An extended loop # is necessary if the loop is in recursive mode if the special loop # variable is accessed in the body. extended_loop = node.recursive or 'loop' in \ find_undeclared(node.iter_child_nodes( only=('body',)), ('loop',)) loop_ref = None if extended_loop: loop_ref = loop_frame.symbols.declare_parameter('loop') loop_frame.symbols.analyze_node(node, for_branch='body') if node.else_: else_frame.symbols.analyze_node(node, for_branch='else') if node.test: loop_filter_func = self.temporary_identifier() test_frame.symbols.analyze_node(node, for_branch='test') self.writeline('%s(fiter):' % self.func(loop_filter_func), node.test) self.indent() self.enter_frame(test_frame) self.writeline(self.environment.is_async and 'async for ' or 'for ') self.visit(node.target, loop_frame) self.write(' in ') self.write(self.environment.is_async and 'auto_aiter(fiter)' or 'fiter') self.write(':') self.indent() self.writeline('if ', node.test) self.visit(node.test, test_frame) self.write(':') self.indent() self.writeline('yield ') self.visit(node.target, loop_frame) self.outdent(3) self.leave_frame(test_frame, with_python_scope=True) # if we don't have an recursive loop we have to find the shadowed # variables at that point. Because loops can be nested but the loop # variable is a special one we have to enforce aliasing for it. if node.recursive: self.writeline('%s(reciter, loop_render_func, depth=0):' % self.func('loop'), node) self.indent() self.buffer(loop_frame) # Use the same buffer for the else frame else_frame.buffer = loop_frame.buffer # make sure the loop variable is a special one and raise a template # assertion error if a loop tries to write to loop if extended_loop: self.writeline('%s = missing' % loop_ref) for name in node.find_all(nodes.Name): if name.ctx == 'store' and name.name == 'loop': self.fail('Can\'t assign to special loop variable ' 'in for-loop target', name.lineno) if node.else_: iteration_indicator = self.temporary_identifier() self.writeline('%s = 1' % iteration_indicator) self.writeline(self.environment.is_async and 'async for ' or 'for ', node) self.visit(node.target, loop_frame) if extended_loop: if self.environment.is_async: self.write(', %s in await make_async_loop_context(' % loop_ref) else: self.write(', %s in LoopContext(' % loop_ref) else: self.write(' in ') if node.test: self.write('%s(' % loop_filter_func) if node.recursive: self.write('reciter') else: if self.environment.is_async and not extended_loop: self.write('auto_aiter(') self.visit(node.iter, frame) if self.environment.is_async and not extended_loop: self.write(')') if node.test: self.write(')') if node.recursive: self.write(', undefined, loop_render_func, depth):') else: self.write(extended_loop and ', undefined):' or ':') self.indent() self.enter_frame(loop_frame) self.blockvisit(node.body, loop_frame) if node.else_: self.writeline('%s = 0' % iteration_indicator) self.outdent() self.leave_frame(loop_frame, with_python_scope=node.recursive and not node.else_) if node.else_: self.writeline('if %s:' % iteration_indicator) self.indent() self.enter_frame(else_frame) self.blockvisit(node.else_, else_frame) self.leave_frame(else_frame) self.outdent() # if the node was recursive we have to return the buffer contents # and start the iteration code if node.recursive: self.return_buffer_contents(loop_frame) self.outdent() self.start_write(frame, node) if self.environment.is_async: self.write('await ') self.write('loop(') if self.environment.is_async: self.write('auto_aiter(') self.visit(node.iter, frame) if self.environment.is_async: self.write(')') self.write(', loop)') self.end_write(frame) def visit_If(self, node, frame): if_frame = frame.soft() self.writeline('if ', node) self.visit(node.test, if_frame) self.write(':') self.indent() self.blockvisit(node.body, if_frame) self.outdent() for elif_ in node.elif_: self.writeline('elif ', elif_) self.visit(elif_.test, if_frame) self.write(':') self.indent() self.blockvisit(elif_.body, if_frame) self.outdent() if node.else_: self.writeline('else:') self.indent() self.blockvisit(node.else_, if_frame) self.outdent() def visit_Macro(self, node, frame): macro_frame, macro_ref = self.macro_body(node, frame) self.newline() if frame.toplevel: if not node.name.startswith('_'): self.write('context.exported_vars.add(%r)' % node.name) ref = frame.symbols.ref(node.name) self.writeline('context.vars[%r] = ' % node.name) self.write('%s = ' % frame.symbols.ref(node.name)) self.macro_def(macro_ref, macro_frame) def visit_CallBlock(self, node, frame): call_frame, macro_ref = self.macro_body(node, frame) self.writeline('caller = ') self.macro_def(macro_ref, call_frame) self.start_write(frame, node) self.visit_Call(node.call, frame, forward_caller=True) self.end_write(frame) def visit_FilterBlock(self, node, frame): filter_frame = frame.inner() filter_frame.symbols.analyze_node(node) self.enter_frame(filter_frame) self.buffer(filter_frame) self.blockvisit(node.body, filter_frame) self.start_write(frame, node) self.visit_Filter(node.filter, filter_frame) self.end_write(frame) self.leave_frame(filter_frame) def visit_With(self, node, frame): with_frame = frame.inner() with_frame.symbols.analyze_node(node) self.enter_frame(with_frame) for idx, (target, expr) in enumerate(izip(node.targets, node.values)): self.newline() self.visit(target, with_frame) self.write(' = ') self.visit(expr, frame) self.blockvisit(node.body, with_frame) self.leave_frame(with_frame) def visit_ExprStmt(self, node, frame): self.newline(node) self.visit(node.node, frame) def visit_Output(self, node, frame): # if we have a known extends statement, we don't output anything # if we are in a require_output_check section if self.has_known_extends and frame.require_output_check: return allow_constant_finalize = True if self.environment.finalize: func = self.environment.finalize if getattr(func, 'contextfunction', False) or \ getattr(func, 'evalcontextfunction', False): allow_constant_finalize = False elif getattr(func, 'environmentfunction', False): finalize = lambda x: text_type( self.environment.finalize(self.environment, x)) else: finalize = lambda x: text_type(self.environment.finalize(x)) else: finalize = text_type # if we are inside a frame that requires output checking, we do so outdent_later = False if frame.require_output_check: self.writeline('if parent_template is None:') self.indent() outdent_later = True # try to evaluate as many chunks as possible into a static # string at compile time. body = [] for child in node.nodes: try: if not allow_constant_finalize: raise nodes.Impossible() const = child.as_const(frame.eval_ctx) except nodes.Impossible: body.append(child) continue # the frame can't be volatile here, becaus otherwise the # as_const() function would raise an Impossible exception # at that point. try: if frame.eval_ctx.autoescape: if hasattr(const, '__html__'): const = const.__html__() else: const = escape(const) const = finalize(const) except Exception: # if something goes wrong here we evaluate the node # at runtime for easier debugging body.append(child) continue if body and isinstance(body[-1], list): body[-1].append(const) else: body.append([const]) # if we have less than 3 nodes or a buffer we yield or extend/append if len(body) < 3 or frame.buffer is not None: if frame.buffer is not None: # for one item we append, for more we extend if len(body) == 1: self.writeline('%s.append(' % frame.buffer) else: self.writeline('%s.extend((' % frame.buffer) self.indent() for item in body: if isinstance(item, list): val = repr(concat(item)) if frame.buffer is None: self.writeline('yield ' + val) else: self.writeline(val + ',') else: if frame.buffer is None: self.writeline('yield ', item) else: self.newline(item) close = 1 if frame.eval_ctx.volatile: self.write('(escape if context.eval_ctx.autoescape' ' else to_string)(') elif frame.eval_ctx.autoescape: self.write('escape(') else: self.write('to_string(') if self.environment.finalize is not None: self.write('environment.finalize(') if getattr(self.environment.finalize, "contextfunction", False): self.write('context, ') close += 1 self.visit(item, frame) self.write(')' * close) if frame.buffer is not None: self.write(',') if frame.buffer is not None: # close the open parentheses self.outdent() self.writeline(len(body) == 1 and ')' or '))') # otherwise we create a format string as this is faster in that case else: format = [] arguments = [] for item in body: if isinstance(item, list): format.append(concat(item).replace('%', '%%')) else: format.append('%s') arguments.append(item) self.writeline('yield ') self.write(repr(concat(format)) + ' % (') self.indent() for argument in arguments: self.newline(argument) close = 0 if frame.eval_ctx.volatile: self.write('(escape if context.eval_ctx.autoescape else' ' to_string)(') close += 1 elif frame.eval_ctx.autoescape: self.write('escape(') close += 1 if self.environment.finalize is not None: self.write('environment.finalize(') if getattr(self.environment.finalize, 'contextfunction', False): self.write('context, ') elif getattr(self.environment.finalize, 'evalcontextfunction', False): self.write('context.eval_ctx, ') elif getattr(self.environment.finalize, 'environmentfunction', False): self.write('environment, ') close += 1 self.visit(argument, frame) self.write(')' * close + ', ') self.outdent() self.writeline(')') if outdent_later: self.outdent() def visit_Assign(self, node, frame): self.push_assign_tracking() self.newline(node) self.visit(node.target, frame) self.write(' = ') self.visit(node.node, frame) self.pop_assign_tracking(frame) def visit_AssignBlock(self, node, frame): self.push_assign_tracking() block_frame = frame.inner() # This is a special case. Since a set block always captures we # will disable output checks. This way one can use set blocks # toplevel even in extended templates. block_frame.require_output_check = False block_frame.symbols.analyze_node(node) self.enter_frame(block_frame) self.buffer(block_frame) self.blockvisit(node.body, block_frame) self.newline(node) self.visit(node.target, frame) self.write(' = (Markup if context.eval_ctx.autoescape ' 'else identity)(') if node.filter is not None: self.visit_Filter(node.filter, block_frame) else: self.write('concat(%s)' % block_frame.buffer) self.write(')') self.pop_assign_tracking(frame) self.leave_frame(block_frame) # -- Expression Visitors def visit_Name(self, node, frame): if node.ctx == 'store' and frame.toplevel: if self._assign_stack: self._assign_stack[-1].add(node.name) ref = frame.symbols.ref(node.name) # If we are looking up a variable we might have to deal with the # case where it's undefined. We can skip that case if the load # instruction indicates a parameter which are always defined. if node.ctx == 'load': load = frame.symbols.find_load(ref) if not (load is not None and load[0] == VAR_LOAD_PARAMETER and \ not self.parameter_is_undeclared(ref)): self.write('(undefined(name=%r) if %s is missing else %s)' % (node.name, ref, ref)) return self.write(ref) def visit_NSRef(self, node, frame): # NSRefs can only be used to store values; since they use the normal # `foo.bar` notation they will be parsed as a normal attribute access # when used anywhere but in a `set` context ref = frame.symbols.ref(node.name) self.writeline('if not isinstance(%s, Namespace):' % ref) self.indent() self.writeline('raise TemplateRuntimeError(%r)' % 'cannot assign attribute on non-namespace object') self.outdent() self.writeline('%s[%r]' % (ref, node.attr)) def visit_Const(self, node, frame): val = node.as_const(frame.eval_ctx) if isinstance(val, float): self.write(str(val)) else: self.write(repr(val)) def visit_TemplateData(self, node, frame): try: self.write(repr(node.as_const(frame.eval_ctx))) except nodes.Impossible: self.write('(Markup if context.eval_ctx.autoescape else identity)(%r)' % node.data) def visit_Tuple(self, node, frame): self.write('(') idx = -1 for idx, item in enumerate(node.items): if idx: self.write(', ') self.visit(item, frame) self.write(idx == 0 and ',)' or ')') def visit_List(self, node, frame): self.write('[') for idx, item in enumerate(node.items): if idx: self.write(', ') self.visit(item, frame) self.write(']') def visit_Dict(self, node, frame): self.write('{') for idx, item in enumerate(node.items): if idx: self.write(', ') self.visit(item.key, frame) self.write(': ') self.visit(item.value, frame) self.write('}') def binop(operator, interceptable=True): @optimizeconst def visitor(self, node, frame): if self.environment.sandboxed and \ operator in self.environment.intercepted_binops: self.write('environment.call_binop(context, %r, ' % operator) self.visit(node.left, frame) self.write(', ') self.visit(node.right, frame) else: self.write('(') self.visit(node.left, frame) self.write(' %s ' % operator) self.visit(node.right, frame) self.write(')') return visitor def uaop(operator, interceptable=True): @optimizeconst def visitor(self, node, frame): if self.environment.sandboxed and \ operator in self.environment.intercepted_unops: self.write('environment.call_unop(context, %r, ' % operator) self.visit(node.node, frame) else: self.write('(' + operator) self.visit(node.node, frame) self.write(')') return visitor visit_Add = binop('+') visit_Sub = binop('-') visit_Mul = binop('*') visit_Div = binop('/') visit_FloorDiv = binop('//') visit_Pow = binop('**') visit_Mod = binop('%') visit_And = binop('and', interceptable=False) visit_Or = binop('or', interceptable=False) visit_Pos = uaop('+') visit_Neg = uaop('-') visit_Not = uaop('not ', interceptable=False) del binop, uaop @optimizeconst def visit_Concat(self, node, frame): if frame.eval_ctx.volatile: func_name = '(context.eval_ctx.volatile and' \ ' markup_join or unicode_join)' elif frame.eval_ctx.autoescape: func_name = 'markup_join' else: func_name = 'unicode_join' self.write('%s((' % func_name) for arg in node.nodes: self.visit(arg, frame) self.write(', ') self.write('))') @optimizeconst def visit_Compare(self, node, frame): self.visit(node.expr, frame) for op in node.ops: self.visit(op, frame) def visit_Operand(self, node, frame): self.write(' %s ' % operators[node.op]) self.visit(node.expr, frame) @optimizeconst def visit_Getattr(self, node, frame): self.write('environment.getattr(') self.visit(node.node, frame) self.write(', %r)' % node.attr) @optimizeconst def visit_Getitem(self, node, frame): # slices bypass the environment getitem method. if isinstance(node.arg, nodes.Slice): self.visit(node.node, frame) self.write('[') self.visit(node.arg, frame) self.write(']') else: self.write('environment.getitem(') self.visit(node.node, frame) self.write(', ') self.visit(node.arg, frame) self.write(')') def visit_Slice(self, node, frame): if node.start is not None: self.visit(node.start, frame) self.write(':') if node.stop is not None: self.visit(node.stop, frame) if node.step is not None: self.write(':') self.visit(node.step, frame) @optimizeconst def visit_Filter(self, node, frame): if self.environment.is_async: self.write('await auto_await(') self.write(self.filters[node.name] + '(') func = self.environment.filters.get(node.name) if func is None: self.fail('no filter named %r' % node.name, node.lineno) if getattr(func, 'contextfilter', False): self.write('context, ') elif getattr(func, 'evalcontextfilter', False): self.write('context.eval_ctx, ') elif getattr(func, 'environmentfilter', False): self.write('environment, ') # if the filter node is None we are inside a filter block # and want to write to the current buffer if node.node is not None: self.visit(node.node, frame) elif frame.eval_ctx.volatile: self.write('(context.eval_ctx.autoescape and' ' Markup(concat(%s)) or concat(%s))' % (frame.buffer, frame.buffer)) elif frame.eval_ctx.autoescape: self.write('Markup(concat(%s))' % frame.buffer) else: self.write('concat(%s)' % frame.buffer) self.signature(node, frame) self.write(')') if self.environment.is_async: self.write(')') @optimizeconst def visit_Test(self, node, frame): self.write(self.tests[node.name] + '(') if node.name not in self.environment.tests: self.fail('no test named %r' % node.name, node.lineno) self.visit(node.node, frame) self.signature(node, frame) self.write(')') @optimizeconst def visit_CondExpr(self, node, frame): def write_expr2(): if node.expr2 is not None: return self.visit(node.expr2, frame) self.write('undefined(%r)' % ('the inline if-' 'expression on %s evaluated to false and ' 'no else section was defined.' % self.position(node))) self.write('(') self.visit(node.expr1, frame) self.write(' if ') self.visit(node.test, frame) self.write(' else ') write_expr2() self.write(')') @optimizeconst def visit_Call(self, node, frame, forward_caller=False): if self.environment.is_async: self.write('await auto_await(') if self.environment.sandboxed: self.write('environment.call(context, ') else: self.write('context.call(') self.visit(node.node, frame) extra_kwargs = forward_caller and {'caller': 'caller'} or None self.signature(node, frame, extra_kwargs) self.write(')') if self.environment.is_async: self.write(')') def visit_Keyword(self, node, frame): self.write(node.key + '=') self.visit(node.value, frame) # -- Unused nodes for extensions def visit_MarkSafe(self, node, frame): self.write('Markup(') self.visit(node.expr, frame) self.write(')') def visit_MarkSafeIfAutoescape(self, node, frame): self.write('(context.eval_ctx.autoescape and Markup or identity)(') self.visit(node.expr, frame) self.write(')') def visit_EnvironmentAttribute(self, node, frame): self.write('environment.' + node.name) def visit_ExtensionAttribute(self, node, frame): self.write('environment.extensions[%r].%s' % (node.identifier, node.name)) def visit_ImportedName(self, node, frame): self.write(self.import_aliases[node.importname]) def visit_InternalName(self, node, frame): self.write(node.name) def visit_ContextReference(self, node, frame): self.write('context') def visit_Continue(self, node, frame): self.writeline('continue', node) def visit_Break(self, node, frame): self.writeline('break', node) def visit_Scope(self, node, frame): scope_frame = frame.inner() scope_frame.symbols.analyze_node(node) self.enter_frame(scope_frame) self.blockvisit(node.body, scope_frame) self.leave_frame(scope_frame) def visit_OverlayScope(self, node, frame): ctx = self.temporary_identifier() self.writeline('%s = %s' % (ctx, self.derive_context(frame))) self.writeline('%s.vars = ' % ctx) self.visit(node.context, frame) self.push_context_reference(ctx) scope_frame = frame.inner(isolated=True) scope_frame.symbols.analyze_node(node) self.enter_frame(scope_frame) self.blockvisit(node.body, scope_frame) self.leave_frame(scope_frame) self.pop_context_reference() def visit_EvalContextModifier(self, node, frame): for keyword in node.options: self.writeline('context.eval_ctx.%s = ' % keyword.key) self.visit(keyword.value, frame) try: val = keyword.value.as_const(frame.eval_ctx) except nodes.Impossible: frame.eval_ctx.volatile = True else: setattr(frame.eval_ctx, keyword.key, val) def visit_ScopedEvalContextModifier(self, node, frame): old_ctx_name = self.temporary_identifier() saved_ctx = frame.eval_ctx.save() self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) self.visit_EvalContextModifier(node, frame) for child in node.body: self.visit(child, frame) frame.eval_ctx.revert(saved_ctx) self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) Jinja2-2.10/jinja2/environment.py0000644000175000017500000014324113176160444017057 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- """ jinja2.environment ~~~~~~~~~~~~~~~~~~ Provides a class that holds runtime and parsing time options. :copyright: (c) 2017 by the Jinja Team. :license: BSD, see LICENSE for more details. """ import os import sys import weakref from functools import reduce, partial from jinja2 import nodes from jinja2.defaults import BLOCK_START_STRING, \ BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \ DEFAULT_POLICIES, KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS from jinja2.lexer import get_lexer, TokenStream from jinja2.parser import Parser from jinja2.nodes import EvalContext from jinja2.compiler import generate, CodeGenerator from jinja2.runtime import Undefined, new_context, Context from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \ TemplatesNotFound, TemplateRuntimeError from jinja2.utils import import_string, LRUCache, Markup, missing, \ concat, consume, internalcode, have_async_gen from jinja2._compat import imap, ifilter, string_types, iteritems, \ text_type, reraise, implements_iterator, implements_to_string, \ encode_filename, PY2, PYPY # for direct template usage we have up to ten living environments _spontaneous_environments = LRUCache(10) # the function to create jinja traceback objects. This is dynamically # imported on the first exception in the exception handler. _make_traceback = None def get_spontaneous_environment(*args): """Return a new spontaneous environment. A spontaneous environment is an unnamed and unaccessible (in theory) environment that is used for templates generated from a string and not from the file system. """ try: env = _spontaneous_environments.get(args) except TypeError: return Environment(*args) if env is not None: return env _spontaneous_environments[args] = env = Environment(*args) env.shared = True return env def create_cache(size): """Return the cache class for the given size.""" if size == 0: return None if size < 0: return {} return LRUCache(size) def copy_cache(cache): """Create an empty copy of the given cache.""" if cache is None: return None elif type(cache) is dict: return {} return LRUCache(cache.capacity) def load_extensions(environment, extensions): """Load the extensions from the list and bind it to the environment. Returns a dict of instantiated environments. """ result = {} for extension in extensions: if isinstance(extension, string_types): extension = import_string(extension) result[extension.identifier] = extension(environment) return result def fail_for_missing_callable(string, name): msg = string % name if isinstance(name, Undefined): try: name._fail_with_undefined_error() except Exception as e: msg = '%s (%s; did you forget to quote the callable name?)' % (msg, e) raise TemplateRuntimeError(msg) def _environment_sanity_check(environment): """Perform a sanity check on the environment.""" assert issubclass(environment.undefined, Undefined), 'undefined must ' \ 'be a subclass of undefined because filters depend on it.' assert environment.block_start_string != \ environment.variable_start_string != \ environment.comment_start_string, 'block, variable and comment ' \ 'start strings must be different' assert environment.newline_sequence in ('\r', '\r\n', '\n'), \ 'newline_sequence set to unknown line ending string.' return environment class Environment(object): r"""The core component of Jinja is the `Environment`. It contains important shared variables like configuration, filters, tests, globals and others. Instances of this class may be modified if they are not shared and if no template was loaded so far. Modifications on environments after the first template was loaded will lead to surprising effects and undefined behavior. Here are the possible initialization parameters: `block_start_string` The string marking the beginning of a block. Defaults to ``'{%'``. `block_end_string` The string marking the end of a block. Defaults to ``'%}'``. `variable_start_string` The string marking the beginning of a print statement. Defaults to ``'{{'``. `variable_end_string` The string marking the end of a print statement. Defaults to ``'}}'``. `comment_start_string` The string marking the beginning of a comment. Defaults to ``'{#'``. `comment_end_string` The string marking the end of a comment. Defaults to ``'#}'``. `line_statement_prefix` If given and a string, this will be used as prefix for line based statements. See also :ref:`line-statements`. `line_comment_prefix` If given and a string, this will be used as prefix for line based comments. See also :ref:`line-statements`. .. versionadded:: 2.2 `trim_blocks` If this is set to ``True`` the first newline after a block is removed (block, not variable tag!). Defaults to `False`. `lstrip_blocks` If this is set to ``True`` leading spaces and tabs are stripped from the start of a line to a block. Defaults to `False`. `newline_sequence` The sequence that starts a newline. Must be one of ``'\r'``, ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a useful default for Linux and OS X systems as well as web applications. `keep_trailing_newline` Preserve the trailing newline when rendering templates. The default is ``False``, which causes a single newline, if present, to be stripped from the end of the template. .. versionadded:: 2.7 `extensions` List of Jinja extensions to use. This can either be import paths as strings or extension classes. For more information have a look at :ref:`the extensions documentation `. `optimized` should the optimizer be enabled? Default is ``True``. `undefined` :class:`Undefined` or a subclass of it that is used to represent undefined values in the template. `finalize` A callable that can be used to process the result of a variable expression before it is output. For example one can convert ``None`` implicitly into an empty string here. `autoescape` If set to ``True`` the XML/HTML autoescaping feature is enabled by default. For more details about autoescaping see :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also be a callable that is passed the template name and has to return ``True`` or ``False`` depending on autoescape should be enabled by default. .. versionchanged:: 2.4 `autoescape` can now be a function `loader` The template loader for this environment. `cache_size` The size of the cache. Per default this is ``400`` which means that if more than 400 templates are loaded the loader will clean out the least recently used template. If the cache size is set to ``0`` templates are recompiled all the time, if the cache size is ``-1`` the cache will not be cleaned. .. versionchanged:: 2.8 The cache size was increased to 400 from a low 50. `auto_reload` Some loaders load templates from locations where the template sources may change (ie: file system or database). If ``auto_reload`` is set to ``True`` (default) every time a template is requested the loader checks if the source changed and if yes, it will reload the template. For higher performance it's possible to disable that. `bytecode_cache` If set to a bytecode cache object, this object will provide a cache for the internal Jinja bytecode so that templates don't have to be parsed if they were not changed. See :ref:`bytecode-cache` for more information. `enable_async` If set to true this enables async template execution which allows you to take advantage of newer Python features. This requires Python 3.6 or later. """ #: if this environment is sandboxed. Modifying this variable won't make #: the environment sandboxed though. For a real sandboxed environment #: have a look at jinja2.sandbox. This flag alone controls the code #: generation by the compiler. sandboxed = False #: True if the environment is just an overlay overlayed = False #: the environment this environment is linked to if it is an overlay linked_to = None #: shared environments have this set to `True`. A shared environment #: must not be modified shared = False #: these are currently EXPERIMENTAL undocumented features. exception_handler = None exception_formatter = None #: the class that is used for code generation. See #: :class:`~jinja2.compiler.CodeGenerator` for more information. code_generator_class = CodeGenerator #: the context class thatis used for templates. See #: :class:`~jinja2.runtime.Context` for more information. context_class = Context def __init__(self, block_start_string=BLOCK_START_STRING, block_end_string=BLOCK_END_STRING, variable_start_string=VARIABLE_START_STRING, variable_end_string=VARIABLE_END_STRING, comment_start_string=COMMENT_START_STRING, comment_end_string=COMMENT_END_STRING, line_statement_prefix=LINE_STATEMENT_PREFIX, line_comment_prefix=LINE_COMMENT_PREFIX, trim_blocks=TRIM_BLOCKS, lstrip_blocks=LSTRIP_BLOCKS, newline_sequence=NEWLINE_SEQUENCE, keep_trailing_newline=KEEP_TRAILING_NEWLINE, extensions=(), optimized=True, undefined=Undefined, finalize=None, autoescape=False, loader=None, cache_size=400, auto_reload=True, bytecode_cache=None, enable_async=False): # !!Important notice!! # The constructor accepts quite a few arguments that should be # passed by keyword rather than position. However it's important to # not change the order of arguments because it's used at least # internally in those cases: # - spontaneous environments (i18n extension and Template) # - unittests # If parameter changes are required only add parameters at the end # and don't change the arguments (or the defaults!) of the arguments # existing already. # lexer / parser information self.block_start_string = block_start_string self.block_end_string = block_end_string self.variable_start_string = variable_start_string self.variable_end_string = variable_end_string self.comment_start_string = comment_start_string self.comment_end_string = comment_end_string self.line_statement_prefix = line_statement_prefix self.line_comment_prefix = line_comment_prefix self.trim_blocks = trim_blocks self.lstrip_blocks = lstrip_blocks self.newline_sequence = newline_sequence self.keep_trailing_newline = keep_trailing_newline # runtime information self.undefined = undefined self.optimized = optimized self.finalize = finalize self.autoescape = autoescape # defaults self.filters = DEFAULT_FILTERS.copy() self.tests = DEFAULT_TESTS.copy() self.globals = DEFAULT_NAMESPACE.copy() # set the loader provided self.loader = loader self.cache = create_cache(cache_size) self.bytecode_cache = bytecode_cache self.auto_reload = auto_reload # configurable policies self.policies = DEFAULT_POLICIES.copy() # load extensions self.extensions = load_extensions(self, extensions) self.enable_async = enable_async self.is_async = self.enable_async and have_async_gen _environment_sanity_check(self) def add_extension(self, extension): """Adds an extension after the environment was created. .. versionadded:: 2.5 """ self.extensions.update(load_extensions(self, [extension])) def extend(self, **attributes): """Add the items to the instance of the environment if they do not exist yet. This is used by :ref:`extensions ` to register callbacks and configuration values without breaking inheritance. """ for key, value in iteritems(attributes): if not hasattr(self, key): setattr(self, key, value) def overlay(self, block_start_string=missing, block_end_string=missing, variable_start_string=missing, variable_end_string=missing, comment_start_string=missing, comment_end_string=missing, line_statement_prefix=missing, line_comment_prefix=missing, trim_blocks=missing, lstrip_blocks=missing, extensions=missing, optimized=missing, undefined=missing, finalize=missing, autoescape=missing, loader=missing, cache_size=missing, auto_reload=missing, bytecode_cache=missing): """Create a new overlay environment that shares all the data with the current environment except for cache and the overridden attributes. Extensions cannot be removed for an overlayed environment. An overlayed environment automatically gets all the extensions of the environment it is linked to plus optional extra extensions. Creating overlays should happen after the initial environment was set up completely. Not all attributes are truly linked, some are just copied over so modifications on the original environment may not shine through. """ args = dict(locals()) del args['self'], args['cache_size'], args['extensions'] rv = object.__new__(self.__class__) rv.__dict__.update(self.__dict__) rv.overlayed = True rv.linked_to = self for key, value in iteritems(args): if value is not missing: setattr(rv, key, value) if cache_size is not missing: rv.cache = create_cache(cache_size) else: rv.cache = copy_cache(self.cache) rv.extensions = {} for key, value in iteritems(self.extensions): rv.extensions[key] = value.bind(rv) if extensions is not missing: rv.extensions.update(load_extensions(rv, extensions)) return _environment_sanity_check(rv) lexer = property(get_lexer, doc="The lexer for this environment.") def iter_extensions(self): """Iterates over the extensions by priority.""" return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) def getitem(self, obj, argument): """Get an item or attribute of an object but prefer the item.""" try: return obj[argument] except (AttributeError, TypeError, LookupError): if isinstance(argument, string_types): try: attr = str(argument) except Exception: pass else: try: return getattr(obj, attr) except AttributeError: pass return self.undefined(obj=obj, name=argument) def getattr(self, obj, attribute): """Get an item or attribute of an object but prefer the attribute. Unlike :meth:`getitem` the attribute *must* be a bytestring. """ try: return getattr(obj, attribute) except AttributeError: pass try: return obj[attribute] except (TypeError, LookupError, AttributeError): return self.undefined(obj=obj, name=attribute) def call_filter(self, name, value, args=None, kwargs=None, context=None, eval_ctx=None): """Invokes a filter on a value the same way the compiler does it. Note that on Python 3 this might return a coroutine in case the filter is running from an environment in async mode and the filter supports async execution. It's your responsibility to await this if needed. .. versionadded:: 2.7 """ func = self.filters.get(name) if func is None: fail_for_missing_callable('no filter named %r', name) args = [value] + list(args or ()) if getattr(func, 'contextfilter', False): if context is None: raise TemplateRuntimeError('Attempted to invoke context ' 'filter without context') args.insert(0, context) elif getattr(func, 'evalcontextfilter', False): if eval_ctx is None: if context is not None: eval_ctx = context.eval_ctx else: eval_ctx = EvalContext(self) args.insert(0, eval_ctx) elif getattr(func, 'environmentfilter', False): args.insert(0, self) return func(*args, **(kwargs or {})) def call_test(self, name, value, args=None, kwargs=None): """Invokes a test on a value the same way the compiler does it. .. versionadded:: 2.7 """ func = self.tests.get(name) if func is None: fail_for_missing_callable('no test named %r', name) return func(value, *(args or ()), **(kwargs or {})) @internalcode def parse(self, source, name=None, filename=None): """Parse the sourcecode and return the abstract syntax tree. This tree of nodes is used by the compiler to convert the template into executable source- or bytecode. This is useful for debugging or to extract information from templates. If you are :ref:`developing Jinja2 extensions ` this gives you a good overview of the node tree generated. """ try: return self._parse(source, name, filename) except TemplateSyntaxError: exc_info = sys.exc_info() self.handle_exception(exc_info, source_hint=source) def _parse(self, source, name, filename): """Internal parsing function used by `parse` and `compile`.""" return Parser(self, source, name, encode_filename(filename)).parse() def lex(self, source, name=None, filename=None): """Lex the given sourcecode and return a generator that yields tokens as tuples in the form ``(lineno, token_type, value)``. This can be useful for :ref:`extension development ` and debugging templates. This does not perform preprocessing. If you want the preprocessing of the extensions to be applied you have to filter source through the :meth:`preprocess` method. """ source = text_type(source) try: return self.lexer.tokeniter(source, name, filename) except TemplateSyntaxError: exc_info = sys.exc_info() self.handle_exception(exc_info, source_hint=source) def preprocess(self, source, name=None, filename=None): """Preprocesses the source with all extensions. This is automatically called for all parsing and compiling methods but *not* for :meth:`lex` because there you usually only want the actual source tokenized. """ return reduce(lambda s, e: e.preprocess(s, name, filename), self.iter_extensions(), text_type(source)) def _tokenize(self, source, name, filename=None, state=None): """Called by the parser to do the preprocessing and filtering for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. """ source = self.preprocess(source, name, filename) stream = self.lexer.tokenize(source, name, filename, state) for ext in self.iter_extensions(): stream = ext.filter_stream(stream) if not isinstance(stream, TokenStream): stream = TokenStream(stream, name, filename) return stream def _generate(self, source, name, filename, defer_init=False): """Internal hook that can be overridden to hook a different generate method in. .. versionadded:: 2.5 """ return generate(source, self, name, filename, defer_init=defer_init, optimized=self.optimized) def _compile(self, source, filename): """Internal hook that can be overridden to hook a different compile method in. .. versionadded:: 2.5 """ return compile(source, filename, 'exec') @internalcode def compile(self, source, name=None, filename=None, raw=False, defer_init=False): """Compile a node or template source code. The `name` parameter is the load name of the template after it was joined using :meth:`join_path` if necessary, not the filename on the file system. the `filename` parameter is the estimated filename of the template on the file system. If the template came from a database or memory this can be omitted. The return value of this method is a python code object. If the `raw` parameter is `True` the return value will be a string with python code equivalent to the bytecode returned otherwise. This method is mainly used internally. `defer_init` is use internally to aid the module code generator. This causes the generated code to be able to import without the global environment variable to be set. .. versionadded:: 2.4 `defer_init` parameter added. """ source_hint = None try: if isinstance(source, string_types): source_hint = source source = self._parse(source, name, filename) source = self._generate(source, name, filename, defer_init=defer_init) if raw: return source if filename is None: filename = '