Binary files /tmp/bkWsBeMhST/jinja2-2.7.3/artwork/.DS_Store and /tmp/iqtS1tb7Pe/jinja2-2.8/artwork/.DS_Store differ diff -Nru jinja2-2.7.3/CHANGES jinja2-2.8/CHANGES --- jinja2-2.7.3/CHANGES 2014-06-06 16:49:01.000000000 +0000 +++ jinja2-2.8/CHANGES 2015-07-26 17:48:11.000000000 +0000 @@ -1,6 +1,40 @@ Jinja2 Changelog ================ +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 incrased 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) diff -Nru jinja2-2.7.3/debian/changelog jinja2-2.8/debian/changelog --- jinja2-2.7.3/debian/changelog 2014-06-10 20:26:42.000000000 +0000 +++ jinja2-2.8/debian/changelog 2015-08-01 12:29:32.000000000 +0000 @@ -1,3 +1,13 @@ +jinja2 (2.8-1) unstable; urgency=medium + + * New upstream release + - drop jinja2-typos and drop_next_import_from_docs-jinjaext patches - both + applied upstream + * rewrite debian/rules to dh sequencer and pybuild buildsystem + * Standards-version bumped to 3.9.6 (no other changes needed) + + -- Piotr Ożarowski Sat, 01 Aug 2015 14:15:36 +0200 + jinja2 (2.7.3-1) unstable; urgency=medium * New upstream release diff -Nru jinja2-2.7.3/debian/control jinja2-2.8/debian/control --- jinja2-2.7.3/debian/control 2014-06-10 20:26:49.000000000 +0000 +++ jinja2-2.8/debian/control 2015-08-01 12:29:04.000000000 +0000 @@ -3,11 +3,11 @@ Priority: optional Maintainer: Piotr Ożarowski Uploaders: Debian Python Modules Team -Build-Depends: debhelper (>= 9), +Build-Depends: debhelper (>= 9), dh-python, python-all (>= 2.6.6-3), python3-all, python-setuptools (>= 0.6b3-1~), python3-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx, python-pygments -Standards-Version: 3.9.4 +Standards-Version: 3.9.6 Homepage: http://jinja.pocoo.org/ X-Python-Version: >= 2.6 X-Python3-Version: >= 3.2 diff -Nru jinja2-2.7.3/debian/patches/drop_next_import_from_docs-jinjaext.patch jinja2-2.8/debian/patches/drop_next_import_from_docs-jinjaext.patch --- jinja2-2.7.3/debian/patches/drop_next_import_from_docs-jinjaext.patch 2013-05-20 19:34:02.000000000 +0000 +++ jinja2-2.8/debian/patches/drop_next_import_from_docs-jinjaext.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -Description: drop next import from docs/jinjaext.py - next is not available in jinja2/utils.py and all Python versions supported by - Jessie already support this function, no need for backwards compatible wrapper -Author: Piotr Ożarowski ---- jinja2-2.7.orig/docs/jinjaext.py -+++ jinja2-2.7/docs/jinjaext.py -@@ -23,7 +23,7 @@ from pygments.style import Style - from pygments.token import Keyword, Name, Comment, String, Error, \ - Number, Operator, Generic - from jinja2 import Environment, FileSystemLoader --from jinja2.utils import next -+#from jinja2.utils import next - - - def parse_rst(state, content_offset, doc): diff -Nru jinja2-2.7.3/debian/patches/jinja2-typos.patch jinja2-2.8/debian/patches/jinja2-typos.patch --- jinja2-2.7.3/debian/patches/jinja2-typos.patch 2013-05-25 21:18:08.000000000 +0000 +++ jinja2-2.8/debian/patches/jinja2-typos.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -Description: doc typos -Author: Jakub Wilk -Bug-Debian: http://bugs.debian.org/679534 ---- jinja2-2.7.orig/docs/tricks.rst -+++ jinja2-2.7/docs/tricks.rst -@@ -15,7 +15,7 @@ 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 igored, it can be used -+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 ---- jinja2-2.7.orig/docs/templates.rst -+++ jinja2-2.7/docs/templates.rst -@@ -474,7 +474,7 @@ include characters that affect the resul - manually escaping each variable or automatically escaping everything by default. - - Jinja supports both, but what is used depends on the application configuration. --The default configuaration is no automatic escaping for various reasons: -+The default configuration is no automatic escaping for various reasons: - - - escaping everything except of safe values will also mean that Jinja is - escaping variables known to not include HTML such as numbers which is -@@ -1033,7 +1033,7 @@ but exists for completeness' sake. The - a look at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``. - - \- -- Substract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. -+ Subtract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. - - / - Divide two numbers. The return value will be a floating point number. ---- jinja2-2.7.orig/docs/sandbox.rst -+++ jinja2-2.7/docs/sandbox.rst -@@ -54,7 +54,7 @@ Operator Intercepting - - .. versionadded:: 2.6 - --For maximum performace Jinja2 will let operators call directly the type -+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 ---- jinja2-2.7.orig/docs/api.rst -+++ jinja2-2.7/docs/api.rst -@@ -450,7 +450,7 @@ This is especially useful if you have a - the first request and Jinja compiles many templates at once which slows down - the application. - --To use a bytecode cache, instanciate it and pass it to the :class:`Environment`. -+To use a bytecode cache, instantiate it and pass it to the :class:`Environment`. - - .. autoclass:: jinja2.BytecodeCache - :members: load_bytecode, dump_bytecode, clear -@@ -618,7 +618,7 @@ Evaluation Context - ------------------ - - The evaluation context (short eval context or eval ctx) is a new object --introducted in Jinja 2.4 that makes it possible to activate and deactivate -+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 ---- jinja2-2.7.orig/docs/extensions.rst -+++ jinja2-2.7/docs/extensions.rst -@@ -213,7 +213,7 @@ Writing Extensions - - .. module:: jinja2.ext - --By writing extensions you can add custom tags to Jinja2. This is a non trival -+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. diff -Nru jinja2-2.7.3/debian/patches/series jinja2-2.8/debian/patches/series --- jinja2-2.7.3/debian/patches/series 2014-06-10 20:38:34.000000000 +0000 +++ jinja2-2.8/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -drop_next_import_from_docs-jinjaext.patch -jinja2-typos.patch diff -Nru jinja2-2.7.3/debian/python-jinja2-doc.dirs jinja2-2.8/debian/python-jinja2-doc.dirs --- jinja2-2.7.3/debian/python-jinja2-doc.dirs 2011-04-05 21:20:13.000000000 +0000 +++ jinja2-2.8/debian/python-jinja2-doc.dirs 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -/usr/share/doc/python-jinja2/ diff -Nru jinja2-2.7.3/debian/python-jinja2-doc.links jinja2-2.8/debian/python-jinja2-doc.links --- jinja2-2.7.3/debian/python-jinja2-doc.links 1970-01-01 00:00:00.000000000 +0000 +++ jinja2-2.8/debian/python-jinja2-doc.links 2015-08-01 12:35:32.000000000 +0000 @@ -0,0 +1,4 @@ +/usr/share/doc/python-jinja2-doc/html /usr/share/doc/python-jinja2/html +/usr/share/doc/python-jinja2-doc/html/_sources /usr/share/doc/python-jinja2/rst +/usr/share/doc/python-jinja2-doc/html /usr/share/doc/python3-jinja2/html +/usr/share/doc/python-jinja2-doc/html/_sources /usr/share/doc/python3-jinja2/rst diff -Nru jinja2-2.7.3/debian/README.source jinja2-2.8/debian/README.source --- jinja2-2.7.3/debian/README.source 2011-04-05 21:20:13.000000000 +0000 +++ jinja2-2.8/debian/README.source 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -This package uses the patch management system quilt as documented in -/usr/share/doc/quilt/README.source. diff -Nru jinja2-2.7.3/debian/rules jinja2-2.8/debian/rules --- jinja2-2.7.3/debian/rules 2013-05-25 21:07:00.000000000 +0000 +++ jinja2-2.8/debian/rules 2015-08-01 12:31:33.000000000 +0000 @@ -2,73 +2,14 @@ #export DH_VERBOSE=1 -include /usr/share/python3/python.mk +export PYBUILD_NAME=jinja2 -PYVERS=$(shell pyversions -vr) $(shell py3versions -vr) -VERSION=$(shell sed -rn 's,^Version: (.*),\1,p' $(CURDIR)/PKG-INFO) -pkgdir = $(CURDIR)/debian/python$(if $(patsubst 3.%,,$(1)),,3)-jinja2 +%: + dh $@ --with python2,python3 --buildsystem=pybuild -clean: - dh_testdir - dh_testroot - rm -rf dist build Jinja2.egg-info - find . -name '*\.pyc' -delete - find . -name '\._*' -delete - find . -name '\.DS_Store' -delete +override_dh_auto_clean: make -C docs clean - dh_clean install-stamp build-stamp \ - $(PYVERS:%=install-python%) $(PYVERS:%=build-python%) + dh_auto_clean -build: build-indep - -build-arch: - -build-indep: $(PYVERS:%=build-python%) build-docs - touch $@ - -build-python3%: - python3$* setup.py build - python debian/u2to3.py --no-diffs --write --nobackups build/lib/ - touch $@ - -build-python%: - python$* setup.py build - touch $@ - -build-docs: +override_dh_auto_build-indep: PYTHONPATH=$(CURDIR):$(CURDIR)/docs make -C docs html - -install: install-stamp -install-stamp: $(PYVERS:%=install-python%) - dh_install - touch $@ - -install-python%: build-python% - python$* setup.py install $(py_setup_install_args) \ - --skip-build --root $(call pkgdir,$*) - touch $@ - -binary-indep: build-indep install - dh_testdir - dh_testroot - dh_installchangelogs -i CHANGES - dh_installdocs -i - dh_sphinxdoc -p python-jinja2-doc - dh_installexamples -i - find $(CURDIR)/debian/ -name '*.py[co]' -delete - dh_python2 -i -p python-jinja2 - dh_python3 -i -p python3-jinja2 - dh_link -i /usr/share/doc/python-jinja2-doc/html /usr/share/doc/python-jinja2/html - dh_link -i /usr/share/doc/python-jinja2-doc/html/_sources /usr/share/doc/python-jinja2/rst - dh_compress -i -X.html -X.py -Xobjects.inv - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i -- -Z bzip2 - -binary-arch: - -binary: binary-indep binary-arch - -.PHONY: build clean binary-indep binary-arch binary install diff -Nru jinja2-2.7.3/debian/u2to3.py jinja2-2.8/debian/u2to3.py --- jinja2-2.7.3/debian/u2to3.py 2013-05-25 21:07:00.000000000 +0000 +++ jinja2-2.8/debian/u2to3.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -#!/usr/bin/python - -''' -minimal 2to3 coverter that touches only u'' literals -''' - -import sys -from lib2to3.fixes.fix_unicode import FixUnicode - -FixUnicode.PATTERN = 'STRING' -sys.argv[1:1] = ['-f', 'unicode'] -execfile('/usr/bin/2to3') diff -Nru jinja2-2.7.3/debian/watch jinja2-2.8/debian/watch --- jinja2-2.7.3/debian/watch 2011-04-05 21:20:13.000000000 +0000 +++ jinja2-2.8/debian/watch 2015-08-01 12:27:37.000000000 +0000 @@ -1,3 +1,3 @@ version=3 -opts=uversionmangle=s/rc/~rc/ \ -http://pypi.python.org/packages/source/J/Jinja2/Jinja2-(.+)\.tar\.gz debian uupdate +opts=uversionmangle=s/(rc|a|b|c)/~$1/ \ +http://pypi.debian.net/Jinja2/Jinja2-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) \ No newline at end of file diff -Nru jinja2-2.7.3/docs/api.rst jinja2-2.8/docs/api.rst --- jinja2-2.7.3/docs/api.rst 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/docs/api.rst 2015-07-26 17:43:00.000000000 +0000 @@ -12,7 +12,7 @@ ------ Jinja2 uses a central object called the template :class:`Environment`. -Instances of this class are used to store the configuration, global objects +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, @@ -44,7 +44,7 @@ print template.render(the='variables', go='here') -Using a template loader rather then passing strings to :class:`Template` +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. @@ -154,6 +154,19 @@ to modify this dict. For more details see :ref:`global-namespace`. For valid object names have a look at :ref:`identifier-naming`. + .. 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]) @@ -167,7 +180,7 @@ 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 ommited. + `name` may be omitted. The most common way to create an undefined object is by providing a name only:: @@ -187,7 +200,7 @@ 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 shold be passed to the undefined object, even if + 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. @@ -316,6 +329,11 @@ .. 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 @@ -450,7 +468,7 @@ the first request and Jinja compiles many templates at once which slows down the application. -To use a bytecode cache, instanciate it and pass it to the :class:`Environment`. +To use a bytecode cache, instantiate it and pass it to the :class:`Environment`. .. autoclass:: jinja2.BytecodeCache :members: load_bytecode, dump_bytecode, clear @@ -602,7 +620,7 @@ @evalcontextfilter def nl2br(eval_ctx, value): - result = u'\n\n'.join(u'

%s

' % p.replace('\n', '
\n') + 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) @@ -618,7 +636,7 @@ ------------------ The evaluation context (short eval context or eval ctx) is a new object -introducted in Jinja 2.4 that makes it possible to activate and deactivate +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 diff -Nru jinja2-2.7.3/docs/cache_extension.py jinja2-2.8/docs/cache_extension.py --- jinja2-2.7.3/docs/cache_extension.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/cache_extension.py 2015-07-26 17:43:00.000000000 +0000 @@ -20,7 +20,7 @@ # 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 = parser.stream.next().lineno + lineno = next(parser.stream).lineno # now we parse a single expression that is used as cache key. args = [parser.parse_expression()] diff -Nru jinja2-2.7.3/docs/contents.rst.inc jinja2-2.8/docs/contents.rst.inc --- jinja2-2.7.3/docs/contents.rst.inc 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/contents.rst.inc 2015-05-25 09:40:18.000000000 +0000 @@ -21,9 +21,3 @@ faq changelog - -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` Binary files /tmp/bkWsBeMhST/jinja2-2.7.3/docs/.DS_Store and /tmp/iqtS1tb7Pe/jinja2-2.8/docs/.DS_Store differ diff -Nru jinja2-2.7.3/docs/extensions.rst jinja2-2.8/docs/extensions.rst --- jinja2-2.7.3/docs/extensions.rst 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/extensions.rst 2015-05-25 09:40:18.000000000 +0000 @@ -4,7 +4,7 @@ ========== Jinja2 supports extensions that can add extra filters, tests, globals or even -extend the parser. The main motivation of extensions is it to move often used +extend the parser. The main motivation of extensions is to move often used code into a reusable class like adding support for internationalization. @@ -14,7 +14,7 @@ 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 -`environment` parameter of the :class:`Environment` constructor. The following +`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']) @@ -27,25 +27,24 @@ **Import name:** `jinja2.ext.i18n` -Jinja2 currently comes with one extension, the i18n extension. It 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`. +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 +After enabling, dummy `_` function that forwards calls to `gettext` is added to the environment globals. An internationalized application then has to -provide at least an `gettext` and optionally a `ngettext` function into the -namespace. Either globally or for each rendering. +provide a `gettext` function and optionally an `ngettext` function into the +namespace, either globally or for each rendering. Environment Methods ~~~~~~~~~~~~~~~~~~~ -After enabling of the extension the environment provides the following +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 tranlations + 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. @@ -89,7 +88,7 @@ * `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 ` + 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 @@ -125,7 +124,7 @@ 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 some common differences between old and new calls: +Here are some common differences between old and new calls: standard gettext: @@ -145,7 +144,7 @@ {{ gettext('Hello %(name)s!', name='World') }} {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} -The advantages of newstyle gettext is that you have less to type and that +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 @@ -174,7 +173,7 @@ **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 +enabling, Jinja2 provides those two keywords which work exactly like in Python. .. _with-extension: @@ -213,10 +212,10 @@ .. module:: jinja2.ext -By writing extensions you can add custom tags to Jinja2. This is a non trival +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. +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 diff -Nru jinja2-2.7.3/docs/faq.rst jinja2-2.8/docs/faq.rst --- jinja2-2.7.3/docs/faq.rst 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/docs/faq.rst 2015-07-26 17:43:00.000000000 +0000 @@ -135,7 +135,7 @@ 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 +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:: @@ -147,8 +147,8 @@ Credit for this snippet goes to `Thomas Johansson `_ -Why is there no Python 2.3 support? ------------------------------------ +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 @@ -156,8 +156,14 @@ 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: diff -Nru jinja2-2.7.3/docs/index.rst jinja2-2.8/docs/index.rst --- jinja2-2.7.3/docs/index.rst 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/index.rst 2014-08-08 14:42:36.000000000 +0000 @@ -1,7 +1,7 @@ Welcome to Jinja2 ================= -Jinja2 is a modern and designer friendly templating language for Python, +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: @@ -20,7 +20,7 @@ - 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 +- optional ahead-of-time template compilation - easy to debug. Line numbers of exceptions directly point to the correct line in the template. - configurable syntax diff -Nru jinja2-2.7.3/docs/intro.rst jinja2-2.8/docs/intro.rst --- jinja2-2.7.3/docs/intro.rst 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/intro.rst 2014-08-08 14:42:36.000000000 +0000 @@ -17,7 +17,7 @@ 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. +need `setuptools`_ or `distribute`_ installed at runtime. Installation ------------ @@ -25,8 +25,8 @@ 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) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +As a Python egg (via `easy_install`) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can install the most recent Jinja2 version using `easy_install`_ or `pip`_:: @@ -46,13 +46,11 @@ 2. Unpack the tarball 3. ``sudo python setup.py install`` -Note that you either have to have setuptools or `distribute`_ installed; +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. -.. _distribute: http://pypi.python.org/pypi/distribute - Installing the development version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -62,10 +60,11 @@ 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 +which will install the package via `distribute` in development mode. This also has the advantage that the C extensions are compiled. .. _download page: http://pypi.python.org/pypi/Jinja2 +.. _distribute: http://pypi.python.org/pypi/distribute .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall .. _pip: http://pypi.python.org/pypi/pip @@ -117,7 +116,7 @@ provide feedback in the `Jinja bug tracker`_. Also please keep in mind that the documentation is written with Python 2 -in mind, you will have to adapt the shown code examples to Python 3 syntax +in mind, so you will have to adapt the shown code examples to Python 3 syntax for yourself. diff -Nru jinja2-2.7.3/docs/jinjaext.py jinja2-2.8/docs/jinjaext.py --- jinja2-2.7.3/docs/jinjaext.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/docs/jinjaext.py 2014-08-08 14:42:36.000000000 +0000 @@ -23,7 +23,6 @@ from pygments.token import Keyword, Name, Comment, String, Error, \ Number, Operator, Generic from jinja2 import Environment, FileSystemLoader -from jinja2.utils import next def parse_rst(state, content_offset, doc): diff -Nru jinja2-2.7.3/docs/sandbox.rst jinja2-2.8/docs/sandbox.rst --- jinja2-2.7.3/docs/sandbox.rst 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/sandbox.rst 2014-08-08 14:42:36.000000000 +0000 @@ -54,7 +54,7 @@ .. versionadded:: 2.6 -For maximum performace Jinja2 will let operators call directly the type +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 diff -Nru jinja2-2.7.3/docs/switching.rst jinja2-2.8/docs/switching.rst --- jinja2-2.7.3/docs/switching.rst 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/docs/switching.rst 2015-05-25 09:40:18.000000000 +0000 @@ -4,7 +4,7 @@ .. 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 syntatic and semantic +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 @@ -100,21 +100,21 @@ Method Calls ~~~~~~~~~~~~ -In Django method calls work implicitly. With Jinja2 you have to specify that -you want to call an object. Thus this Django code:: +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 %} - -will look like this in Jinja:: + +...looks like this in Jinja:: {% for page in user.get_created_pages() %} ... {% endfor %} -This allows you to pass variables to the function which is also used for macros -which is not possible in Django. +This allows you to pass variables to the method, which is not possible in +Django. This syntax is also used for macros. Conditions ~~~~~~~~~~ @@ -159,7 +159,7 @@ {{ items|join(', ') }} -In fact it's a bit more verbose but it allows different types of arguments - +It is a bit more verbose, but it allows different types of arguments - including variables - and more than one of them. Tests @@ -177,11 +177,11 @@ Loops ~~~~~ -For loops work very similar to Django. Notably, in Jinja2 the special variable for -the loop context is called `loop` and not `forloop` like in Django. +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:: +In addition, the Django `empty` argument is called `else` in Jinja2. For +example, the Django template:: {% for item in items %} {{ item }} @@ -189,7 +189,7 @@ No items! {% endfor %} -would be handled in Jinja2 as:: +...looks like this in Jinja2:: {% for item in items %} {{ item }} @@ -200,9 +200,8 @@ Cycle ~~~~~ -The ``{% cycle %}`` tag does not exist in Jinja because of it's implicit -nature. However you can achieve mostly the same by using the `cycle` -method on a loop object. +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:: @@ -210,7 +209,7 @@
  • {{ user }}
  • {% endfor %} -Would look like this in Jinja:: +...looks like this in Jinja2:: {% for user in users %}
  • {{ user }}
  • @@ -231,11 +230,11 @@ env = Environment('<%', '%>', '${', '}', '<%doc>', '', '%', '##') -Once the environment is 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 (in -Jinja2 defs are called macros) and template inheritance is different too. The -following Mako template:: +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 diff -Nru jinja2-2.7.3/docs/templates.rst jinja2-2.8/docs/templates.rst --- jinja2-2.7.3/docs/templates.rst 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/docs/templates.rst 2015-05-25 09:40:18.000000000 +0000 @@ -5,26 +5,27 @@ 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 might -be slightly different from here in terms of delimiters and behavior of -undefined values. +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 template is simply a text file. It can generate any text-based format -(HTML, XML, CSV, LaTeX, etc.). It doesn't have a specific extension, -``.html`` or ``.xml`` are just fine. - -A template contains **variables** or **expressions**, which get replaced with -values when the template is evaluated, and tags, which control the logic of -the template. The template syntax is heavily inspired by Django and Python. +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. We will cover -the details later in that document:: +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 @@ -38,63 +39,77 @@

    My Webpage

    {{ a_variable }} + + {# a comment #} -This covers the default settings. The application developer might have -changed the syntax from ``{% foo %}`` to ``<% foo %>`` or something similar. +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 ` -There are two kinds of delimiters. ``{% ... %}`` and ``{{ ... }}``. The first -one is used to execute statements such as for-loops or assign values, the -latter prints the result of the expression to the template. .. _variables: Variables --------- -The application passes variables to the templates you can mess around in the -template. Variables may have attributes or elements on them you can access -too. How a variable looks like, heavily depends on the application providing -those. - -You can use a dot (``.``) to access attributes of a variable, alternative the -so-called "subscript" syntax (``[]``) can be used. The following lines do -the same:: +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 curly braces are *not* part of the variable -but the print statement. If you access variables inside tags don't put the -braces around. +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 +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 that it evaluates to an empty string -if printed and that you can iterate over it, but every other operation fails. +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 convenience sake ``foo.bar`` in Jinja2 does the following things on - the Python layer: + For the sake of convenience, ``foo.bar`` in Jinja2 does the following + things on the Python layer: - - check if there is an attribute called `bar` on `foo`. - - if there is not, check if there is an item ``'bar'`` in `foo`. + - 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']`` on the other hand works mostly the same with the a small - difference in the order: + ``foo['bar']`` works mostly the same with a small difference in sequence: - - check if there is an item ``'bar'`` in `foo`. - - if there is not, check if there is an attribute called `bar` on `foo`. + - 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 or attribute with the same - name. Additionally there is the :func:`attr` filter that just looks up - attributes. + 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: @@ -106,10 +121,12 @@ parentheses. Multiple filters can be chained. The output of one filter is applied to the next. -``{{ name|striptags|title }}`` for example will remove all HTML Tags from the -`name` and title-cases it. Filters that accept arguments have parentheses -around the arguments, like a function call. This example will join a list -by commas: ``{{ list|join(', ') }}``. +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. @@ -118,15 +135,16 @@ Tests ----- -Beside filters there are also so called "tests" available. Tests can be used +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 if `name` is defined. - -Tests can accept arguments too. If the test only takes one argument you can -leave out the parentheses to group them. For example the following two -expressions do the same:: +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) %} @@ -134,6 +152,8 @@ The :ref:`builtin-tests` below describes all the builtin tests. +.. _comments: + Comments -------- @@ -142,7 +162,7 @@ template for debugging or to add information for other template designers or yourself:: - {# note: disabled template because we no longer use this + {# note: commented-out template because we no longer use this {% for user in users %} ... {% endfor %} @@ -152,16 +172,18 @@ Whitespace Control ------------------ -In the default configuration, a single trailing newline is stripped if -present, and whitespace is not further modified by the template engine. Each -whitespace (spaces, tabs, newlines etc.) is returned unchanged. If the -application configures Jinja to `trim_blocks` the first newline after a +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 +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 +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:: @@ -180,8 +202,8 @@ -But with both `trim_blocks` and `lstrip_blocks` enabled, the lines with the -template blocks are removed while preserving the whitespace of the contents:: +But with both `trim_blocks` and `lstrip_blocks` enabled, the template block +lines are removed and other whitespace is preserved::
    yay @@ -194,28 +216,27 @@ {%+ if something %}yay{% endif %}
    -You can also strip whitespace in templates by hand. If you put an minus -sign (``-``) to the start or end of an block (for example a for tag), a -comment or variable expression you can remove the whitespaces after or before -that block:: +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``. +a list of numbers from ``1`` to ``9``, the output would be ``123456789``. -If :ref:`line-statements` are enabled they strip leading whitespace +If :ref:`line-statements` are enabled, they strip leading whitespace automatically up to the beginning of the line. -Jinja2 by default also removes trailing newlines. To keep the single -trailing newline when it is present, configure Jinja to -`keep_trailing_newline`. +By default, Jinja2 also removes trailing newlines. To keep single +trailing newlines, configure Jinja to `keep_trailing_newline`. .. admonition:: Note - You must not use a whitespace between the tag and the minus sign. + You must not add whitespace between the tag and the minus sign. **valid**:: @@ -229,18 +250,18 @@ Escaping -------- -It is sometimes desirable or even necessary to have Jinja ignore parts it -would otherwise handle as variables or blocks. For example if the default -syntax is used and you want to use ``{{`` as raw string in the template and -not start a variable you have to use a trick. +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 is to output the variable delimiter (``{{``) by using a +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 -put Jinja syntax as example into a template you can use this snippet:: +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 %}
      @@ -256,9 +277,9 @@ 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:: +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 @@ -273,7 +294,7 @@
      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 +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: @@ -293,8 +314,8 @@ # 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 +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: @@ -322,9 +343,8 @@ 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 %} @@ -339,10 +359,11 @@ {% endblock %} + In this example, the ``{% block %}`` tags define four blocks that child templates -can fill in. All the `block` tag does is to tell the template engine that a -child template may override those portions of the template. +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 ~~~~~~~~~~~~~~ @@ -360,18 +381,18 @@ {% block content %}

    Index

    - Welcome on my awesome homepage. + 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, first it locates the parent. The extends tag should be the +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 +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:: @@ -383,12 +404,12 @@ You can't define multiple ``{% block %}`` tags with the same name in the same template. This limitation exists because a block tag works in "both" -directions. That is, a block tag doesn't just provide a hole to fill - it -also defines the content that fills the hole in the *parent*. If there -were two similarly-named ``{% block %}`` tags in a template, that template's -parent wouldn't know which one of the blocks' content to use. +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 +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 %} @@ -421,13 +442,13 @@ {% endblock inner_sidebar %} {% endblock sidebar %} -However the name after the `endblock` word must match the block name. +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 +Blocks can be nested for more complex layouts. However, per default blocks may not access variables from outer scopes:: {% for item in seq %} @@ -436,10 +457,10 @@ 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 +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 +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:: @@ -447,7 +468,7 @@
  • {% block loop_item scoped %}{{ item }}{% endblock %}
  • {% endfor %} -When overriding a block the `scoped` modifier does not have to be provided. +When overriding a block, the `scoped` modifier does not have to be provided. Template Objects @@ -455,14 +476,14 @@ .. versionchanged:: 2.4 -If a template object was passed to the template context you can +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 +Previously, the `layout_template` variable had to be a string with the layout template's filename for this to work. @@ -470,60 +491,69 @@ ------------- 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: -manually escaping each variable or automatically escaping everything by default. +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, but what is used depends on the application configuration. -The default configuaration is no automatic escaping for various reasons: +Jinja supports both. What is used depends on the application configuration. +The default configuration is no automatic escaping; for various reasons: -- escaping everything except of safe values will also mean that Jinja is - escaping variables known to not include HTML such as numbers which is - a huge performance hit. +- 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. + 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 +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 -**have to** escape it unless the variable contains well-formed and trusted -HTML. Escaping works by piping the variable through the ``|e`` filter: -``{{ user.username|e }}``. +**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. Those can either be marked by the -application or in the template by using the `|safe` filter. The main -problem with this approach is that Python itself doesn't have the concept -of tainted values so the information if a value is safe or unsafe can get -lost. If the information is lost 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 the string modulo -operator. +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 (%)*. -Functions returning template data (macros, `super`, `self.BLOCKNAME`) return -safe markup always. +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 -too. The reason for this is that the safe string is an extension to Python -and not every library will work properly with it. +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. Control structures appear inside ``{% ... %}`` blocks -in the default syntax. +macros and blocks. With the default syntax, control structures appear inside +``{% ... %}`` blocks. + +.. _for-loop: For ~~~ @@ -548,11 +578,11 @@ {% endfor %} -Note however that dictionaries usually are unordered so you might want to -either pass it as a sorted list to the template or use the `dictsort` -filter. +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: +Inside of a for-loop block, you can access some special variables: +-----------------------+---------------------------------------------------+ | Variable | Description | @@ -579,7 +609,7 @@ | `loop.depth` | Indicates how deep in deep in a recursive loop | | | the rendering currently is. Starts at level 1 | +-----------------------+---------------------------------------------------+ -| `loop.depth0 | Indicates how deep in deep in a recursive loop | +| `loop.depth0` | Indicates how deep in deep in a recursive loop | | | the rendering currently is. Starts at level 0 | +-----------------------+---------------------------------------------------+ @@ -590,24 +620,24 @@
  • {{ 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`. +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 +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 +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 replacement block +removed all the items from the sequence, you can render a default block by using `else`::
      @@ -618,14 +648,15 @@ {% endfor %}
    -Note that in Python `else` blocks are executed whenever the corresponding -loop did not `break`. Since in Jinja loops cannot `break` anyway, +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. 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. +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:: @@ -639,15 +670,17 @@ The `loop` variable always refers to the closest (innermost) loop. If we -have more than one levels of loops, we can rebind the variable `loop` by +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(...) }}` +.. _if: + If ~~ -The `if` statement in Jinja is comparable with the if statements of Python. -In the simplest form you can use it to test if a variable is defined, not +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 or not false:: {% if users %} @@ -658,8 +691,8 @@ {% endif %} -For multiple branches `elif` and `else` can be used like in Python. You can -use more complex :ref:`expressions` there too:: +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. @@ -669,18 +702,19 @@ Kenny looks okay --- so far {% endif %} -If can also be used as :ref:`inline expression ` and for +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. +yourself ("DRY"). -Here a small example of a macro that renders a form element:: +Here's a small example of a macro that renders a form element:: {% macro input(name, value='', type='text', size=20) -%} Index +
  • Downloads + {% endset %} + +The `navigation` variable then contains the navigation HTML source. + + +.. _extends: + Extends ~~~~~~~ -The `extends` tag can be used to extend a template from another one. You -can have multiple of them in a file but only one of them may be executed -at the time. See the section about :ref:`template-inheritance` above. +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. -Block -~~~~~ -Blocks are used for inheritance and act as placeholders and replacements -at the same time. They are documented in detail as part of the section -about :ref:`template-inheritance`. +.. _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 @@ -836,13 +899,13 @@ {% 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 +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 +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 has -to be placed *before* the context visibility statement. Here some valid +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 %} @@ -862,7 +925,7 @@ {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} .. versionchanged:: 2.4 - If a template object was passed to the template context you can + If a template object was passed to the template context, you can include that object using `include`. .. _import: @@ -871,13 +934,13 @@ ~~~~~~ Jinja2 supports putting often used code into macros. These macros can go into -different templates and get imported from there. This works similar to the +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`. +imports and includes, see :ref:`import-visibility`. -There are two ways to import templates. You can import the complete template +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`):: @@ -891,8 +954,9 @@ }}">{{ value|e }} {%- endmacro %} -The easiest and most flexible is importing the whole module into a variable. -That way you can access the attributes:: +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 %}
    @@ -904,7 +968,7 @@

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

    -Alternatively you can import names from the template into the current +Alternatively, you can import specific names from a template into the current namespace:: {% from 'forms.html' import input as input_field, textarea %} @@ -920,7 +984,7 @@ cannot be imported. .. versionchanged:: 2.4 - If a template object was passed to the template context you can + If a template object was passed to the template context, you can import from that object. @@ -929,31 +993,31 @@ Import Context Behavior ----------------------- -Per default included templates are passed the current context and imported -templates not. The reason for this is that imports unlike includes are -cached as imports are often used just as a module that holds macros. +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 however can be changed of course explicitly. By adding `with context` -or `without context` to the import/include directive the current context +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 two examples:: +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 + 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:: + 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 + `box` in Jinja 2.0. As of Jinja 2.1, ``render_box.html`` *is* able to do so. @@ -962,9 +1026,9 @@ Expressions ----------- -Jinja allows basic expressions everywhere. These work very similar to regular -Python and even if you're not working with Python you should feel comfortable -with it. +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 ~~~~~~~~ @@ -974,20 +1038,20 @@ "Hello World": Everything between two double or single quotes is a string. They are - useful whenever you need a string in the template (for example as - arguments to function calls, filters or just to extend or include a + 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 for Python ``42`` and ``42.0`` is something - different. + 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 to store - sequential data in or to iterate over them. For example you can easily - create a list of links using lists and tuples with a for loop:: + 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'), @@ -997,15 +1061,15 @@
    ('tuple', 'of', 'values'): - Tuples are like lists, just that you can't modify them. If the tuple - only has one item you have to end it with a comma. Tuples are usually - used to represent items of two or more elements. See the example above - for more details. + 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` + templates; they are useful in some rare cases such as the :func:`xmlattr` filter. true / false: @@ -1013,12 +1077,13 @@ .. admonition:: Note - The special constants `true`, `false` and `none` are indeed lowercase. - Because that caused confusion in the past, when writing `True` expands - to an undefined variable that is considered false, all three of them can - be written in title case too (`True`, `False`, and `None`). However for - consistency (all Jinja identifiers are lowercase) you should use the - lowercase versions. + 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 ~~~~ @@ -1027,17 +1092,18 @@ 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 at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``. + 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``. \- - Substract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. + 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. @@ -1079,14 +1145,14 @@ Logic ~~~~~ -For `if` statements, `for` filtering or `if` expressions it can be useful to +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 is true. + Return true if the left and the right operand are true. or - Return true if the left or the right operand is true. + Return true if the left or the right operand are true. not negate a statement (see below). @@ -1096,7 +1162,7 @@ .. admonition:: Note - The ``is`` and ``in`` operators support negation using an infix notation + 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).`` @@ -1109,9 +1175,9 @@ two categories: in - Perform 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. + 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 `. @@ -1121,12 +1187,14 @@ ~ Converts all operands into strings and concatenates them. - ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is - ``'John'``) ``Hello John!``. + + ``{{ "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: + can use positional arguments and keyword arguments like in Python: + ``{{ post.render(user, full=true) }}``. . / [] @@ -1139,7 +1207,7 @@ ~~~~~~~~~~~~~ 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 +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' %} @@ -1147,7 +1215,7 @@ The general syntax is `` if else ``. -The `else` part is optional. If not provided the else block implicitly +The `else` part is optional. If not provided, the else block implicitly evaluates into an undefined object:: {{ '[%s]' % page.title if page.title }} @@ -1178,12 +1246,14 @@ .. 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. + ``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) returns [0, 1, 2, 3]. The end point is omitted! + 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 for example + 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:: @@ -1198,9 +1268,9 @@ .. function:: lipsum(n=5, html=True, min=20, max=100) - Generates some lorem ipsum for the template. Per default five paragraphs - with HTML are generated each paragraph between 20 and 100 words. If html - is disabled regular text is returned. This is useful to generate simple + 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) @@ -1211,11 +1281,11 @@ .. class:: cycler(\*items) The cycler allows you to cycle among values similar to how `loop.cycle` - works. Unlike `loop.cycle` however you can use this cycler outside of + works. Unlike `loop.cycle`, you can use this cycler outside of loops or over multiple loops. - This is for example 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 + 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:: @@ -1238,7 +1308,7 @@ .. method:: next() - Goes one item a head and returns the then current item. + Goes one item ahead and returns the then-current item. .. attribute:: current @@ -1248,9 +1318,9 @@ .. class:: joiner(sep=', ') - A tiny helper that can be use to "join" multiple sections. A joiner is + 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 situation it returns an empty string. You can + the first time (in which case it returns an empty string). You can use this to join things:: {% set pipe = joiner("|") %} @@ -1271,21 +1341,22 @@ ---------- The following sections cover the built-in Jinja2 extensions that may be -enabled by the application. The application could also provide further -extensions not covered by this documentation. In that case there should -be a separate document explaining the extensions. +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`:: +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 just +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:: @@ -1309,30 +1380,30 @@ There are {{ count }} {{ name }} objects. {% endtrans %} -Per 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 +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 %} -It's also possible to translate strings in expressions. For that purpose +It's also possible to translate strings in expressions. For that purpose, three functions exist: -_ `gettext`: translate a single string +- `gettext`: translate a single string - `ngettext`: translate a pluralizable string - `_`: alias for `gettext` -For example you can print a translated string easily this way:: +For example, you can easily print a translated string like this:: {{ _('Hello World!') }} -To use placeholders you can use the `format` filter:: +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. +For multiple placeholders, always use keyword arguments to `format`, +as other languages may not use the words in the same order. .. versionchanged:: 2.5 @@ -1346,15 +1417,15 @@ {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} Note that the `ngettext` function's format string automatically receives -the count as `num` parameter additionally to the regular parameters. +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 (``{{ ... }}``) just -that it doesn't print anything. This can be used to modify lists:: +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') %} @@ -1362,31 +1433,34 @@ Loop Controls ~~~~~~~~~~~~~ -If the application enables the :ref:`loopcontrols-extension` it's possible to +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 a loop that skips every second item:: +Here's a loop that skips every second item:: {% for user in users %} {%- if loop.index is even %}{% continue %}{% endif %} ... {% endfor %} -Likewise a look that stops processing after the 10th iteration:: +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 -If the application enables the :ref:`with-extension` it is possible to +If the application enables the :ref:`with-extension`, it is possible to use the `with` keyword in templates. This makes it possible to create a new inner scope. Variables set within this scope are not visible outside of the scope. @@ -1399,8 +1473,8 @@ {% 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 +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 %} @@ -1419,7 +1493,7 @@ .. versionadded:: 2.4 -If the application enables the :ref:`autoescape-extension` one can +If the application enables the :ref:`autoescape-extension`, one can activate and deactivate the autoescaping from within the templates. Example:: @@ -1432,4 +1506,4 @@ Autoescaping is inactive within this block {% endautoescape %} -After the `endautoescape` the behavior is reverted to what it was before. +After an `endautoescape` the behavior is reverted to what it was before. diff -Nru jinja2-2.7.3/docs/tricks.rst jinja2-2.8/docs/tricks.rst --- jinja2-2.7.3/docs/tricks.rst 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/docs/tricks.rst 2015-05-25 09:40:18.000000000 +0000 @@ -15,11 +15,11 @@ 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 igored, it can be used +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 put the `extends` tag into an `if` tag to +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 @@ -63,7 +63,7 @@ {% set active_page = "index" %} The layout template can then access `active_page`. Additionally it makes -sense to defined a default for that variable:: +sense to define a default for that variable:: {% set navigation_bar = [ ('/', 'index', 'Index'), @@ -93,7 +93,7 @@ {% set rowloop = loop %} {% for cell in row %} - {{ cell }} {% endfor %} {% endfor %} diff -Nru jinja2-2.7.3/examples/basic/test_filter_and_linestatements.py jinja2-2.8/examples/basic/test_filter_and_linestatements.py --- jinja2-2.7.3/examples/basic/test_filter_and_linestatements.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/examples/basic/test_filter_and_linestatements.py 2015-05-25 09:40:18.000000000 +0000 @@ -22,4 +22,4 @@ % endfilter """) -print tmpl.render(seq=range(10)) +print(tmpl.render(seq=range(10))) diff -Nru jinja2-2.7.3/examples/basic/test_loop_filter.py jinja2-2.8/examples/basic/test_loop_filter.py --- jinja2-2.7.3/examples/basic/test_loop_filter.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/examples/basic/test_loop_filter.py 2015-05-25 09:40:18.000000000 +0000 @@ -9,4 +9,4 @@ if condition: {{ 1 if foo else 0 }} """) -print tmpl.render(foo=True) +print(tmpl.render(foo=True)) diff -Nru jinja2-2.7.3/examples/basic/test.py jinja2-2.8/examples/basic/test.py --- jinja2-2.7.3/examples/basic/test.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/examples/basic/test.py 2015-05-25 09:40:18.000000000 +0000 @@ -24,4 +24,4 @@ tmpl = env.get_template("child.html") -print tmpl.render() +print(tmpl.render()) diff -Nru jinja2-2.7.3/ext/djangojinja2.py jinja2-2.8/ext/djangojinja2.py --- jinja2-2.7.3/ext/djangojinja2.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/ext/djangojinja2.py 2015-05-25 09:40:18.000000000 +0000 @@ -45,7 +45,7 @@ searchpath = list(settings.JINJA2_TEMPLATE_DIRS) return Environment(loader=FileSystemLoader(searchpath), auto_reload=settings.TEMPLATE_DEBUG, - cache_size=getattr(settings, 'JINJA2_CACHE_SIZE', 50), + cache_size=getattr(settings, 'JINJA2_CACHE_SIZE', 400), extensions=getattr(settings, 'JINJA2_EXTENSIONS', ())) diff -Nru jinja2-2.7.3/ext/Vim/jinja.vim jinja2-2.8/ext/Vim/jinja.vim --- jinja2-2.7.3/ext/Vim/jinja.vim 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/ext/Vim/jinja.vim 2015-07-26 17:46:35.000000000 +0000 @@ -59,8 +59,8 @@ 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 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 diff -Nru jinja2-2.7.3/jinja2/bccache.py jinja2-2.8/jinja2/bccache.py --- jinja2-2.7.3/jinja2/bccache.py 2014-06-06 16:47:51.000000000 +0000 +++ jinja2-2.8/jinja2/bccache.py 2014-08-08 14:42:36.000000000 +0000 @@ -16,8 +16,8 @@ """ from os import path, listdir import os -import stat import sys +import stat import errno import marshal import tempfile @@ -88,7 +88,12 @@ if self.checksum != checksum: self.reset() return - self.code = marshal_load(f) + # if marshal_load fails then we need to reload + try: + self.code = marshal_load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return def write_bytecode(self, f): """Dump the bytecode into the file or file like object passed.""" @@ -212,6 +217,10 @@ self.pattern = pattern def _get_default_cache_dir(self): + def _unsafe_dir(): + raise RuntimeError('Cannot determine safe temp directory. You ' + 'need to explicitly provide one.') + tmpdir = tempfile.gettempdir() # On windows the temporary directory is used specific unless @@ -219,23 +228,32 @@ if os.name == 'nt': return tmpdir if not hasattr(os, 'getuid'): - raise RuntimeError('Cannot determine safe temp directory. You ' - 'need to explicitly provide one.') + _unsafe_dir() dirname = '_jinja2-cache-%d' % os.getuid() actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise try: - os.mkdir(actual_dir, stat.S_IRWXU) # 0o700 + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if actual_dir_stat.st_uid != os.getuid() \ + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() except OSError as e: if e.errno != errno.EEXIST: raise actual_dir_stat = os.lstat(actual_dir) if actual_dir_stat.st_uid != os.getuid() \ - or not stat.S_ISDIR(actual_dir_stat.st_mode) \ - or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: - raise RuntimeError('Temporary directory \'%s\' has an incorrect ' - 'owner, permissions, or type.' % actual_dir) + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() return actual_dir diff -Nru jinja2-2.7.3/jinja2/_compat.py jinja2-2.8/jinja2/_compat.py --- jinja2-2.7.3/jinja2/_compat.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/_compat.py 2015-05-25 09:40:18.000000000 +0000 @@ -22,6 +22,7 @@ range_type = range text_type = str string_types = (str,) + integer_types = (int,) iterkeys = lambda d: iter(d.keys()) itervalues = lambda d: iter(d.values()) @@ -51,6 +52,7 @@ text_type = unicode range_type = xrange string_types = (str, unicode) + integer_types = (int, long) iterkeys = lambda d: d.iterkeys() itervalues = lambda d: d.itervalues() @@ -82,12 +84,6 @@ return filename.encode('utf-8') return filename -try: - next = next -except NameError: - def next(it): - return it.next() - def with_metaclass(meta, *bases): # This requires a bit of explanation: the basic idea is to make a @@ -110,41 +106,6 @@ try: - from collections import Mapping as mapping_types -except ImportError: - import UserDict - mapping_types = (UserDict.UserDict, UserDict.DictMixin, dict) - - -# common types. These do exist in the special types module too which however -# does not exist in IronPython out of the box. Also that way we don't have -# to deal with implementation specific stuff here -class _C(object): - def method(self): pass -def _func(): - yield None -function_type = type(_func) -generator_type = type(_func()) -method_type = type(_C().method) -code_type = type(_C.method.__code__) -try: - raise TypeError() -except TypeError: - _tb = sys.exc_info()[2] - traceback_type = type(_tb) - frame_type = type(_tb.tb_frame) - - -try: from urllib.parse import quote_from_bytes as url_quote except ImportError: from urllib import quote as url_quote - - -try: - from thread import allocate_lock -except ImportError: - try: - from threading import Lock as allocate_lock - except ImportError: - from dummy_thread import allocate_lock diff -Nru jinja2-2.7.3/jinja2/compiler.py jinja2-2.8/jinja2/compiler.py --- jinja2-2.7.3/jinja2/compiler.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/compiler.py 2015-05-25 10:20:55.000000000 +0000 @@ -16,7 +16,7 @@ from jinja2.visitor import NodeVisitor from jinja2.exceptions import TemplateAssertionError from jinja2.utils import Markup, concat, escape -from jinja2._compat import range_type, next, text_type, string_types, \ +from jinja2._compat import range_type, text_type, string_types, \ iteritems, NativeStringIO, imap @@ -57,7 +57,8 @@ """Generate the python source for a node tree.""" if not isinstance(node, nodes.Template): raise TypeError('Can\'t compile non template nodes') - generator = CodeGenerator(environment, name, filename, stream, defer_init) + generator = environment.code_generator_class(environment, name, filename, + stream, defer_init) generator.visit(node) if stream is None: return generator.stream.getvalue() @@ -347,6 +348,9 @@ def visit_FilterBlock(self, node): self.visit(node.filter) + def visit_AssignBlock(self, node): + """Stop visiting at block assigns.""" + def visit_Scope(self, node): """Stop visiting at scopes.""" @@ -1215,8 +1219,17 @@ if self.has_known_extends and frame.require_output_check: return + allow_constant_finalize = True if self.environment.finalize: - finalize = lambda x: text_type(self.environment.finalize(x)) + 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 @@ -1232,6 +1245,8 @@ 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) @@ -1287,6 +1302,9 @@ 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) @@ -1309,7 +1327,6 @@ arguments.append(item) self.writeline('yield ') self.write(repr(concat(format)) + ' % (') - idx = -1 self.indent() for argument in arguments: self.newline(argument) @@ -1323,6 +1340,15 @@ 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 + ', ') @@ -1332,42 +1358,62 @@ if outdent_later: self.outdent() - def visit_Assign(self, node, frame): - self.newline(node) + def make_assignment_frame(self, frame): # toplevel assignments however go into the local namespace and # the current template's context. We create a copy of the frame # here and add a set so that the Name visitor can add the assigned # names here. - if frame.toplevel: - assignment_frame = frame.copy() - assignment_frame.toplevel_assignments = set() + if not frame.toplevel: + return frame + assignment_frame = frame.copy() + assignment_frame.toplevel_assignments = set() + return assignment_frame + + def export_assigned_vars(self, frame, assignment_frame): + if not frame.toplevel: + return + public_names = [x for x in assignment_frame.toplevel_assignments + if not x.startswith('_')] + if len(assignment_frame.toplevel_assignments) == 1: + name = next(iter(assignment_frame.toplevel_assignments)) + self.writeline('context.vars[%r] = l_%s' % (name, name)) else: - assignment_frame = frame + self.writeline('context.vars.update({') + for idx, name in enumerate(assignment_frame.toplevel_assignments): + if idx: + self.write(', ') + self.write('%r: l_%s' % (name, name)) + 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))) + + def visit_Assign(self, node, frame): + self.newline(node) + assignment_frame = self.make_assignment_frame(frame) self.visit(node.target, assignment_frame) self.write(' = ') self.visit(node.node, frame) + self.export_assigned_vars(frame, assignment_frame) - # make sure toplevel assignments are added to the context. - if frame.toplevel: - public_names = [x for x in assignment_frame.toplevel_assignments - if not x.startswith('_')] - if len(assignment_frame.toplevel_assignments) == 1: - name = next(iter(assignment_frame.toplevel_assignments)) - self.writeline('context.vars[%r] = l_%s' % (name, name)) - else: - self.writeline('context.vars.update({') - for idx, name in enumerate(assignment_frame.toplevel_assignments): - if idx: - self.write(', ') - self.write('%r: l_%s' % (name, name)) - 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))) + def visit_AssignBlock(self, node, frame): + block_frame = frame.inner() + block_frame.inspect(node.body) + aliases = self.push_scope(block_frame) + self.pull_locals(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.pop_scope(aliases, block_frame) + + assignment_frame = self.make_assignment_frame(frame) + self.newline(node) + self.visit(node.target, assignment_frame) + self.write(' = concat(%s)' % block_frame.buffer) + self.export_assigned_vars(frame, assignment_frame) # -- Expression Visitors diff -Nru jinja2-2.7.3/jinja2/debug.py jinja2-2.8/jinja2/debug.py --- jinja2-2.7.3/jinja2/debug.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/debug.py 2015-05-25 10:25:02.000000000 +0000 @@ -12,10 +12,10 @@ """ import sys import traceback -from types import TracebackType +from types import TracebackType, CodeType from jinja2.utils import missing, internal_code from jinja2.exceptions import TemplateSyntaxError -from jinja2._compat import iteritems, reraise, code_type +from jinja2._compat import iteritems, reraise, PY2 # on pypy we can take advantage of transparent proxies try: @@ -245,12 +245,21 @@ location = 'block "%s"' % function[6:] else: location = 'template' - code = code_type(0, code.co_nlocals, code.co_stacksize, - code.co_flags, code.co_code, code.co_consts, - code.co_names, code.co_varnames, filename, - location, code.co_firstlineno, - code.co_lnotab, (), ()) - except: + + if PY2: + code = CodeType(0, code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + else: + code = CodeType(0, code.co_kwonlyargcount, + code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + except Exception as e: pass # execute the code and catch the new traceback @@ -273,11 +282,15 @@ import ctypes from types import TracebackType - # figure out side of _Py_ssize_t - if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): - _Py_ssize_t = ctypes.c_int64 + if PY2: + # figure out size of _Py_ssize_t for Python 2: + if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): + _Py_ssize_t = ctypes.c_int64 + else: + _Py_ssize_t = ctypes.c_int else: - _Py_ssize_t = ctypes.c_int + # platform ssize_t on Python 3 + _Py_ssize_t = ctypes.c_ssize_t # regular python class _PyObject(ctypes.Structure): diff -Nru jinja2-2.7.3/jinja2/defaults.py jinja2-2.8/jinja2/defaults.py --- jinja2-2.7.3/jinja2/defaults.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/defaults.py 2015-05-25 09:40:18.000000000 +0000 @@ -32,7 +32,7 @@ from jinja2.tests import TESTS as DEFAULT_TESTS DEFAULT_NAMESPACE = { 'range': range_type, - 'dict': lambda **kw: kw, + 'dict': dict, 'lipsum': generate_lorem_ipsum, 'cycler': Cycler, 'joiner': Joiner diff -Nru jinja2-2.7.3/jinja2/environment.py jinja2-2.8/jinja2/environment.py --- jinja2-2.7.3/jinja2/environment.py 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/jinja2/environment.py 2015-05-25 09:40:18.000000000 +0000 @@ -21,8 +21,8 @@ from jinja2.parser import Parser from jinja2.nodes import EvalContext from jinja2.optimizer import optimize -from jinja2.compiler import generate -from jinja2.runtime import Undefined, new_context +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, \ @@ -90,13 +90,13 @@ 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.' + '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' + 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.' + 'newline_sequence set to unknown line ending string.' return environment @@ -108,16 +108,16 @@ Modifications on environments after the first template was loaded will lead to surprising effects and undefined behavior. - Here the possible initialization parameters: + Here are the possible initialization parameters: `block_start_string` - The string marking the begin of a block. Defaults to ``'{%'``. + 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 begin of a print statement. + The string marking the beginning of a print statement. Defaults to ``'{{'``. `variable_end_string` @@ -125,7 +125,7 @@ ``'}}'``. `comment_start_string` - The string marking the begin of a comment. Defaults to ``'{#'``. + The string marking the beginning of a comment. Defaults to ``'{#'``. `comment_end_string` The string marking the end of a comment. Defaults to ``'#}'``. @@ -136,7 +136,7 @@ `line_comment_prefix` If given and a string, this will be used as prefix for line based - based comments. See also :ref:`line-statements`. + comments. See also :ref:`line-statements`. .. versionadded:: 2.2 @@ -180,7 +180,7 @@ `autoescape` If set to true the XML/HTML autoescaping feature is enabled by - default. For more details about auto escaping see + 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 @@ -193,12 +193,15 @@ The template loader for this environment. `cache_size` - The size of the cache. Per default this is ``50`` which means - that if more than 50 templates are loaded the loader will clean + 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 @@ -235,6 +238,14 @@ 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, @@ -254,7 +265,7 @@ finalize=None, autoescape=False, loader=None, - cache_size=50, + cache_size=400, auto_reload=True, bytecode_cache=None): # !!Important notice!! @@ -330,7 +341,7 @@ 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 of cache and the overridden attributes. + 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. @@ -551,7 +562,7 @@ return self._compile(source, filename) except TemplateSyntaxError: exc_info = sys.exc_info() - self.handle_exception(exc_info, source_hint=source) + self.handle_exception(exc_info, source_hint=source_hint) def compile_expression(self, source, undefined_to_none=True): """A handy helper method that returns a callable that accepts keyword @@ -603,8 +614,8 @@ ignore_errors=True, py_compile=False): """Finds all the templates the loader can find, compiles them and stores them in `target`. If `zip` is `None`, instead of in a - zipfile, the templates will be will be stored in a directory. - By default a deflate zip algorithm is used, to switch to + zipfile, the templates will be stored in a directory. + By default a deflate zip algorithm is used. To switch to the stored algorithm, `zip` can be set to ``'stored'``. `extensions` and `filter_func` are passed to :meth:`list_templates`. @@ -634,7 +645,8 @@ warn(Warning('py_compile has no effect on pypy or Python 3')) py_compile = False else: - import imp, marshal + import imp + import marshal py_header = imp.get_magic() + \ u'\xff\xff\xff\xff'.encode('iso-8859-15') @@ -716,7 +728,7 @@ filter_func = lambda x: '.' in x and \ x.rsplit('.', 1)[1] in extensions if filter_func is not None: - x = ifilter(filter_func, x) + x = list(ifilter(filter_func, x)) return x def handle_exception(self, exc_info=None, rendered=False, source_hint=None): @@ -757,14 +769,23 @@ def _load_template(self, name, globals): if self.loader is None: raise TypeError('no loader for this environment specified') + try: + # use abs path for cache key + cache_key = self.loader.get_source(self, name)[1] + except RuntimeError: + # if loader does not implement get_source() + cache_key = None + # if template is not file, use name for cache key + if cache_key is None: + cache_key = name if self.cache is not None: - template = self.cache.get(name) - if template is not None and (not self.auto_reload or \ + template = self.cache.get(cache_key) + if template is not None and (not self.auto_reload or template.is_up_to_date): return template template = self.loader.load(self, name, globals) if self.cache is not None: - self.cache[name] = template + self.cache[cache_key] = template return template @internalcode @@ -866,13 +887,12 @@ and compatible settings. >>> template = Template('Hello {{ name }}!') - >>> template.render(name='John Doe') - u'Hello John Doe!' - + >>> template.render(name='John Doe') == u'Hello John Doe!' + True >>> stream = template.stream(name='John Doe') - >>> stream.next() - u'Hello John Doe!' - >>> stream.next() + >>> next(stream) == u'Hello John Doe!' + True + >>> next(stream) Traceback (most recent call last): ... StopIteration @@ -1019,10 +1039,10 @@ exported template variables from the Python layer: >>> t = Template('{% macro foo() %}42{% endmacro %}23') - >>> unicode(t.module) - u'23' - >>> t.module.foo() - u'42' + >>> str(t.module) + '23' + >>> t.module.foo() == u'42' + True """ if self._module is not None: return self._module @@ -1131,7 +1151,9 @@ """ close = False if isinstance(fp, string_types): - fp = open(fp, encoding is None and 'w' or 'wb') + if encoding is None: + encoding = 'utf-8' + fp = open(fp, 'wb') close = True try: if encoding is not None: diff -Nru jinja2-2.7.3/jinja2/ext.py jinja2-2.8/jinja2/ext.py --- jinja2-2.7.3/jinja2/ext.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/ext.py 2014-08-08 14:42:36.000000000 +0000 @@ -20,7 +20,7 @@ from jinja2.runtime import concat from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError from jinja2.utils import contextfunction, import_string, Markup -from jinja2._compat import next, with_metaclass, string_types, iteritems +from jinja2._compat import with_metaclass, string_types, iteritems # the only real useful gettext functions for a Jinja template. Note diff -Nru jinja2-2.7.3/jinja2/filters.py jinja2-2.8/jinja2/filters.py --- jinja2-2.7.3/jinja2/filters.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/filters.py 2015-07-26 17:43:00.000000000 +0000 @@ -18,7 +18,7 @@ unicode_urlencode from jinja2.runtime import Undefined from jinja2.exceptions import FilterArgumentError -from jinja2._compat import next, imap, string_types, text_type, iteritems +from jinja2._compat import imap, string_types, text_type, iteritems _word_re = re.compile(r'\w+(?u)') @@ -94,7 +94,8 @@ if itemiter is None: return unicode_urlencode(value) return u'&'.join(unicode_urlencode(k) + '=' + - unicode_urlencode(v) for k, v in itemiter) + unicode_urlencode(v, for_qs=True) + for k, v in itemiter) @evalcontextfilter @@ -183,7 +184,7 @@ uppercase letters, all remaining characters are lowercase. """ rv = [] - for item in re.compile(r'([-\s]+)(?u)').split(s): + for item in re.compile(r'([-\s]+)(?u)').split(soft_unicode(s)): if not item: continue rv.append(item[0].upper() + item[1:].lower()) @@ -204,8 +205,7 @@ sort the dict by key, case sensitive {% for item in mydict|dictsort(false, 'value') %} - sort the dict by key, case insensitive, sorted - normally and ordered by value. + sort the dict by value, case insensitive """ if by == 'key': pos = 0 @@ -409,7 +409,8 @@ @evalcontextfilter -def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False): +def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False, + target=None): """Converts URLs in plain text into clickable links. If you pass the filter an additional integer it will shorten the urls @@ -420,8 +421,18 @@ {{ mytext|urlize(40, true) }} links are shortened to 40 chars and defined with rel="nofollow" + + If *target* is specified, the ``target`` attribute will be added to the + ```` tag: + + .. sourcecode:: jinja + + {{ mytext|urlize(40, target='_blank') }} + + .. versionchanged:: 2.8+ + The *target* parameter was added. """ - rv = urlize(value, trim_url_limit, nofollow) + rv = urlize(value, trim_url_limit, nofollow, target) if eval_ctx.autoescape: rv = Markup(rv) return rv @@ -456,25 +467,22 @@ .. sourcecode:: jinja - {{ "foo bar"|truncate(5) }} + {{ "foo bar baz"|truncate(9) }} -> "foo ..." - {{ "foo bar"|truncate(5, True) }} - -> "foo b..." + {{ "foo bar baz"|truncate(9, True) }} + -> "foo ba..." + """ if len(s) <= length: return s elif killwords: - return s[:length] + end - words = s.split(' ') - result = [] - m = 0 - for word in words: - m += len(word) + 1 - if m > length: - break - result.append(word) - result.append(end) - return u' '.join(result) + return s[:length - len(end)] + end + + result = s[:length - len(end)].rsplit(' ', 1)[0] + if len(result) < length: + result += ' ' + return result + end + @environmentfilter def do_wordwrap(environment, s, width=79, break_long_words=True, @@ -503,13 +511,16 @@ return len(_word_re.findall(s)) -def do_int(value, default=0): +def do_int(value, default=0, base=10): """Convert the value into an integer. If the conversion doesn't work it will return ``0``. You can - override this default using the first parameter. + override this default using the first parameter. You + can also override the default base (10) in the second + parameter, which handles input with prefixes such as + 0b, 0o and 0x for bases 2, 8 and 16 respectively. """ try: - return int(value) + return int(value, base) except (TypeError, ValueError): # this quirk is necessary so that "42.23"|int gives 42. try: @@ -612,7 +623,6 @@ {%- endfor %} """ - result = [] tmp = [] for item in value: if len(tmp) == linecount: @@ -753,7 +763,7 @@ def do_reverse(value): - """Reverse the object or return an iterator the iterates over it the other + """Reverse the object or return an iterator that iterates over it the other way round. """ if isinstance(value, string_types): @@ -772,7 +782,7 @@ @environmentfilter def do_attr(environment, obj, name): """Get an attribute of an object. ``foo|attr("bar")`` works like - ``foo["bar"]`` just that always an attribute is returned and items are not + ``foo.bar`` just that always an attribute is returned and items are not looked up. See :ref:`Notes on subscriptions ` for more details. @@ -842,14 +852,15 @@ @contextfilter def do_select(*args, **kwargs): - """Filters a sequence of objects by appying a test to either the object - or the attribute and only selecting the ones with the test succeeding. + """Filters a sequence of objects by applying a test to the object and only + selecting the ones with the test succeeding. Example usage: .. sourcecode:: jinja {{ numbers|select("odd") }} + {{ numbers|select("odd") }} .. versionadded:: 2.7 """ @@ -858,8 +869,8 @@ @contextfilter def do_reject(*args, **kwargs): - """Filters a sequence of objects by appying a test to either the object - or the attribute and rejecting the ones with the test succeeding. + """Filters a sequence of objects by applying a test to the object and + rejecting the ones with the test succeeding. Example usage: @@ -874,8 +885,8 @@ @contextfilter def do_selectattr(*args, **kwargs): - """Filters a sequence of objects by appying a test to either the object - or the attribute and only selecting the ones with the test succeeding. + """Filters a sequence of objects by applying a test to an attribute of an + object and only selecting the ones with the test succeeding. Example usage: @@ -891,8 +902,8 @@ @contextfilter def do_rejectattr(*args, **kwargs): - """Filters a sequence of objects by appying a test to either the object - or the attribute and rejecting the ones with the test succeeding. + """Filters a sequence of objects by applying a test to an attribute of an + object or the attribute and rejecting the ones with the test succeeding. .. sourcecode:: jinja @@ -933,55 +944,53 @@ FILTERS = { + 'abs': abs, 'attr': do_attr, - 'replace': do_replace, - 'upper': do_upper, - 'lower': do_lower, - 'escape': escape, - 'e': escape, - 'forceescape': do_forceescape, + 'batch': do_batch, 'capitalize': do_capitalize, - 'title': do_title, - 'default': do_default, - 'd': do_default, - 'join': do_join, + 'center': do_center, 'count': len, + 'd': do_default, + 'default': do_default, 'dictsort': do_dictsort, - 'sort': do_sort, - 'length': len, - 'reverse': do_reverse, - 'center': do_center, - 'indent': do_indent, - 'title': do_title, - 'capitalize': do_capitalize, + 'e': escape, + 'escape': escape, + 'filesizeformat': do_filesizeformat, 'first': do_first, + 'float': do_float, + 'forceescape': do_forceescape, + 'format': do_format, + 'groupby': do_groupby, + 'indent': do_indent, + 'int': do_int, + 'join': do_join, 'last': do_last, + 'length': len, + 'list': do_list, + 'lower': do_lower, 'map': do_map, + 'pprint': do_pprint, 'random': do_random, 'reject': do_reject, 'rejectattr': do_rejectattr, - 'filesizeformat': do_filesizeformat, - 'pprint': do_pprint, - 'truncate': do_truncate, - 'wordwrap': do_wordwrap, - 'wordcount': do_wordcount, - 'int': do_int, - 'float': do_float, - 'string': soft_unicode, - 'list': do_list, - 'urlize': do_urlize, - 'format': do_format, - 'trim': do_trim, - 'striptags': do_striptags, + 'replace': do_replace, + 'reverse': do_reverse, + 'round': do_round, + 'safe': do_mark_safe, 'select': do_select, 'selectattr': do_selectattr, 'slice': do_slice, - 'batch': do_batch, + 'sort': do_sort, + 'string': soft_unicode, + 'striptags': do_striptags, 'sum': do_sum, - 'abs': abs, - 'round': do_round, - 'groupby': do_groupby, - 'safe': do_mark_safe, + 'title': do_title, + 'trim': do_trim, + 'truncate': do_truncate, + 'upper': do_upper, + 'urlencode': do_urlencode, + 'urlize': do_urlize, + 'wordcount': do_wordcount, + 'wordwrap': do_wordwrap, 'xmlattr': do_xmlattr, - 'urlencode': do_urlencode } diff -Nru jinja2-2.7.3/jinja2/__init__.py jinja2-2.8/jinja2/__init__.py --- jinja2-2.7.3/jinja2/__init__.py 2014-06-06 16:49:12.000000000 +0000 +++ jinja2-2.8/jinja2/__init__.py 2015-07-26 17:49:40.000000000 +0000 @@ -27,7 +27,7 @@ :license: BSD, see LICENSE for more details. """ __docformat__ = 'restructuredtext en' -__version__ = '2.7.3' +__version__ = '2.8' # high level interface from jinja2.environment import Environment, Template @@ -42,7 +42,8 @@ MemcachedBytecodeCache # undefined types -from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined +from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \ + make_logging_undefined # exceptions from jinja2.exceptions import TemplateError, UndefinedError, \ @@ -65,5 +66,5 @@ 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', - 'evalcontextfilter', 'evalcontextfunction' + 'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined', ] diff -Nru jinja2-2.7.3/jinja2/lexer.py jinja2-2.8/jinja2/lexer.py --- jinja2-2.7.3/jinja2/lexer.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/lexer.py 2014-08-08 14:42:36.000000000 +0000 @@ -20,8 +20,8 @@ from collections import deque from jinja2.exceptions import TemplateSyntaxError from jinja2.utils import LRUCache -from jinja2._compat import next, iteritems, implements_iterator, text_type, \ - intern +from jinja2._compat import iteritems, implements_iterator, text_type, \ + intern, PY2 # cache for the lexers. Exists in order to be able to have multiple @@ -136,8 +136,8 @@ ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT, TOKEN_COMMENT_END, TOKEN_WHITESPACE, - TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN, - TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT]) + TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END, + TOKEN_LINECOMMENT]) ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, TOKEN_COMMENT, TOKEN_LINECOMMENT]) @@ -578,10 +578,11 @@ # we do that for support of semi broken APIs # as datetime.datetime.strftime. On python 3 this # call becomes a noop thanks to 2to3 - try: - value = str(value) - except UnicodeError: - pass + if PY2: + try: + value = value.encode('ascii') + except UnicodeError: + pass elif token == 'integer': value = int(value) elif token == 'float': diff -Nru jinja2-2.7.3/jinja2/loaders.py jinja2-2.8/jinja2/loaders.py --- jinja2-2.7.3/jinja2/loaders.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/loaders.py 2015-07-26 17:43:00.000000000 +0000 @@ -141,20 +141,28 @@ 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: + 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'): + 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) @@ -169,6 +177,7 @@ f.close() mtime = path.getmtime(filename) + def uptodate(): try: return path.getmtime(filename) == mtime @@ -180,7 +189,8 @@ def list_templates(self): found = set() for searchpath in self.searchpath: - for dirpath, dirnames, filenames in os.walk(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) \ @@ -281,7 +291,7 @@ class FunctionLoader(BaseLoader): """A loader that is passed a function which does the loading. The - function becomes the name of the template passed and has to return either + 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. diff -Nru jinja2-2.7.3/jinja2/meta.py jinja2-2.8/jinja2/meta.py --- jinja2-2.7.3/jinja2/meta.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/meta.py 2015-05-25 09:40:18.000000000 +0000 @@ -39,8 +39,8 @@ >>> from jinja2 import Environment, meta >>> env = Environment() >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') - >>> meta.find_undeclared_variables(ast) - set(['bar']) + >>> meta.find_undeclared_variables(ast) == set(['bar']) + True .. admonition:: Implementation diff -Nru jinja2-2.7.3/jinja2/nodes.py jinja2-2.8/jinja2/nodes.py --- jinja2-2.7.3/jinja2/nodes.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/nodes.py 2014-08-08 14:42:36.000000000 +0000 @@ -12,16 +12,16 @@ :copyright: (c) 2010 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 next, izip, with_metaclass, text_type, \ - method_type, function_type +from jinja2._compat import izip, with_metaclass, text_type #: the types we support for context functions -_context_function_types = (function_type, method_type) +_context_function_types = (types.FunctionType, types.MethodType) _binop_to_func = { @@ -347,6 +347,11 @@ fields = ('target', 'node') +class AssignBlock(Stmt): + """Assigns a block to a target.""" + fields = ('target', 'body') + + class Expr(Node): """Baseclass for all expressions.""" abstract = True @@ -746,7 +751,7 @@ class Sub(BinExpr): - """Substract the right from the left node.""" + """Subtract the right from the left node.""" operator = '-' diff -Nru jinja2-2.7.3/jinja2/parser.py jinja2-2.8/jinja2/parser.py --- jinja2-2.7.3/jinja2/parser.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/parser.py 2015-05-25 09:40:18.000000000 +0000 @@ -11,10 +11,9 @@ from jinja2 import nodes from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError from jinja2.lexer import describe_token, describe_token_expr -from jinja2._compat import next, imap +from jinja2._compat import imap -#: statements that callinto _statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print', 'macro', 'include', 'from', 'import', 'set']) @@ -169,9 +168,12 @@ """Parse an assign statement.""" lineno = next(self.stream).lineno target = self.parse_assign_target() - self.stream.expect('assign') - expr = self.parse_tuple() - return nodes.Assign(target, expr, lineno=lineno) + if self.stream.skip_if('assign'): + expr = self.parse_tuple() + return nodes.Assign(target, expr, lineno=lineno) + body = self.parse_statements(('name:endset',), + drop_needle=True) + return nodes.AssignBlock(target, body, lineno=lineno) def parse_for(self): """Parse a for loop.""" @@ -312,6 +314,8 @@ arg.set_ctx('param') if self.stream.skip_if('assign'): defaults.append(self.parse_expression()) + elif defaults: + self.fail('non-default argument follows default argument') args.append(arg) self.stream.expect('rparen') @@ -434,8 +438,8 @@ ops.append(nodes.Operand(token_type, self.parse_add())) elif self.stream.skip_if('name:in'): ops.append(nodes.Operand('in', self.parse_add())) - elif self.stream.current.test('name:not') and \ - self.stream.look().test('name:in'): + elif (self.stream.current.test('name:not') and + self.stream.look().test('name:in')): self.stream.skip(2) ops.append(nodes.Operand('notin', self.parse_add())) else: @@ -771,7 +775,7 @@ else: ensure(dyn_args is None and dyn_kwargs is None) if self.stream.current.type == 'name' and \ - self.stream.look().type == 'assign': + self.stream.look().type == 'assign': key = self.stream.current.value self.stream.skip(2) value = self.parse_expression() @@ -824,11 +828,11 @@ kwargs = [] if self.stream.current.type == 'lparen': args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) - elif self.stream.current.type in ('name', 'string', 'integer', - 'float', 'lparen', 'lbracket', - 'lbrace') and not \ - self.stream.current.test_any('name:else', 'name:or', - 'name:and'): + elif (self.stream.current.type in ('name', 'string', 'integer', + 'float', 'lparen', 'lbracket', + 'lbrace') and not + self.stream.current.test_any('name:else', 'name:or', + 'name:and')): if self.stream.current.test('name:is'): self.fail('You cannot chain multiple tests with is') args = [self.parse_expression()] diff -Nru jinja2-2.7.3/jinja2/runtime.py jinja2-2.8/jinja2/runtime.py --- jinja2-2.7.3/jinja2/runtime.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/runtime.py 2015-05-25 09:40:18.000000000 +0000 @@ -8,13 +8,15 @@ :copyright: (c) 2010 by the Jinja Team. :license: BSD. """ +import sys + from itertools import chain from jinja2.nodes import EvalContext, _context_function_types from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ internalcode, object_type_repr from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ TemplateNotFound -from jinja2._compat import next, imap, text_type, iteritems, \ +from jinja2._compat import imap, text_type, iteritems, \ implements_iterator, implements_to_string, string_types, PY2 @@ -22,7 +24,7 @@ __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', 'TemplateRuntimeError', 'missing', 'concat', 'escape', 'markup_join', 'unicode_join', 'to_string', 'identity', - 'TemplateNotFound'] + 'TemplateNotFound', 'make_logging_undefined'] #: the name of the function that is used to convert something into #: a string. We can just use the text type here. @@ -67,7 +69,8 @@ for key, value in iteritems(locals): if key[:2] == 'l_' and value is not missing: parent[key[2:]] = value - return Context(environment, parent, template_name, blocks) + return environment.context_class(environment, parent, template_name, + blocks) class TemplateReference(object): @@ -171,7 +174,7 @@ :func:`environmentfunction`. """ if __debug__: - __traceback_hide__ = True + __traceback_hide__ = True # noqa # Allow callable classes to take a context fn = __obj.__call__ @@ -339,10 +342,11 @@ # if was not possible to get the length of the iterator when # the loop context was created (ie: iterating over a generator) # we have to convert the iterable into a sequence and use the - # length of that. + # length of that + the number of iterations so far. iterable = tuple(self._iterator) self._iterator = iter(iterable) - self._length = len(iterable) + self.index0 + 1 + iterations_done = self.index0 + 2 + self._length = len(iterable) + iterations_done return self._length def __repr__(self): @@ -441,7 +445,7 @@ @implements_to_string class Undefined(object): """The default undefined type. This undefined type can be printed and - iterated over, but every other access will raise an :exc:`UndefinedError`: + iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: >>> foo = Undefined(name='foo') >>> str(foo) @@ -451,7 +455,7 @@ >>> foo + 42 Traceback (most recent call last): ... - UndefinedError: 'foo' is undefined + jinja2.exceptions.UndefinedError: 'foo' is undefined """ __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', '_undefined_exception') @@ -465,7 +469,7 @@ @internalcode def _fail_with_undefined_error(self, *args, **kwargs): """Regular callback function for undefined objects that raises an - `UndefinedError` on call. + `jinja2.exceptions.UndefinedError` on call. """ if self._undefined_hint is None: if self._undefined_obj is missing: @@ -491,10 +495,10 @@ return self._fail_with_undefined_error() __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ - __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ - __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ - __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ - __float__ = __complex__ = __pow__ = __rpow__ = \ + __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ + __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ + __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ + __float__ = __complex__ = __pow__ = __rpow__ = \ _fail_with_undefined_error def __eq__(self, other): @@ -518,11 +522,93 @@ def __nonzero__(self): return False + __bool__ = __nonzero__ def __repr__(self): return 'Undefined' +def make_logging_undefined(logger=None, base=None): + """Given a logger object this returns a new undefined class that will + log certain failures. It will log iterations and printing. If no + logger is given a default logger is created. + + Example:: + + logger = logging.getLogger(__name__) + LoggingUndefined = make_logging_undefined( + logger=logger, + base=Undefined + ) + + .. versionadded:: 2.8 + + :param logger: the logger to use. If not provided, a default logger + is created. + :param base: the base class to add logging functionality to. This + defaults to :class:`Undefined`. + """ + if logger is None: + import logging + logger = logging.getLogger(__name__) + logger.addHandler(logging.StreamHandler(sys.stderr)) + if base is None: + base = Undefined + + def _log_message(undef): + if undef._undefined_hint is None: + if undef._undefined_obj is missing: + hint = '%s is undefined' % undef._undefined_name + elif not isinstance(undef._undefined_name, string_types): + hint = '%s has no element %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = '%s has no attribute %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = undef._undefined_hint + logger.warning('Template variable warning: %s', hint) + + class LoggingUndefined(base): + + def _fail_with_undefined_error(self, *args, **kwargs): + try: + return base._fail_with_undefined_error(self, *args, **kwargs) + except self._undefined_exception as e: + logger.error('Template variable error: %s', str(e)) + raise e + + def __str__(self): + rv = base.__str__(self) + _log_message(self) + return rv + + def __iter__(self): + rv = base.__iter__(self) + _log_message(self) + return rv + + if PY2: + def __nonzero__(self): + rv = base.__nonzero__(self) + _log_message(self) + return rv + + def __unicode__(self): + rv = base.__unicode__(self) + _log_message(self) + return rv + else: + def __bool__(self): + rv = base.__bool__(self) + _log_message(self) + return rv + + return LoggingUndefined + + @implements_to_string class DebugUndefined(Undefined): """An undefined that returns the debug info when printed. @@ -535,7 +621,7 @@ >>> foo + 42 Traceback (most recent call last): ... - UndefinedError: 'foo' is undefined + jinja2.exceptions.UndefinedError: 'foo' is undefined """ __slots__ = () @@ -560,15 +646,15 @@ >>> str(foo) Traceback (most recent call last): ... - UndefinedError: 'foo' is undefined + jinja2.exceptions.UndefinedError: 'foo' is undefined >>> not foo Traceback (most recent call last): ... - UndefinedError: 'foo' is undefined + jinja2.exceptions.UndefinedError: 'foo' is undefined >>> foo + 42 Traceback (most recent call last): ... - UndefinedError: 'foo' is undefined + jinja2.exceptions.UndefinedError: 'foo' is undefined """ __slots__ = () __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ diff -Nru jinja2-2.7.3/jinja2/sandbox.py jinja2-2.8/jinja2/sandbox.py --- jinja2-2.7.3/jinja2/sandbox.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/sandbox.py 2015-05-25 09:40:18.000000000 +0000 @@ -12,19 +12,25 @@ :copyright: (c) 2010 by the Jinja Team. :license: BSD. """ +import types import operator from jinja2.environment import Environment from jinja2.exceptions import SecurityError -from jinja2._compat import string_types, function_type, method_type, \ - traceback_type, code_type, frame_type, generator_type, PY2 +from jinja2._compat import string_types, PY2 #: maximum number of items a range may produce MAX_RANGE = 100000 #: attributes of function objects that are considered unsafe. -UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', - 'func_defaults', 'func_globals']) +if PY2: + UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', + 'func_defaults', 'func_globals']) +else: + # On versions > python 2 the special attributes on functions are gone, + # but they remain on methods and generators for whatever reason. + UNSAFE_FUNCTION_ATTRIBUTES = set() + #: unsafe method attributes. function attributes are unsafe for methods too UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) @@ -32,11 +38,6 @@ #: unsafe generator attirbutes. UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) -# On versions > python 2 the special attributes on functions are gone, -# but they remain on methods and generators for whatever reason. -if not PY2: - UNSAFE_FUNCTION_ATTRIBUTES = set() - import warnings # make sure we don't warn in python 2.6 about stuff we don't care about @@ -124,26 +125,24 @@ :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. >>> from jinja2.sandbox import is_internal_attribute - >>> is_internal_attribute(lambda: None, "func_code") - True - >>> is_internal_attribute((lambda x:x).func_code, 'co_code') + >>> is_internal_attribute(str, "mro") True >>> is_internal_attribute(str, "upper") False """ - if isinstance(obj, function_type): + if isinstance(obj, types.FunctionType): if attr in UNSAFE_FUNCTION_ATTRIBUTES: return True - elif isinstance(obj, method_type): + elif isinstance(obj, types.MethodType): if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ attr in UNSAFE_METHOD_ATTRIBUTES: return True elif isinstance(obj, type): if attr == 'mro': return True - elif isinstance(obj, (code_type, traceback_type, frame_type)): + elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)): return True - elif isinstance(obj, generator_type): + elif isinstance(obj, types.GeneratorType): if attr in UNSAFE_GENERATOR_ATTRIBUTES: return True return attr.startswith('__') diff -Nru jinja2-2.7.3/jinja2/tests.py jinja2-2.8/jinja2/tests.py --- jinja2-2.7.3/jinja2/tests.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/tests.py 2015-05-25 09:40:18.000000000 +0000 @@ -9,9 +9,10 @@ :license: BSD, see LICENSE for more details. """ import re +from collections import Mapping from jinja2.runtime import Undefined -from jinja2._compat import text_type, string_types, mapping_types - +from jinja2._compat import text_type, string_types, integer_types +import decimal number_re = re.compile(r'^-?\d+(\.\d+)?$') regex_type = type(number_re) @@ -82,12 +83,12 @@ .. versionadded:: 2.6 """ - return isinstance(value, mapping_types) + return isinstance(value, Mapping) def test_number(value): """Return true if the variable is a number.""" - return isinstance(value, (int, float, complex)) + return isinstance(value, integer_types + (float, complex, decimal.Decimal)) def test_sequence(value): @@ -102,6 +103,28 @@ return True +def test_equalto(value, other): + """Check if an object has the same value as another object: + + .. sourcecode:: jinja + + {% if foo.expression is equalto 42 %} + the foo attribute evaluates to the constant 42 + {% endif %} + + This appears to be a useless test as it does exactly the same as the + ``==`` operator, but it can be useful when used together with the + `selectattr` function: + + .. sourcecode:: jinja + + {{ users|selectattr("email", "equalto", "foo@bar.invalid") }} + + .. versionadded:: 2.8 + """ + return value == other + + def test_sameas(value, other): """Check if an object points to the same memory address than another object: @@ -145,5 +168,6 @@ 'iterable': test_iterable, 'callable': test_callable, 'sameas': test_sameas, + 'equalto': test_equalto, 'escaped': test_escaped } diff -Nru jinja2-2.7.3/jinja2/testsuite/api.py jinja2-2.8/jinja2/testsuite/api.py --- jinja2-2.7.3/jinja2/testsuite/api.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/api.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.api - ~~~~~~~~~~~~~~~~~~~~ - - Tests the public API and related stuff. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest -import os -import tempfile -import shutil - -from jinja2.testsuite import JinjaTestCase -from jinja2._compat import next - -from jinja2 import Environment, Undefined, DebugUndefined, \ - StrictUndefined, UndefinedError, meta, \ - is_undefined, Template, DictLoader -from jinja2.utils import Cycler - -env = Environment() - - -class ExtendedAPITestCase(JinjaTestCase): - - def test_item_and_attribute(self): - from jinja2.sandbox import SandboxedEnvironment - - for env in Environment(), SandboxedEnvironment(): - # the |list is necessary for python3 - tmpl = env.from_string('{{ foo.items()|list }}') - assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" - tmpl = env.from_string('{{ foo|attr("items")()|list }}') - assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" - tmpl = env.from_string('{{ foo["items"] }}') - assert tmpl.render(foo={'items': 42}) == '42' - - def test_finalizer(self): - def finalize_none_empty(value): - if value is None: - value = u'' - return value - env = Environment(finalize=finalize_none_empty) - tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}') - assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo' - tmpl = env.from_string('<{{ none }}>') - assert tmpl.render() == '<>' - - def test_cycler(self): - items = 1, 2, 3 - c = Cycler(*items) - for item in items + items: - assert c.current == item - assert next(c) == item - next(c) - assert c.current == 2 - c.reset() - assert c.current == 1 - - def test_expressions(self): - expr = env.compile_expression("foo") - assert expr() is None - assert expr(foo=42) == 42 - expr2 = env.compile_expression("foo", undefined_to_none=False) - assert is_undefined(expr2()) - - expr = env.compile_expression("42 + foo") - assert expr(foo=42) == 84 - - def test_template_passthrough(self): - t = Template('Content') - assert env.get_template(t) is t - assert env.select_template([t]) is t - assert env.get_or_select_template([t]) is t - assert env.get_or_select_template(t) is t - - def test_autoescape_autoselect(self): - def select_autoescape(name): - if name is None or '.' not in name: - return False - return name.endswith('.html') - env = Environment(autoescape=select_autoescape, - loader=DictLoader({ - 'test.txt': '{{ foo }}', - 'test.html': '{{ foo }}' - })) - t = env.get_template('test.txt') - assert t.render(foo='') == '' - t = env.get_template('test.html') - assert t.render(foo='') == '<foo>' - t = env.from_string('{{ foo }}') - assert t.render(foo='') == '' - - -class MetaTestCase(JinjaTestCase): - - def test_find_undeclared_variables(self): - ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') - x = meta.find_undeclared_variables(ast) - assert x == set(['bar']) - - ast = env.parse('{% set foo = 42 %}{{ bar + foo }}' - '{% macro meh(x) %}{{ x }}{% endmacro %}' - '{% for item in seq %}{{ muh(item) + meh(seq) }}{% endfor %}') - x = meta.find_undeclared_variables(ast) - assert x == set(['bar', 'seq', 'muh']) - - def test_find_refererenced_templates(self): - ast = env.parse('{% extends "layout.html" %}{% include helper %}') - i = meta.find_referenced_templates(ast) - assert next(i) == 'layout.html' - assert next(i) is None - assert list(i) == [] - - ast = env.parse('{% extends "layout.html" %}' - '{% from "test.html" import a, b as c %}' - '{% import "meh.html" as meh %}' - '{% include "muh.html" %}') - i = meta.find_referenced_templates(ast) - assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html'] - - def test_find_included_templates(self): - ast = env.parse('{% include ["foo.html", "bar.html"] %}') - i = meta.find_referenced_templates(ast) - assert list(i) == ['foo.html', 'bar.html'] - - ast = env.parse('{% include ("foo.html", "bar.html") %}') - i = meta.find_referenced_templates(ast) - assert list(i) == ['foo.html', 'bar.html'] - - ast = env.parse('{% include ["foo.html", "bar.html", foo] %}') - i = meta.find_referenced_templates(ast) - assert list(i) == ['foo.html', 'bar.html', None] - - ast = env.parse('{% include ("foo.html", "bar.html", foo) %}') - i = meta.find_referenced_templates(ast) - assert list(i) == ['foo.html', 'bar.html', None] - - -class StreamingTestCase(JinjaTestCase): - - def test_basic_streaming(self): - tmpl = env.from_string("
      {% for item in seq %}
    • {{ loop.index " - "}} - {{ item }}
    • {%- endfor %}
    ") - stream = tmpl.stream(seq=list(range(4))) - self.assert_equal(next(stream), '
      ') - self.assert_equal(next(stream), '
    • 1 - 0
    • ') - self.assert_equal(next(stream), '
    • 2 - 1
    • ') - self.assert_equal(next(stream), '
    • 3 - 2
    • ') - self.assert_equal(next(stream), '
    • 4 - 3
    • ') - self.assert_equal(next(stream), '
    ') - - def test_buffered_streaming(self): - tmpl = env.from_string("
      {% for item in seq %}
    • {{ loop.index " - "}} - {{ item }}
    • {%- endfor %}
    ") - stream = tmpl.stream(seq=list(range(4))) - stream.enable_buffering(size=3) - self.assert_equal(next(stream), u'
    • 1 - 0
    • 2 - 1
    • ') - self.assert_equal(next(stream), u'
    • 3 - 2
    • 4 - 3
    ') - - def test_streaming_behavior(self): - tmpl = env.from_string("") - stream = tmpl.stream() - assert not stream.buffered - stream.enable_buffering(20) - assert stream.buffered - stream.disable_buffering() - assert not stream.buffered - - def test_dump_stream(self): - tmp = tempfile.mkdtemp() - try: - tmpl = env.from_string(u"\u2713") - stream = tmpl.stream() - stream.dump(os.path.join(tmp, 'dump.txt'), 'utf-8') - with open(os.path.join(tmp, 'dump.txt'), 'rb') as f: - self.assertEqual(f.read(), b'\xe2\x9c\x93') - finally: - shutil.rmtree(tmp) - - -class UndefinedTestCase(JinjaTestCase): - - def test_stopiteration_is_undefined(self): - def test(): - raise StopIteration() - t = Template('A{{ test() }}B') - assert t.render(test=test) == 'AB' - t = Template('A{{ test().missingattribute }}B') - self.assert_raises(UndefinedError, t.render, test=test) - - def test_undefined_and_special_attributes(self): - try: - Undefined('Foo').__dict__ - except AttributeError: - pass - else: - assert False, "Expected actual attribute error" - - def test_default_undefined(self): - env = Environment(undefined=Undefined) - self.assert_equal(env.from_string('{{ missing }}').render(), u'') - self.assert_raises(UndefinedError, - env.from_string('{{ missing.attribute }}').render) - self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]') - self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') - self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '') - self.assert_equal(env.from_string('{{ not missing }}').render(), 'True') - - def test_debug_undefined(self): - env = Environment(undefined=DebugUndefined) - self.assert_equal(env.from_string('{{ missing }}').render(), '{{ missing }}') - self.assert_raises(UndefinedError, - env.from_string('{{ missing.attribute }}').render) - self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]') - self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') - self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), - u"{{ no such element: int object['missing'] }}") - self.assert_equal(env.from_string('{{ not missing }}').render(), 'True') - - def test_strict_undefined(self): - env = Environment(undefined=StrictUndefined) - self.assert_raises(UndefinedError, env.from_string('{{ missing }}').render) - self.assert_raises(UndefinedError, env.from_string('{{ missing.attribute }}').render) - self.assert_raises(UndefinedError, env.from_string('{{ missing|list }}').render) - self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True') - self.assert_raises(UndefinedError, env.from_string('{{ foo.missing }}').render, foo=42) - self.assert_raises(UndefinedError, env.from_string('{{ not missing }}').render) - self.assert_equal(env.from_string('{{ missing|default("default", true) }}').render(), 'default') - - def test_indexing_gives_undefined(self): - t = Template("{{ var[42].foo }}") - self.assert_raises(UndefinedError, t.render, var=0) - - def test_none_gives_proper_error(self): - try: - Environment().getattr(None, 'split')() - except UndefinedError as e: - assert e.message == "'None' has no attribute 'split'" - else: - assert False, 'expected exception' - - def test_object_repr(self): - try: - Undefined(obj=42, name='upper')() - except UndefinedError as e: - assert e.message == "'int object' has no attribute 'upper'" - else: - assert False, 'expected exception' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ExtendedAPITestCase)) - suite.addTest(unittest.makeSuite(MetaTestCase)) - suite.addTest(unittest.makeSuite(StreamingTestCase)) - suite.addTest(unittest.makeSuite(UndefinedTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/bytecode_cache.py jinja2-2.8/jinja2/testsuite/bytecode_cache.py --- jinja2-2.7.3/jinja2/testsuite/bytecode_cache.py 2013-08-07 11:31:17.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/bytecode_cache.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.bytecode_cache - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Test bytecode caching - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase, package_loader - -from jinja2 import Environment -from jinja2.bccache import FileSystemBytecodeCache -from jinja2.exceptions import TemplateNotFound - -bytecode_cache = FileSystemBytecodeCache() -env = Environment( - loader=package_loader, - bytecode_cache=bytecode_cache, -) - - -class ByteCodeCacheTestCase(JinjaTestCase): - - def test_simple(self): - tmpl = env.get_template('test.html') - assert tmpl.render().strip() == 'BAR' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ByteCodeCacheTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/core_tags.py jinja2-2.8/jinja2/testsuite/core_tags.py --- jinja2-2.7.3/jinja2/testsuite/core_tags.py 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/core_tags.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,305 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.core_tags - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Test the core tags like for and if. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment, TemplateSyntaxError, UndefinedError, \ - DictLoader - -env = Environment() - - -class ForLoopTestCase(JinjaTestCase): - - def test_simple(self): - tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}') - assert tmpl.render(seq=list(range(10))) == '0123456789' - - def test_else(self): - tmpl = env.from_string('{% for item in seq %}XXX{% else %}...{% endfor %}') - assert tmpl.render() == '...' - - def test_empty_blocks(self): - tmpl = env.from_string('<{% for item in seq %}{% else %}{% endfor %}>') - assert tmpl.render() == '<>' - - def test_context_vars(self): - tmpl = env.from_string('''{% for item in seq -%} - {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ - loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ - loop.length }}###{% endfor %}''') - one, two, _ = tmpl.render(seq=[0, 1]).split('###') - (one_index, one_index0, one_revindex, one_revindex0, one_first, - one_last, one_length) = one.split('|') - (two_index, two_index0, two_revindex, two_revindex0, two_first, - two_last, two_length) = two.split('|') - - assert int(one_index) == 1 and int(two_index) == 2 - assert int(one_index0) == 0 and int(two_index0) == 1 - assert int(one_revindex) == 2 and int(two_revindex) == 1 - assert int(one_revindex0) == 1 and int(two_revindex0) == 0 - assert one_first == 'True' and two_first == 'False' - assert one_last == 'False' and two_last == 'True' - assert one_length == two_length == '2' - - def test_cycling(self): - tmpl = env.from_string('''{% for item in seq %}{{ - loop.cycle('<1>', '<2>') }}{% endfor %}{% - for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''') - output = tmpl.render(seq=list(range(4)), through=('<1>', '<2>')) - assert output == '<1><2>' * 4 - - def test_scope(self): - tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}') - output = tmpl.render(seq=list(range(10))) - assert not output - - def test_varlen(self): - def inner(): - for item in range(5): - yield item - tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}') - output = tmpl.render(iter=inner()) - assert output == '01234' - - def test_noniter(self): - tmpl = env.from_string('{% for item in none %}...{% endfor %}') - self.assert_raises(TypeError, tmpl.render) - - def test_recursive(self): - tmpl = env.from_string('''{% for item in seq recursive -%} - [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}''') - assert tmpl.render(seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a='a')]) - ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]' - - def test_recursive_depth0(self): - tmpl = env.from_string('''{% for item in seq recursive -%} - [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}''') - self.assertEqual(tmpl.render(seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a='a')]) - ]), '[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]') - - def test_recursive_depth(self): - tmpl = env.from_string('''{% for item in seq recursive -%} - [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}''') - self.assertEqual(tmpl.render(seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a='a')]) - ]), '[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]') - - def test_looploop(self): - tmpl = env.from_string('''{% for row in table %} - {%- set rowloop = loop -%} - {% for cell in row -%} - [{{ rowloop.index }}|{{ loop.index }}] - {%- endfor %} - {%- endfor %}''') - assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]' - - def test_reversed_bug(self): - tmpl = env.from_string('{% for i in items %}{{ i }}' - '{% if not loop.last %}' - ',{% endif %}{% endfor %}') - assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3' - - def test_loop_errors(self): - tmpl = env.from_string('''{% for item in [1] if loop.index - == 0 %}...{% endfor %}''') - self.assert_raises(UndefinedError, tmpl.render) - tmpl = env.from_string('''{% for item in [] %}...{% else - %}{{ loop }}{% endfor %}''') - assert tmpl.render() == '' - - def test_loop_filter(self): - tmpl = env.from_string('{% for item in range(10) if item ' - 'is even %}[{{ item }}]{% endfor %}') - assert tmpl.render() == '[0][2][4][6][8]' - tmpl = env.from_string(''' - {%- for item in range(10) if item is even %}[{{ - loop.index }}:{{ item }}]{% endfor %}''') - assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]' - - def test_loop_unassignable(self): - self.assert_raises(TemplateSyntaxError, env.from_string, - '{% for loop in seq %}...{% endfor %}') - - def test_scoped_special_var(self): - t = env.from_string('{% for s in seq %}[{{ loop.first }}{% for c in s %}' - '|{{ loop.first }}{% endfor %}]{% endfor %}') - assert t.render(seq=('ab', 'cd')) == '[True|True|False][False|True|False]' - - def test_scoped_loop_var(self): - t = env.from_string('{% for x in seq %}{{ loop.first }}' - '{% for y in seq %}{% endfor %}{% endfor %}') - assert t.render(seq='ab') == 'TrueFalse' - t = env.from_string('{% for x in seq %}{% for y in seq %}' - '{{ loop.first }}{% endfor %}{% endfor %}') - assert t.render(seq='ab') == 'TrueFalseTrueFalse' - - def test_recursive_empty_loop_iter(self): - t = env.from_string(''' - {%- for item in foo recursive -%}{%- endfor -%} - ''') - assert t.render(dict(foo=[])) == '' - - def test_call_in_loop(self): - t = env.from_string(''' - {%- macro do_something() -%} - [{{ caller() }}] - {%- endmacro %} - - {%- for i in [1, 2, 3] %} - {%- call do_something() -%} - {{ i }} - {%- endcall %} - {%- endfor -%} - ''') - assert t.render() == '[1][2][3]' - - def test_scoping_bug(self): - t = env.from_string(''' - {%- for item in foo %}...{{ item }}...{% endfor %} - {%- macro item(a) %}...{{ a }}...{% endmacro %} - {{- item(2) -}} - ''') - assert t.render(foo=(1,)) == '...1......2...' - - def test_unpacking(self): - tmpl = env.from_string('{% for a, b, c in [[1, 2, 3]] %}' - '{{ a }}|{{ b }}|{{ c }}{% endfor %}') - assert tmpl.render() == '1|2|3' - - -class IfConditionTestCase(JinjaTestCase): - - def test_simple(self): - tmpl = env.from_string('''{% if true %}...{% endif %}''') - assert tmpl.render() == '...' - - def test_elif(self): - tmpl = env.from_string('''{% if false %}XXX{% elif true - %}...{% else %}XXX{% endif %}''') - assert tmpl.render() == '...' - - def test_else(self): - tmpl = env.from_string('{% if false %}XXX{% else %}...{% endif %}') - assert tmpl.render() == '...' - - def test_empty(self): - tmpl = env.from_string('[{% if true %}{% else %}{% endif %}]') - assert tmpl.render() == '[]' - - def test_complete(self): - tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}' - 'C{% else %}D{% endif %}') - assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C' - - def test_no_scope(self): - tmpl = env.from_string('{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}') - assert tmpl.render(a=True) == '1' - tmpl = env.from_string('{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}') - assert tmpl.render() == '1' - - -class MacrosTestCase(JinjaTestCase): - env = Environment(trim_blocks=True) - - def test_simple(self): - tmpl = self.env.from_string('''\ -{% macro say_hello(name) %}Hello {{ name }}!{% endmacro %} -{{ say_hello('Peter') }}''') - assert tmpl.render() == 'Hello Peter!' - - def test_scoping(self): - tmpl = self.env.from_string('''\ -{% macro level1(data1) %} -{% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %} -{{ level2('bar') }}{% endmacro %} -{{ level1('foo') }}''') - assert tmpl.render() == 'foo|bar' - - def test_arguments(self): - tmpl = self.env.from_string('''\ -{% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %} -{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}''') - assert tmpl.render() == '||c|d|a||c|d|a|b|c|d|1|2|3|d' - - def test_varargs(self): - tmpl = self.env.from_string('''\ -{% macro test() %}{{ varargs|join('|') }}{% endmacro %}\ -{{ test(1, 2, 3) }}''') - assert tmpl.render() == '1|2|3' - - def test_simple_call(self): - tmpl = self.env.from_string('''\ -{% macro test() %}[[{{ caller() }}]]{% endmacro %}\ -{% call test() %}data{% endcall %}''') - assert tmpl.render() == '[[data]]' - - def test_complex_call(self): - tmpl = self.env.from_string('''\ -{% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\ -{% call(data) test() %}{{ data }}{% endcall %}''') - assert tmpl.render() == '[[data]]' - - def test_caller_undefined(self): - tmpl = self.env.from_string('''\ -{% set caller = 42 %}\ -{% macro test() %}{{ caller is not defined }}{% endmacro %}\ -{{ test() }}''') - assert tmpl.render() == 'True' - - def test_include(self): - self.env = Environment(loader=DictLoader({'include': - '{% macro test(foo) %}[{{ foo }}]{% endmacro %}'})) - tmpl = self.env.from_string('{% from "include" import test %}{{ test("foo") }}') - assert tmpl.render() == '[foo]' - - def test_macro_api(self): - tmpl = self.env.from_string('{% macro foo(a, b) %}{% endmacro %}' - '{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}' - '{% macro baz() %}{{ caller() }}{% endmacro %}') - assert tmpl.module.foo.arguments == ('a', 'b') - assert tmpl.module.foo.defaults == () - assert tmpl.module.foo.name == 'foo' - assert not tmpl.module.foo.caller - assert not tmpl.module.foo.catch_kwargs - assert not tmpl.module.foo.catch_varargs - assert tmpl.module.bar.arguments == () - assert tmpl.module.bar.defaults == () - assert not tmpl.module.bar.caller - assert tmpl.module.bar.catch_kwargs - assert tmpl.module.bar.catch_varargs - assert tmpl.module.baz.caller - - def test_callself(self): - tmpl = self.env.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|' - '{{ foo(x - 1) }}{% endif %}{% endmacro %}' - '{{ foo(5) }}') - assert tmpl.render() == '5|4|3|2|1' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ForLoopTestCase)) - suite.addTest(unittest.makeSuite(IfConditionTestCase)) - suite.addTest(unittest.makeSuite(MacrosTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/debug.py jinja2-2.8/jinja2/testsuite/debug.py --- jinja2-2.7.3/jinja2/testsuite/debug.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/debug.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.debug - ~~~~~~~~~~~~~~~~~~~~~~ - - Tests the debug system. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase, filesystem_loader - -from jinja2 import Environment, TemplateSyntaxError - -env = Environment(loader=filesystem_loader) - - -class DebugTestCase(JinjaTestCase): - - def test_runtime_error(self): - def test(): - tmpl.render(fail=lambda: 1 / 0) - tmpl = env.get_template('broken.html') - self.assert_traceback_matches(test, r''' - File ".*?broken.html", line 2, in (top-level template code|) - \{\{ fail\(\) \}\} - File ".*?debug.pyc?", line \d+, in - tmpl\.render\(fail=lambda: 1 / 0\) -ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero -''') - - def test_syntax_error(self): - # XXX: the .*? is necessary for python3 which does not hide - # some of the stack frames we don't want to show. Not sure - # what's up with that, but that is not that critical. Should - # be fixed though. - self.assert_traceback_matches(lambda: env.get_template('syntaxerror.html'), r'''(?sm) - File ".*?syntaxerror.html", line 4, in (template|) - \{% endif %\}.*? -(jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'. - ''') - - def test_regular_syntax_error(self): - def test(): - raise TemplateSyntaxError('wtf', 42) - self.assert_traceback_matches(test, r''' - File ".*debug.pyc?", line \d+, in test - raise TemplateSyntaxError\('wtf', 42\) -(jinja2\.exceptions\.)?TemplateSyntaxError: wtf - line 42''') - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(DebugTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/doctests.py jinja2-2.8/jinja2/testsuite/doctests.py --- jinja2-2.7.3/jinja2/testsuite/doctests.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/doctests.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.doctests - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - The doctests. Collects all tests we want to test from - the Jinja modules. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest -import doctest - - -def suite(): - from jinja2 import utils, sandbox, runtime, meta, loaders, \ - ext, environment, bccache, nodes - suite = unittest.TestSuite() - suite.addTest(doctest.DocTestSuite(utils)) - suite.addTest(doctest.DocTestSuite(sandbox)) - suite.addTest(doctest.DocTestSuite(runtime)) - suite.addTest(doctest.DocTestSuite(meta)) - suite.addTest(doctest.DocTestSuite(loaders)) - suite.addTest(doctest.DocTestSuite(ext)) - suite.addTest(doctest.DocTestSuite(environment)) - suite.addTest(doctest.DocTestSuite(bccache)) - suite.addTest(doctest.DocTestSuite(nodes)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/ext.py jinja2-2.8/jinja2/testsuite/ext.py --- jinja2-2.7.3/jinja2/testsuite/ext.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/ext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,459 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.ext - ~~~~~~~~~~~~~~~~~~~~ - - Tests for the extensions. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import re -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment, DictLoader, contextfunction, nodes -from jinja2.exceptions import TemplateAssertionError -from jinja2.ext import Extension -from jinja2.lexer import Token, count_newlines -from jinja2._compat import next, BytesIO, itervalues, text_type - -importable_object = 23 - -_gettext_re = re.compile(r'_\((.*?)\)(?s)') - - -i18n_templates = { - 'master.html': '{{ page_title|default(_("missing")) }}' - '{% block body %}{% endblock %}', - 'child.html': '{% extends "master.html" %}{% block body %}' - '{% trans %}watch out{% endtrans %}{% endblock %}', - 'plural.html': '{% trans user_count %}One user online{% pluralize %}' - '{{ user_count }} users online{% endtrans %}', - 'plural2.html': '{% trans user_count=get_user_count() %}{{ user_count }}s' - '{% pluralize %}{{ user_count }}p{% endtrans %}', - 'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}' -} - -newstyle_i18n_templates = { - 'master.html': '{{ page_title|default(_("missing")) }}' - '{% block body %}{% endblock %}', - 'child.html': '{% extends "master.html" %}{% block body %}' - '{% trans %}watch out{% endtrans %}{% endblock %}', - 'plural.html': '{% trans user_count %}One user online{% pluralize %}' - '{{ user_count }} users online{% endtrans %}', - 'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}', - 'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}', - 'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}' - '{{ num }} apples{% endtrans %}', - 'transvars1.html': '{% trans %}User: {{ num }}{% endtrans %}', - 'transvars2.html': '{% trans num=count %}User: {{ num }}{% endtrans %}', - 'transvars3.html': '{% trans count=num %}User: {{ count }}{% endtrans %}', - 'novars.html': '{% trans %}%(hello)s{% endtrans %}', - 'vars.html': '{% trans %}{{ foo }}%(foo)s{% endtrans %}', - 'explicitvars.html': '{% trans foo="42" %}%(foo)s{% endtrans %}' -} - - -languages = { - 'de': { - 'missing': u'fehlend', - 'watch out': u'pass auf', - 'One user online': u'Ein Benutzer online', - '%(user_count)s users online': u'%(user_count)s Benutzer online', - 'User: %(num)s': u'Benutzer: %(num)s', - 'User: %(count)s': u'Benutzer: %(count)s', - '%(num)s apple': u'%(num)s Apfel', - '%(num)s apples': u'%(num)s Äpfel' - } -} - - -@contextfunction -def gettext(context, string): - language = context.get('LANGUAGE', 'en') - return languages.get(language, {}).get(string, string) - - -@contextfunction -def ngettext(context, s, p, n): - language = context.get('LANGUAGE', 'en') - if n != 1: - return languages.get(language, {}).get(p, p) - return languages.get(language, {}).get(s, s) - - -i18n_env = Environment( - loader=DictLoader(i18n_templates), - extensions=['jinja2.ext.i18n'] -) -i18n_env.globals.update({ - '_': gettext, - 'gettext': gettext, - 'ngettext': ngettext -}) - -newstyle_i18n_env = Environment( - loader=DictLoader(newstyle_i18n_templates), - extensions=['jinja2.ext.i18n'] -) -newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True) - -class TestExtension(Extension): - tags = set(['test']) - ext_attr = 42 - - def parse(self, parser): - return nodes.Output([self.call_method('_dump', [ - nodes.EnvironmentAttribute('sandboxed'), - self.attr('ext_attr'), - nodes.ImportedName(__name__ + '.importable_object'), - nodes.ContextReference() - ])]).set_lineno(next(parser.stream).lineno) - - def _dump(self, sandboxed, ext_attr, imported_object, context): - return '%s|%s|%s|%s' % ( - sandboxed, - ext_attr, - imported_object, - context.blocks - ) - - -class PreprocessorExtension(Extension): - - def preprocess(self, source, name, filename=None): - return source.replace('[[TEST]]', '({{ foo }})') - - -class StreamFilterExtension(Extension): - - def filter_stream(self, stream): - for token in stream: - if token.type == 'data': - for t in self.interpolate(token): - yield t - else: - yield token - - def interpolate(self, token): - pos = 0 - end = len(token.value) - lineno = token.lineno - while 1: - match = _gettext_re.search(token.value, pos) - if match is None: - break - value = token.value[pos:match.start()] - if value: - yield Token(lineno, 'data', value) - lineno += count_newlines(token.value) - yield Token(lineno, 'variable_begin', None) - yield Token(lineno, 'name', 'gettext') - yield Token(lineno, 'lparen', None) - yield Token(lineno, 'string', match.group(1)) - yield Token(lineno, 'rparen', None) - yield Token(lineno, 'variable_end', None) - pos = match.end() - if pos < end: - yield Token(lineno, 'data', token.value[pos:]) - - -class ExtensionsTestCase(JinjaTestCase): - - def test_extend_late(self): - env = Environment() - env.add_extension('jinja2.ext.autoescape') - t = env.from_string('{% autoescape true %}{{ "" }}{% endautoescape %}') - assert t.render() == '<test>' - - def test_loop_controls(self): - env = Environment(extensions=['jinja2.ext.loopcontrols']) - - tmpl = env.from_string(''' - {%- for item in [1, 2, 3, 4] %} - {%- if item % 2 == 0 %}{% continue %}{% endif -%} - {{ item }} - {%- endfor %}''') - assert tmpl.render() == '13' - - tmpl = env.from_string(''' - {%- for item in [1, 2, 3, 4] %} - {%- if item > 2 %}{% break %}{% endif -%} - {{ item }} - {%- endfor %}''') - assert tmpl.render() == '12' - - def test_do(self): - env = Environment(extensions=['jinja2.ext.do']) - tmpl = env.from_string(''' - {%- set items = [] %} - {%- for char in "foo" %} - {%- do items.append(loop.index0 ~ char) %} - {%- endfor %}{{ items|join(', ') }}''') - assert tmpl.render() == '0f, 1o, 2o' - - def test_with(self): - env = Environment(extensions=['jinja2.ext.with_']) - tmpl = env.from_string('''\ - {% with a=42, b=23 -%} - {{ a }} = {{ b }} - {% endwith -%} - {{ a }} = {{ b }}\ - ''') - assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] \ - == ['42 = 23', '1 = 2'] - - def test_extension_nodes(self): - env = Environment(extensions=[TestExtension]) - tmpl = env.from_string('{% test %}') - assert tmpl.render() == 'False|42|23|{}' - - def test_identifier(self): - assert TestExtension.identifier == __name__ + '.TestExtension' - - def test_rebinding(self): - original = Environment(extensions=[TestExtension]) - overlay = original.overlay() - for env in original, overlay: - for ext in itervalues(env.extensions): - assert ext.environment is env - - def test_preprocessor_extension(self): - env = Environment(extensions=[PreprocessorExtension]) - tmpl = env.from_string('{[[TEST]]}') - assert tmpl.render(foo=42) == '{(42)}' - - def test_streamfilter_extension(self): - env = Environment(extensions=[StreamFilterExtension]) - env.globals['gettext'] = lambda x: x.upper() - tmpl = env.from_string('Foo _(bar) Baz') - out = tmpl.render() - assert out == 'Foo BAR Baz' - - def test_extension_ordering(self): - class T1(Extension): - priority = 1 - class T2(Extension): - priority = 2 - env = Environment(extensions=[T1, T2]) - ext = list(env.iter_extensions()) - assert ext[0].__class__ is T1 - assert ext[1].__class__ is T2 - - -class InternationalizationTestCase(JinjaTestCase): - - def test_trans(self): - tmpl = i18n_env.get_template('child.html') - assert tmpl.render(LANGUAGE='de') == 'fehlendpass auf' - - def test_trans_plural(self): - tmpl = i18n_env.get_template('plural.html') - assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online' - assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online' - - def test_trans_plural_with_functions(self): - tmpl = i18n_env.get_template('plural2.html') - def get_user_count(): - get_user_count.called += 1 - return 1 - get_user_count.called = 0 - assert tmpl.render(LANGUAGE='de', get_user_count=get_user_count) == '1s' - assert get_user_count.called == 1 - - def test_complex_plural(self): - tmpl = i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% ' - 'pluralize count %}{{ count }} items{% endtrans %}') - assert tmpl.render() == '2 items' - self.assert_raises(TemplateAssertionError, i18n_env.from_string, - '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') - - def test_trans_stringformatting(self): - tmpl = i18n_env.get_template('stringformat.html') - assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5' - - def test_extract(self): - from jinja2.ext import babel_extract - source = BytesIO(''' - {{ gettext('Hello World') }} - {% trans %}Hello World{% endtrans %} - {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} - '''.encode('ascii')) # make python 3 happy - assert list(babel_extract(source, ('gettext', 'ngettext', '_'), [], {})) == [ - (2, 'gettext', u'Hello World', []), - (3, 'gettext', u'Hello World', []), - (4, 'ngettext', (u'%(users)s user', u'%(users)s users', None), []) - ] - - def test_comment_extract(self): - from jinja2.ext import babel_extract - source = BytesIO(''' - {# trans first #} - {{ gettext('Hello World') }} - {% trans %}Hello World{% endtrans %}{# trans second #} - {#: third #} - {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} - '''.encode('utf-8')) # make python 3 happy - assert list(babel_extract(source, ('gettext', 'ngettext', '_'), ['trans', ':'], {})) == [ - (3, 'gettext', u'Hello World', ['first']), - (4, 'gettext', u'Hello World', ['second']), - (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), ['third']) - ] - - -class NewstyleInternationalizationTestCase(JinjaTestCase): - - def test_trans(self): - tmpl = newstyle_i18n_env.get_template('child.html') - assert tmpl.render(LANGUAGE='de') == 'fehlendpass auf' - - def test_trans_plural(self): - tmpl = newstyle_i18n_env.get_template('plural.html') - assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online' - assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online' - - def test_complex_plural(self): - tmpl = newstyle_i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% ' - 'pluralize count %}{{ count }} items{% endtrans %}') - assert tmpl.render() == '2 items' - self.assert_raises(TemplateAssertionError, i18n_env.from_string, - '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') - - def test_trans_stringformatting(self): - tmpl = newstyle_i18n_env.get_template('stringformat.html') - assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5' - - def test_newstyle_plural(self): - tmpl = newstyle_i18n_env.get_template('ngettext.html') - assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel' - assert tmpl.render(LANGUAGE='de', apples=5) == u'5 Äpfel' - - def test_autoescape_support(self): - env = Environment(extensions=['jinja2.ext.autoescape', - 'jinja2.ext.i18n']) - env.install_gettext_callables(lambda x: u'Wert: %(name)s', - lambda s, p, n: s, newstyle=True) - t = env.from_string('{% autoescape ae %}{{ gettext("foo", name=' - '"") }}{% endautoescape %}') - assert t.render(ae=True) == 'Wert: <test>' - assert t.render(ae=False) == 'Wert: ' - - def test_num_used_twice(self): - tmpl = newstyle_i18n_env.get_template('ngettext_long.html') - assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel' - - def test_num_called_num(self): - source = newstyle_i18n_env.compile(''' - {% trans num=3 %}{{ num }} apple{% pluralize - %}{{ num }} apples{% endtrans %} - ''', raw=True) - # quite hacky, but the only way to properly test that. The idea is - # that the generated code does not pass num twice (although that - # would work) for better performance. This only works on the - # newstyle gettext of course - assert re.search(r"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s " - r"apples', 3", source) is not None - - def test_trans_vars(self): - t1 = newstyle_i18n_env.get_template('transvars1.html') - t2 = newstyle_i18n_env.get_template('transvars2.html') - t3 = newstyle_i18n_env.get_template('transvars3.html') - assert t1.render(num=1, LANGUAGE='de') == 'Benutzer: 1' - assert t2.render(count=23, LANGUAGE='de') == 'Benutzer: 23' - assert t3.render(num=42, LANGUAGE='de') == 'Benutzer: 42' - - def test_novars_vars_escaping(self): - t = newstyle_i18n_env.get_template('novars.html') - assert t.render() == '%(hello)s' - t = newstyle_i18n_env.get_template('vars.html') - assert t.render(foo='42') == '42%(foo)s' - t = newstyle_i18n_env.get_template('explicitvars.html') - assert t.render() == '%(foo)s' - - -class AutoEscapeTestCase(JinjaTestCase): - - def test_scoped_setting(self): - env = Environment(extensions=['jinja2.ext.autoescape'], - autoescape=True) - tmpl = env.from_string(''' - {{ "" }} - {% autoescape false %} - {{ "" }} - {% endautoescape %} - {{ "" }} - ''') - assert tmpl.render().split() == \ - [u'<HelloWorld>', u'', u'<HelloWorld>'] - - env = Environment(extensions=['jinja2.ext.autoescape'], - autoescape=False) - tmpl = env.from_string(''' - {{ "" }} - {% autoescape true %} - {{ "" }} - {% endautoescape %} - {{ "" }} - ''') - assert tmpl.render().split() == \ - [u'', u'<HelloWorld>', u''] - - def test_nonvolatile(self): - env = Environment(extensions=['jinja2.ext.autoescape'], - autoescape=True) - tmpl = env.from_string('{{ {"foo": ""}|xmlattr|escape }}') - assert tmpl.render() == ' foo="<test>"' - tmpl = env.from_string('{% autoescape false %}{{ {"foo": ""}' - '|xmlattr|escape }}{% endautoescape %}') - assert tmpl.render() == ' foo="&lt;test&gt;"' - - def test_volatile(self): - env = Environment(extensions=['jinja2.ext.autoescape'], - autoescape=True) - tmpl = env.from_string('{% autoescape foo %}{{ {"foo": ""}' - '|xmlattr|escape }}{% endautoescape %}') - assert tmpl.render(foo=False) == ' foo="&lt;test&gt;"' - assert tmpl.render(foo=True) == ' foo="<test>"' - - def test_scoping(self): - env = Environment(extensions=['jinja2.ext.autoescape']) - tmpl = env.from_string('{% autoescape true %}{% set x = "" %}{{ x }}' - '{% endautoescape %}{{ x }}{{ "" }}') - assert tmpl.render(x=1) == '<x>1' - - def test_volatile_scoping(self): - env = Environment(extensions=['jinja2.ext.autoescape']) - tmplsource = ''' - {% autoescape val %} - {% macro foo(x) %} - [{{ x }}] - {% endmacro %} - {{ foo().__class__.__name__ }} - {% endautoescape %} - {{ '' }} - ''' - tmpl = env.from_string(tmplsource) - assert tmpl.render(val=True).split()[0] == 'Markup' - assert tmpl.render(val=False).split()[0] == text_type.__name__ - - # looking at the source we should see there in raw - # (and then escaped as well) - env = Environment(extensions=['jinja2.ext.autoescape']) - pysource = env.compile(tmplsource, raw=True) - assert '\\n' in pysource - - env = Environment(extensions=['jinja2.ext.autoescape'], - autoescape=True) - pysource = env.compile(tmplsource, raw=True) - assert '<testing>\\n' in pysource - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ExtensionsTestCase)) - suite.addTest(unittest.makeSuite(InternationalizationTestCase)) - suite.addTest(unittest.makeSuite(NewstyleInternationalizationTestCase)) - suite.addTest(unittest.makeSuite(AutoEscapeTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/filters.py jinja2-2.8/jinja2/testsuite/filters.py --- jinja2-2.7.3/jinja2/testsuite/filters.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/filters.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,515 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.filters - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Tests for the jinja filters. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Markup, Environment -from jinja2._compat import text_type, implements_to_string - -env = Environment() - - -class FilterTestCase(JinjaTestCase): - - def test_filter_calling(self): - rv = env.call_filter('sum', [1, 2, 3]) - self.assert_equal(rv, 6) - - def test_capitalize(self): - tmpl = env.from_string('{{ "foo bar"|capitalize }}') - assert tmpl.render() == 'Foo bar' - - def test_center(self): - tmpl = env.from_string('{{ "foo"|center(9) }}') - assert tmpl.render() == ' foo ' - - def test_default(self): - tmpl = env.from_string( - "{{ missing|default('no') }}|{{ false|default('no') }}|" - "{{ false|default('no', true) }}|{{ given|default('no') }}" - ) - assert tmpl.render(given='yes') == 'no|False|no|yes' - - def test_dictsort(self): - tmpl = env.from_string( - '{{ foo|dictsort }}|' - '{{ foo|dictsort(true) }}|' - '{{ foo|dictsort(false, "value") }}' - ) - out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3}) - assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|" - "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|" - "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]") - - def test_batch(self): - tmpl = env.from_string("{{ foo|batch(3)|list }}|" - "{{ foo|batch(3, 'X')|list }}") - out = tmpl.render(foo=list(range(10))) - assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|" - "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]") - - def test_slice(self): - tmpl = env.from_string('{{ foo|slice(3)|list }}|' - '{{ foo|slice(3, "X")|list }}') - out = tmpl.render(foo=list(range(10))) - assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" - "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]") - - def test_escape(self): - tmpl = env.from_string('''{{ '<">&'|escape }}''') - out = tmpl.render() - assert out == '<">&' - - def test_striptags(self): - tmpl = env.from_string('''{{ foo|striptags }}''') - out = tmpl.render(foo='

    just a small \n ' - 'example link

    \n

    to a webpage

    ' - '') - assert out == 'just a small example link to a webpage' - - def test_filesizeformat(self): - tmpl = env.from_string( - '{{ 100|filesizeformat }}|' - '{{ 1000|filesizeformat }}|' - '{{ 1000000|filesizeformat }}|' - '{{ 1000000000|filesizeformat }}|' - '{{ 1000000000000|filesizeformat }}|' - '{{ 100|filesizeformat(true) }}|' - '{{ 1000|filesizeformat(true) }}|' - '{{ 1000000|filesizeformat(true) }}|' - '{{ 1000000000|filesizeformat(true) }}|' - '{{ 1000000000000|filesizeformat(true) }}' - ) - out = tmpl.render() - self.assert_equal(out, ( - '100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|' - '1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB' - )) - - def test_filesizeformat_issue59(self): - tmpl = env.from_string( - '{{ 300|filesizeformat }}|' - '{{ 3000|filesizeformat }}|' - '{{ 3000000|filesizeformat }}|' - '{{ 3000000000|filesizeformat }}|' - '{{ 3000000000000|filesizeformat }}|' - '{{ 300|filesizeformat(true) }}|' - '{{ 3000|filesizeformat(true) }}|' - '{{ 3000000|filesizeformat(true) }}' - ) - out = tmpl.render() - self.assert_equal(out, ( - '300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|' - '2.9 KiB|2.9 MiB' - )) - - - def test_first(self): - tmpl = env.from_string('{{ foo|first }}') - out = tmpl.render(foo=list(range(10))) - assert out == '0' - - def test_float(self): - tmpl = env.from_string('{{ "42"|float }}|' - '{{ "ajsghasjgd"|float }}|' - '{{ "32.32"|float }}') - out = tmpl.render() - assert out == '42.0|0.0|32.32' - - def test_format(self): - tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''') - out = tmpl.render() - assert out == 'a|b' - - def test_indent(self): - tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}') - text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2) - out = tmpl.render(foo=text) - assert out == ('foo bar foo bar\n foo bar foo bar| ' - 'foo bar foo bar\n foo bar foo bar') - - def test_int(self): - tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|' - '{{ "32.32"|int }}') - out = tmpl.render() - assert out == '42|0|32' - - def test_join(self): - tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}') - out = tmpl.render() - assert out == '1|2|3' - - env2 = Environment(autoescape=True) - tmpl = env2.from_string('{{ ["", "foo"|safe]|join }}') - assert tmpl.render() == '<foo>foo' - - def test_join_attribute(self): - class User(object): - def __init__(self, username): - self.username = username - tmpl = env.from_string('''{{ users|join(', ', 'username') }}''') - assert tmpl.render(users=map(User, ['foo', 'bar'])) == 'foo, bar' - - def test_last(self): - tmpl = env.from_string('''{{ foo|last }}''') - out = tmpl.render(foo=list(range(10))) - assert out == '9' - - def test_length(self): - tmpl = env.from_string('''{{ "hello world"|length }}''') - out = tmpl.render() - assert out == '11' - - def test_lower(self): - tmpl = env.from_string('''{{ "FOO"|lower }}''') - out = tmpl.render() - assert out == 'foo' - - def test_pprint(self): - from pprint import pformat - tmpl = env.from_string('''{{ data|pprint }}''') - data = list(range(1000)) - assert tmpl.render(data=data) == pformat(data) - - def test_random(self): - tmpl = env.from_string('''{{ seq|random }}''') - seq = list(range(100)) - for _ in range(10): - assert int(tmpl.render(seq=seq)) in seq - - def test_reverse(self): - tmpl = env.from_string('{{ "foobar"|reverse|join }}|' - '{{ [1, 2, 3]|reverse|list }}') - assert tmpl.render() == 'raboof|[3, 2, 1]' - - def test_string(self): - x = [1, 2, 3, 4, 5] - tmpl = env.from_string('''{{ obj|string }}''') - assert tmpl.render(obj=x) == text_type(x) - - def test_title(self): - tmpl = env.from_string('''{{ "foo bar"|title }}''') - assert tmpl.render() == "Foo Bar" - tmpl = env.from_string('''{{ "foo's bar"|title }}''') - assert tmpl.render() == "Foo's Bar" - tmpl = env.from_string('''{{ "foo bar"|title }}''') - assert tmpl.render() == "Foo Bar" - tmpl = env.from_string('''{{ "f bar f"|title }}''') - assert tmpl.render() == "F Bar F" - tmpl = env.from_string('''{{ "foo-bar"|title }}''') - assert tmpl.render() == "Foo-Bar" - tmpl = env.from_string('''{{ "foo\tbar"|title }}''') - assert tmpl.render() == "Foo\tBar" - tmpl = env.from_string('''{{ "FOO\tBAR"|title }}''') - assert tmpl.render() == "Foo\tBar" - - def test_truncate(self): - tmpl = env.from_string( - '{{ data|truncate(15, true, ">>>") }}|' - '{{ data|truncate(15, false, ">>>") }}|' - '{{ smalldata|truncate(15) }}' - ) - out = tmpl.render(data='foobar baz bar' * 1000, - smalldata='foobar baz bar') - assert out == 'foobar baz barf>>>|foobar baz >>>|foobar baz bar' - - def test_upper(self): - tmpl = env.from_string('{{ "foo"|upper }}') - assert tmpl.render() == 'FOO' - - def test_urlize(self): - tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}') - assert tmpl.render() == 'foo '\ - 'http://www.example.com/ bar' - - def test_wordcount(self): - tmpl = env.from_string('{{ "foo bar baz"|wordcount }}') - assert tmpl.render() == '3' - - def test_block(self): - tmpl = env.from_string('{% filter lower|escape %}{% endfilter %}') - assert tmpl.render() == '<hehe>' - - def test_chaining(self): - tmpl = env.from_string('''{{ ['', '']|first|upper|escape }}''') - assert tmpl.render() == '<FOO>' - - def test_sum(self): - tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''') - assert tmpl.render() == '21' - - def test_sum_attributes(self): - tmpl = env.from_string('''{{ values|sum('value') }}''') - assert tmpl.render(values=[ - {'value': 23}, - {'value': 1}, - {'value': 18}, - ]) == '42' - - def test_sum_attributes_nested(self): - tmpl = env.from_string('''{{ values|sum('real.value') }}''') - assert tmpl.render(values=[ - {'real': {'value': 23}}, - {'real': {'value': 1}}, - {'real': {'value': 18}}, - ]) == '42' - - def test_sum_attributes_tuple(self): - tmpl = env.from_string('''{{ values.items()|sum('1') }}''') - assert tmpl.render(values={ - 'foo': 23, - 'bar': 1, - 'baz': 18, - }) == '42' - - def test_abs(self): - tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''') - assert tmpl.render() == '1|1', tmpl.render() - - def test_round_positive(self): - tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|' - "{{ 2.1234|round(3, 'floor') }}|" - "{{ 2.1|round(0, 'ceil') }}") - assert tmpl.render() == '3.0|2.0|2.123|3.0', tmpl.render() - - def test_round_negative(self): - tmpl = env.from_string('{{ 21.3|round(-1)}}|' - "{{ 21.3|round(-1, 'ceil')}}|" - "{{ 21.3|round(-1, 'floor')}}") - assert tmpl.render() == '20.0|30.0|20.0',tmpl.render() - - def test_xmlattr(self): - tmpl = env.from_string("{{ {'foo': 42, 'bar': 23, 'fish': none, " - "'spam': missing, 'blub:blub': ''}|xmlattr }}") - out = tmpl.render().split() - assert len(out) == 3 - assert 'foo="42"' in out - assert 'bar="23"' in out - assert 'blub:blub="<?>"' in out - - def test_sort1(self): - tmpl = env.from_string('{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}') - assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]' - - def test_sort2(self): - tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}') - assert tmpl.render() == 'AbcD' - - def test_sort3(self): - tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''') - assert tmpl.render() == "['Bar', 'blah', 'foo']" - - def test_sort4(self): - @implements_to_string - class Magic(object): - def __init__(self, value): - self.value = value - def __str__(self): - return text_type(self.value) - tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''') - assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234' - - def test_groupby(self): - tmpl = env.from_string(''' - {%- for grouper, list in [{'foo': 1, 'bar': 2}, - {'foo': 2, 'bar': 3}, - {'foo': 1, 'bar': 1}, - {'foo': 3, 'bar': 4}]|groupby('foo') -%} - {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| - {%- endfor %}''') - assert tmpl.render().split('|') == [ - "1: 1, 2: 1, 1", - "2: 2, 3", - "3: 3, 4", - "" - ] - - def test_groupby_tuple_index(self): - tmpl = env.from_string(''' - {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%} - {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| - {%- endfor %}''') - assert tmpl.render() == 'a:1:2|b:1|' - - def test_groupby_multidot(self): - class Date(object): - def __init__(self, day, month, year): - self.day = day - self.month = month - self.year = year - class Article(object): - def __init__(self, title, *date): - self.date = Date(*date) - self.title = title - articles = [ - Article('aha', 1, 1, 1970), - Article('interesting', 2, 1, 1970), - Article('really?', 3, 1, 1970), - Article('totally not', 1, 1, 1971) - ] - tmpl = env.from_string(''' - {%- for year, list in articles|groupby('date.year') -%} - {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| - {%- endfor %}''') - assert tmpl.render(articles=articles).split('|') == [ - '1970[aha][interesting][really?]', - '1971[totally not]', - '' - ] - - def test_filtertag(self): - tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}" - "foobar{% endfilter %}") - assert tmpl.render() == 'fooBAR' - - def test_replace(self): - env = Environment() - tmpl = env.from_string('{{ string|replace("o", 42) }}') - assert tmpl.render(string='') == '' - env = Environment(autoescape=True) - tmpl = env.from_string('{{ string|replace("o", 42) }}') - assert tmpl.render(string='') == '<f4242>' - tmpl = env.from_string('{{ string|replace("<", 42) }}') - assert tmpl.render(string='') == '42foo>' - tmpl = env.from_string('{{ string|replace("o", ">x<") }}') - assert tmpl.render(string=Markup('foo')) == 'f>x<>x<' - - def test_forceescape(self): - tmpl = env.from_string('{{ x|forceescape }}') - assert tmpl.render(x=Markup('
    ')) == u'<div />' - - def test_safe(self): - env = Environment(autoescape=True) - tmpl = env.from_string('{{ "
    foo
    "|safe }}') - assert tmpl.render() == '
    foo
    ' - tmpl = env.from_string('{{ "
    foo
    " }}') - assert tmpl.render() == '<div>foo</div>' - - def test_urlencode(self): - env = Environment(autoescape=True) - tmpl = env.from_string('{{ "Hello, world!"|urlencode }}') - assert tmpl.render() == 'Hello%2C%20world%21' - tmpl = env.from_string('{{ o|urlencode }}') - assert tmpl.render(o=u"Hello, world\u203d") == "Hello%2C%20world%E2%80%BD" - assert tmpl.render(o=(("f", 1),)) == "f=1" - assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&z=2" - assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1" - assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1" - assert tmpl.render(o={0: 1}) == "0=1" - - def test_simple_map(self): - env = Environment() - tmpl = env.from_string('{{ ["1", "2", "3"]|map("int")|sum }}') - self.assertEqual(tmpl.render(), '6') - - def test_attribute_map(self): - class User(object): - def __init__(self, name): - self.name = name - env = Environment() - users = [ - User('john'), - User('jane'), - User('mike'), - ] - tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}') - self.assertEqual(tmpl.render(users=users), 'john|jane|mike') - - def test_empty_map(self): - env = Environment() - tmpl = env.from_string('{{ none|map("upper")|list }}') - self.assertEqual(tmpl.render(), '[]') - - def test_simple_select(self): - env = Environment() - tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}') - self.assertEqual(tmpl.render(), '1|3|5') - - def test_bool_select(self): - env = Environment() - tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}') - self.assertEqual(tmpl.render(), '1|2|3|4|5') - - def test_simple_reject(self): - env = Environment() - tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|reject("odd")|join("|") }}') - self.assertEqual(tmpl.render(), '2|4') - - def test_bool_reject(self): - env = Environment() - tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}') - self.assertEqual(tmpl.render(), 'None|False|0') - - def test_simple_select_attr(self): - class User(object): - def __init__(self, name, is_active): - self.name = name - self.is_active = is_active - env = Environment() - users = [ - User('john', True), - User('jane', True), - User('mike', False), - ] - tmpl = env.from_string('{{ users|selectattr("is_active")|' - 'map(attribute="name")|join("|") }}') - self.assertEqual(tmpl.render(users=users), 'john|jane') - - def test_simple_reject_attr(self): - class User(object): - def __init__(self, name, is_active): - self.name = name - self.is_active = is_active - env = Environment() - users = [ - User('john', True), - User('jane', True), - User('mike', False), - ] - tmpl = env.from_string('{{ users|rejectattr("is_active")|' - 'map(attribute="name")|join("|") }}') - self.assertEqual(tmpl.render(users=users), 'mike') - - def test_func_select_attr(self): - class User(object): - def __init__(self, id, name): - self.id = id - self.name = name - env = Environment() - users = [ - User(1, 'john'), - User(2, 'jane'), - User(3, 'mike'), - ] - tmpl = env.from_string('{{ users|selectattr("id", "odd")|' - 'map(attribute="name")|join("|") }}') - self.assertEqual(tmpl.render(users=users), 'john|mike') - - def test_func_reject_attr(self): - class User(object): - def __init__(self, id, name): - self.id = id - self.name = name - env = Environment() - users = [ - User(1, 'john'), - User(2, 'jane'), - User(3, 'mike'), - ] - tmpl = env.from_string('{{ users|rejectattr("id", "odd")|' - 'map(attribute="name")|join("|") }}') - self.assertEqual(tmpl.render(users=users), 'jane') - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(FilterTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/imports.py jinja2-2.8/jinja2/testsuite/imports.py --- jinja2-2.7.3/jinja2/testsuite/imports.py 2013-08-07 13:28:30.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.imports - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Tests the import features (with includes). - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment, DictLoader -from jinja2.exceptions import TemplateNotFound, TemplatesNotFound - - -test_env = Environment(loader=DictLoader(dict( - module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}', - header='[{{ foo }}|{{ 23 }}]', - o_printer='({{ o }})' -))) -test_env.globals['bar'] = 23 - - -class ImportsTestCase(JinjaTestCase): - - def test_context_imports(self): - t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') - assert t.render(foo=42) == '[|23]' - t = test_env.from_string('{% import "module" as m without context %}{{ m.test() }}') - assert t.render(foo=42) == '[|23]' - t = test_env.from_string('{% import "module" as m with context %}{{ m.test() }}') - assert t.render(foo=42) == '[42|23]' - t = test_env.from_string('{% from "module" import test %}{{ test() }}') - assert t.render(foo=42) == '[|23]' - t = test_env.from_string('{% from "module" import test without context %}{{ test() }}') - assert t.render(foo=42) == '[|23]' - t = test_env.from_string('{% from "module" import test with context %}{{ test() }}') - assert t.render(foo=42) == '[42|23]' - - def test_trailing_comma(self): - test_env.from_string('{% from "foo" import bar, baz with context %}') - test_env.from_string('{% from "foo" import bar, baz, with context %}') - test_env.from_string('{% from "foo" import bar, with context %}') - test_env.from_string('{% from "foo" import bar, with, context %}') - test_env.from_string('{% from "foo" import bar, with with context %}') - - def test_exports(self): - m = test_env.from_string(''' - {% macro toplevel() %}...{% endmacro %} - {% macro __private() %}...{% endmacro %} - {% set variable = 42 %} - {% for item in [1] %} - {% macro notthere() %}{% endmacro %} - {% endfor %} - ''').module - assert m.toplevel() == '...' - assert not hasattr(m, '__missing') - assert m.variable == 42 - assert not hasattr(m, 'notthere') - - -class IncludesTestCase(JinjaTestCase): - - def test_context_include(self): - t = test_env.from_string('{% include "header" %}') - assert t.render(foo=42) == '[42|23]' - t = test_env.from_string('{% include "header" with context %}') - assert t.render(foo=42) == '[42|23]' - t = test_env.from_string('{% include "header" without context %}') - assert t.render(foo=42) == '[|23]' - - def test_choice_includes(self): - t = test_env.from_string('{% include ["missing", "header"] %}') - assert t.render(foo=42) == '[42|23]' - - t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}') - assert t.render(foo=42) == '' - - t = test_env.from_string('{% include ["missing", "missing2"] %}') - self.assert_raises(TemplateNotFound, t.render) - try: - t.render() - except TemplatesNotFound as e: - assert e.templates == ['missing', 'missing2'] - assert e.name == 'missing2' - else: - assert False, 'thou shalt raise' - - def test_includes(t, **ctx): - ctx['foo'] = 42 - assert t.render(ctx) == '[42|23]' - - t = test_env.from_string('{% include ["missing", "header"] %}') - test_includes(t) - t = test_env.from_string('{% include x %}') - test_includes(t, x=['missing', 'header']) - t = test_env.from_string('{% include [x, "header"] %}') - test_includes(t, x='missing') - t = test_env.from_string('{% include x %}') - test_includes(t, x='header') - t = test_env.from_string('{% include x %}') - test_includes(t, x='header') - t = test_env.from_string('{% include [x] %}') - test_includes(t, x='header') - - def test_include_ignoring_missing(self): - t = test_env.from_string('{% include "missing" %}') - self.assert_raises(TemplateNotFound, t.render) - for extra in '', 'with context', 'without context': - t = test_env.from_string('{% include "missing" ignore missing ' + - extra + ' %}') - assert t.render() == '' - - def test_context_include_with_overrides(self): - env = Environment(loader=DictLoader(dict( - main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", - item="{{ item }}" - ))) - assert env.get_template("main").render() == "123" - - def test_unoptimized_scopes(self): - t = test_env.from_string(""" - {% macro outer(o) %} - {% macro inner() %} - {% include "o_printer" %} - {% endmacro %} - {{ inner() }} - {% endmacro %} - {{ outer("FOO") }} - """) - assert t.render().strip() == '(FOO)' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ImportsTestCase)) - suite.addTest(unittest.makeSuite(IncludesTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/inheritance.py jinja2-2.8/jinja2/testsuite/inheritance.py --- jinja2-2.7.3/jinja2/testsuite/inheritance.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/inheritance.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,250 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.inheritance - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Tests the template inheritance feature. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment, DictLoader, TemplateError - - -LAYOUTTEMPLATE = '''\ -|{% block block1 %}block 1 from layout{% endblock %} -|{% block block2 %}block 2 from layout{% endblock %} -|{% block block3 %} -{% block block4 %}nested block 4 from layout{% endblock %} -{% endblock %}|''' - -LEVEL1TEMPLATE = '''\ -{% extends "layout" %} -{% block block1 %}block 1 from level1{% endblock %}''' - -LEVEL2TEMPLATE = '''\ -{% extends "level1" %} -{% block block2 %}{% block block5 %}nested block 5 from level2{% -endblock %}{% endblock %}''' - -LEVEL3TEMPLATE = '''\ -{% extends "level2" %} -{% block block5 %}block 5 from level3{% endblock %} -{% block block4 %}block 4 from level3{% endblock %} -''' - -LEVEL4TEMPLATE = '''\ -{% extends "level3" %} -{% block block3 %}block 3 from level4{% endblock %} -''' - -WORKINGTEMPLATE = '''\ -{% extends "layout" %} -{% block block1 %} - {% if false %} - {% block block2 %} - this should workd - {% endblock %} - {% endif %} -{% endblock %} -''' - -DOUBLEEXTENDS = '''\ -{% extends "layout" %} -{% extends "layout" %} -{% block block1 %} - {% if false %} - {% block block2 %} - this should workd - {% endblock %} - {% endif %} -{% endblock %} -''' - - -env = Environment(loader=DictLoader({ - 'layout': LAYOUTTEMPLATE, - 'level1': LEVEL1TEMPLATE, - 'level2': LEVEL2TEMPLATE, - 'level3': LEVEL3TEMPLATE, - 'level4': LEVEL4TEMPLATE, - 'working': WORKINGTEMPLATE, - 'doublee': DOUBLEEXTENDS, -}), trim_blocks=True) - - -class InheritanceTestCase(JinjaTestCase): - - def test_layout(self): - tmpl = env.get_template('layout') - assert tmpl.render() == ('|block 1 from layout|block 2 from ' - 'layout|nested block 4 from layout|') - - def test_level1(self): - tmpl = env.get_template('level1') - assert tmpl.render() == ('|block 1 from level1|block 2 from ' - 'layout|nested block 4 from layout|') - - def test_level2(self): - tmpl = env.get_template('level2') - assert tmpl.render() == ('|block 1 from level1|nested block 5 from ' - 'level2|nested block 4 from layout|') - - def test_level3(self): - tmpl = env.get_template('level3') - assert tmpl.render() == ('|block 1 from level1|block 5 from level3|' - 'block 4 from level3|') - - def test_level4(sel): - tmpl = env.get_template('level4') - assert tmpl.render() == ('|block 1 from level1|block 5 from ' - 'level3|block 3 from level4|') - - def test_super(self): - env = Environment(loader=DictLoader({ - 'a': '{% block intro %}INTRO{% endblock %}|' - 'BEFORE|{% block data %}INNER{% endblock %}|AFTER', - 'b': '{% extends "a" %}{% block data %}({{ ' - 'super() }}){% endblock %}', - 'c': '{% extends "b" %}{% block intro %}--{{ ' - 'super() }}--{% endblock %}\n{% block data ' - '%}[{{ super() }}]{% endblock %}' - })) - tmpl = env.get_template('c') - assert tmpl.render() == '--INTRO--|BEFORE|[(INNER)]|AFTER' - - def test_working(self): - tmpl = env.get_template('working') - - def test_reuse_blocks(self): - tmpl = env.from_string('{{ self.foo() }}|{% block foo %}42' - '{% endblock %}|{{ self.foo() }}') - assert tmpl.render() == '42|42|42' - - def test_preserve_blocks(self): - env = Environment(loader=DictLoader({ - 'a': '{% if false %}{% block x %}A{% endblock %}{% endif %}{{ self.x() }}', - 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}' - })) - tmpl = env.get_template('b') - assert tmpl.render() == 'BA' - - def test_dynamic_inheritance(self): - env = Environment(loader=DictLoader({ - 'master1': 'MASTER1{% block x %}{% endblock %}', - 'master2': 'MASTER2{% block x %}{% endblock %}', - 'child': '{% extends master %}{% block x %}CHILD{% endblock %}' - })) - tmpl = env.get_template('child') - for m in range(1, 3): - assert tmpl.render(master='master%d' % m) == 'MASTER%dCHILD' % m - - def test_multi_inheritance(self): - env = Environment(loader=DictLoader({ - 'master1': 'MASTER1{% block x %}{% endblock %}', - 'master2': 'MASTER2{% block x %}{% endblock %}', - 'child': '''{% if master %}{% extends master %}{% else %}{% extends - 'master1' %}{% endif %}{% block x %}CHILD{% endblock %}''' - })) - tmpl = env.get_template('child') - assert tmpl.render(master='master2') == 'MASTER2CHILD' - assert tmpl.render(master='master1') == 'MASTER1CHILD' - assert tmpl.render() == 'MASTER1CHILD' - - def test_scoped_block(self): - env = Environment(loader=DictLoader({ - 'master.html': '{% for item in seq %}[{% block item scoped %}' - '{% endblock %}]{% endfor %}' - })) - t = env.from_string('{% extends "master.html" %}{% block item %}' - '{{ item }}{% endblock %}') - assert t.render(seq=list(range(5))) == '[0][1][2][3][4]' - - def test_super_in_scoped_block(self): - env = Environment(loader=DictLoader({ - 'master.html': '{% for item in seq %}[{% block item scoped %}' - '{{ item }}{% endblock %}]{% endfor %}' - })) - t = env.from_string('{% extends "master.html" %}{% block item %}' - '{{ super() }}|{{ item * 2 }}{% endblock %}') - assert t.render(seq=list(range(5))) == '[0|0][1|2][2|4][3|6][4|8]' - - def test_scoped_block_after_inheritance(self): - env = Environment(loader=DictLoader({ - 'layout.html': ''' - {% block useless %}{% endblock %} - ''', - 'index.html': ''' - {%- extends 'layout.html' %} - {% from 'helpers.html' import foo with context %} - {% block useless %} - {% for x in [1, 2, 3] %} - {% block testing scoped %} - {{ foo(x) }} - {% endblock %} - {% endfor %} - {% endblock %} - ''', - 'helpers.html': ''' - {% macro foo(x) %}{{ the_foo + x }}{% endmacro %} - ''' - })) - rv = env.get_template('index.html').render(the_foo=42).split() - assert rv == ['43', '44', '45'] - - -class BugFixTestCase(JinjaTestCase): - - def test_fixed_macro_scoping_bug(self): - assert Environment(loader=DictLoader({ - 'test.html': '''\ - {% extends 'details.html' %} - - {% macro my_macro() %} - my_macro - {% endmacro %} - - {% block inner_box %} - {{ my_macro() }} - {% endblock %} - ''', - 'details.html': '''\ - {% extends 'standard.html' %} - - {% macro my_macro() %} - my_macro - {% endmacro %} - - {% block content %} - {% block outer_box %} - outer_box - {% block inner_box %} - inner_box - {% endblock %} - {% endblock %} - {% endblock %} - ''', - 'standard.html': ''' - {% block content %} {% endblock %} - ''' - })).get_template("test.html").render().split() == [u'outer_box', u'my_macro'] - - def test_double_extends(self): - """Ensures that a template with more than 1 {% extends ... %} usage - raises a ``TemplateError``. - """ - try: - tmpl = env.get_template('doublee') - except Exception as e: - assert isinstance(e, TemplateError) - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(InheritanceTestCase)) - suite.addTest(unittest.makeSuite(BugFixTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/__init__.py jinja2-2.8/jinja2/testsuite/__init__.py --- jinja2-2.7.3/jinja2/testsuite/__init__.py 2013-08-07 11:31:17.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite - ~~~~~~~~~~~~~~~~ - - All the unittests of Jinja2. These tests can be executed by - either running run-tests.py using multiple Python versions at - the same time. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import os -import re -import sys -import unittest -from traceback import format_exception -from jinja2 import loaders -from jinja2._compat import PY2 - - -here = os.path.dirname(os.path.abspath(__file__)) - -dict_loader = loaders.DictLoader({ - 'justdict.html': 'FOO' -}) -package_loader = loaders.PackageLoader('jinja2.testsuite.res', 'templates') -filesystem_loader = loaders.FileSystemLoader(here + '/res/templates') -function_loader = loaders.FunctionLoader({'justfunction.html': 'FOO'}.get) -choice_loader = loaders.ChoiceLoader([dict_loader, package_loader]) -prefix_loader = loaders.PrefixLoader({ - 'a': filesystem_loader, - 'b': dict_loader -}) - - -class JinjaTestCase(unittest.TestCase): - - ### use only these methods for testing. If you need standard - ### unittest method, wrap them! - - def setup(self): - pass - - def teardown(self): - pass - - def setUp(self): - self.setup() - - def tearDown(self): - self.teardown() - - def assert_equal(self, a, b): - return self.assertEqual(a, b) - - def assert_raises(self, *args, **kwargs): - return self.assertRaises(*args, **kwargs) - - def assert_traceback_matches(self, callback, expected_tb): - try: - callback() - except Exception as e: - tb = format_exception(*sys.exc_info()) - if re.search(expected_tb.strip(), ''.join(tb)) is None: - raise self.fail('Traceback did not match:\n\n%s\nexpected:\n%s' - % (''.join(tb), expected_tb)) - else: - self.fail('Expected exception') - - -def find_all_tests(suite): - """Yields all the tests and their names from a given suite.""" - suites = [suite] - while suites: - s = suites.pop() - try: - suites.extend(s) - except TypeError: - yield s, '%s.%s.%s' % ( - s.__class__.__module__, - s.__class__.__name__, - s._testMethodName - ) - - -class BetterLoader(unittest.TestLoader): - """A nicer loader that solves two problems. First of all we are setting - up tests from different sources and we're doing this programmatically - which breaks the default loading logic so this is required anyways. - Secondly this loader has a nicer interpolation for test names than the - default one so you can just do ``run-tests.py ViewTestCase`` and it - will work. - """ - - def getRootSuite(self): - return suite() - - def loadTestsFromName(self, name, module=None): - root = self.getRootSuite() - if name == 'suite': - return root - - all_tests = [] - for testcase, testname in find_all_tests(root): - if testname == name or \ - testname.endswith('.' + name) or \ - ('.' + name + '.') in testname or \ - testname.startswith(name + '.'): - all_tests.append(testcase) - - if not all_tests: - raise LookupError('could not find test case for "%s"' % name) - - if len(all_tests) == 1: - return all_tests[0] - rv = unittest.TestSuite() - for test in all_tests: - rv.addTest(test) - return rv - - -def suite(): - from jinja2.testsuite import ext, filters, tests, core_tags, \ - loader, inheritance, imports, lexnparse, security, api, \ - regression, debug, utils, bytecode_cache, doctests - suite = unittest.TestSuite() - suite.addTest(ext.suite()) - suite.addTest(filters.suite()) - suite.addTest(tests.suite()) - suite.addTest(core_tags.suite()) - suite.addTest(loader.suite()) - suite.addTest(inheritance.suite()) - suite.addTest(imports.suite()) - suite.addTest(lexnparse.suite()) - suite.addTest(security.suite()) - suite.addTest(api.suite()) - suite.addTest(regression.suite()) - suite.addTest(debug.suite()) - suite.addTest(utils.suite()) - suite.addTest(bytecode_cache.suite()) - - # doctests will not run on python 3 currently. Too many issues - # with that, do not test that on that platform. - if PY2: - suite.addTest(doctests.suite()) - - return suite - - -def main(): - """Runs the testsuite as command line application.""" - try: - unittest.main(testLoader=BetterLoader(), defaultTest='suite') - except Exception as e: - print('Error: %s' % e) diff -Nru jinja2-2.7.3/jinja2/testsuite/lexnparse.py jinja2-2.8/jinja2/testsuite/lexnparse.py --- jinja2-2.7.3/jinja2/testsuite/lexnparse.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/lexnparse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,593 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.lexnparse - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - All the unittests regarding lexing, parsing and syntax. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment, Template, TemplateSyntaxError, \ - UndefinedError, nodes -from jinja2._compat import next, iteritems, text_type, PY2 -from jinja2.lexer import Token, TokenStream, TOKEN_EOF, \ - TOKEN_BLOCK_BEGIN, TOKEN_BLOCK_END - -env = Environment() - - -# how does a string look like in jinja syntax? -if PY2: - def jinja_string_repr(string): - return repr(string)[1:] -else: - jinja_string_repr = repr - - -class TokenStreamTestCase(JinjaTestCase): - test_tokens = [Token(1, TOKEN_BLOCK_BEGIN, ''), - Token(2, TOKEN_BLOCK_END, ''), - ] - - def test_simple(self): - ts = TokenStream(self.test_tokens, "foo", "bar") - assert ts.current.type is TOKEN_BLOCK_BEGIN - assert bool(ts) - assert not bool(ts.eos) - next(ts) - assert ts.current.type is TOKEN_BLOCK_END - assert bool(ts) - assert not bool(ts.eos) - next(ts) - assert ts.current.type is TOKEN_EOF - assert not bool(ts) - assert bool(ts.eos) - - def test_iter(self): - token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")] - assert token_types == ['block_begin', 'block_end', ] - - -class LexerTestCase(JinjaTestCase): - - def test_raw1(self): - tmpl = env.from_string('{% raw %}foo{% endraw %}|' - '{%raw%}{{ bar }}|{% baz %}{% endraw %}') - assert tmpl.render() == 'foo|{{ bar }}|{% baz %}' - - def test_raw2(self): - tmpl = env.from_string('1 {%- raw -%} 2 {%- endraw -%} 3') - assert tmpl.render() == '123' - - def test_balancing(self): - env = Environment('{%', '%}', '${', '}') - tmpl = env.from_string('''{% for item in seq - %}${{'foo': item}|upper}{% endfor %}''') - assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}" - - def test_comments(self): - env = Environment('', '{', '}') - tmpl = env.from_string('''\ -
      - -
    • {item}
    • - -
    ''') - assert tmpl.render(seq=list(range(3))) == ("
      \n
    • 0
    • \n " - "
    • 1
    • \n
    • 2
    • \n
    ") - - def test_string_escapes(self): - for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n': - tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char)) - assert tmpl.render() == char - assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668' - - def test_bytefallback(self): - from pprint import pformat - tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''') - assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär') - - def test_operators(self): - from jinja2.lexer import operators - for test, expect in iteritems(operators): - if test in '([{}])': - continue - stream = env.lexer.tokenize('{{ %s }}' % test) - next(stream) - assert stream.current.type == expect - - def test_normalizing(self): - for seq in '\r', '\r\n', '\n': - env = Environment(newline_sequence=seq) - tmpl = env.from_string('1\n2\r\n3\n4\n') - result = tmpl.render() - assert result.replace(seq, 'X') == '1X2X3X4' - - def test_trailing_newline(self): - for keep in [True, False]: - env = Environment(keep_trailing_newline=keep) - for template,expected in [ - ('', {}), - ('no\nnewline', {}), - ('with\nnewline\n', {False: 'with\nnewline'}), - ('with\nseveral\n\n\n', {False: 'with\nseveral\n\n'}), - ]: - tmpl = env.from_string(template) - expect = expected.get(keep, template) - result = tmpl.render() - assert result == expect, (keep, template, result, expect) - -class ParserTestCase(JinjaTestCase): - - def test_php_syntax(self): - env = Environment('', '', '') - tmpl = env.from_string('''\ -\ - - -''') - assert tmpl.render(seq=list(range(5))) == '01234' - - def test_erb_syntax(self): - env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>') - tmpl = env.from_string('''\ -<%# I'm a comment, I'm not interesting %>\ -<% for item in seq -%> - <%= item %> -<%- endfor %>''') - assert tmpl.render(seq=list(range(5))) == '01234' - - def test_comment_syntax(self): - env = Environment('', '${', '}', '') - tmpl = env.from_string('''\ -\ - - ${item} -''') - assert tmpl.render(seq=list(range(5))) == '01234' - - def test_balancing(self): - tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''') - assert tmpl.render() == 'bar' - - def test_start_comment(self): - tmpl = env.from_string('''{# foo comment -and bar comment #} -{% macro blub() %}foo{% endmacro %} -{{ blub() }}''') - assert tmpl.render().strip() == 'foo' - - def test_line_syntax(self): - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%') - tmpl = env.from_string('''\ -<%# regular comment %> -% for item in seq: - ${item} -% endfor''') - assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \ - list(range(5)) - - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##') - tmpl = env.from_string('''\ -<%# regular comment %> -% for item in seq: - ${item} ## the rest of the stuff -% endfor''') - assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \ - list(range(5)) - - def test_line_syntax_priority(self): - # XXX: why is the whitespace there in front of the newline? - env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#') - tmpl = env.from_string('''\ -/* ignore me. - I'm a multiline comment */ -## for item in seq: -* ${item} # this is just extra stuff -## endfor''') - assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2' - env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##') - tmpl = env.from_string('''\ -/* ignore me. - I'm a multiline comment */ -# for item in seq: -* ${item} ## this is just extra stuff - ## extra stuff i just want to ignore -# endfor''') - assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2' - - def test_error_messages(self): - def assert_error(code, expected): - try: - Template(code) - except TemplateSyntaxError as e: - assert str(e) == expected, 'unexpected error message' - else: - assert False, 'that was supposed to be an error' - - assert_error('{% for item in seq %}...{% endif %}', - "Encountered unknown tag 'endif'. Jinja was looking " - "for the following tags: 'endfor' or 'else'. The " - "innermost block that needs to be closed is 'for'.") - assert_error('{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}', - "Encountered unknown tag 'endfor'. Jinja was looking for " - "the following tags: 'elif' or 'else' or 'endif'. The " - "innermost block that needs to be closed is 'if'.") - assert_error('{% if foo %}', - "Unexpected end of template. Jinja was looking for the " - "following tags: 'elif' or 'else' or 'endif'. The " - "innermost block that needs to be closed is 'if'.") - assert_error('{% for item in seq %}', - "Unexpected end of template. Jinja was looking for the " - "following tags: 'endfor' or 'else'. The innermost block " - "that needs to be closed is 'for'.") - assert_error('{% block foo-bar-baz %}', - "Block names in Jinja have to be valid Python identifiers " - "and may not contain hyphens, use an underscore instead.") - assert_error('{% unknown_tag %}', - "Encountered unknown tag 'unknown_tag'.") - - -class SyntaxTestCase(JinjaTestCase): - - def test_call(self): - env = Environment() - env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g - tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}") - assert tmpl.render() == 'abdfh' - - def test_slicing(self): - tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}') - assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]' - - def test_attr(self): - tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}") - assert tmpl.render(foo={'bar': 42}) == '42|42' - - def test_subscript(self): - tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}") - assert tmpl.render(foo=[0, 1, 2]) == '0|2' - - def test_tuple(self): - tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}') - assert tmpl.render() == '()|(1,)|(1, 2)' - - def test_math(self): - tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}') - assert tmpl.render() == '1.5|8' - - def test_div(self): - tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}') - assert tmpl.render() == '1|1.5|1' - - def test_unary(self): - tmpl = env.from_string('{{ +3 }}|{{ -3 }}') - assert tmpl.render() == '3|-3' - - def test_concat(self): - tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}") - assert tmpl.render() == '[1, 2]foo' - - def test_compare(self): - tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|' - '{{ 2 == 2 }}|{{ 1 <= 1 }}') - assert tmpl.render() == 'True|True|True|True|True' - - def test_inop(self): - tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}') - assert tmpl.render() == 'True|False' - - def test_literals(self): - tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}') - assert tmpl.render().lower() == '[]|{}|()' - - def test_bool(self): - tmpl = env.from_string('{{ true and false }}|{{ false ' - 'or true }}|{{ not false }}') - assert tmpl.render() == 'False|True|True' - - def test_grouping(self): - tmpl = env.from_string('{{ (true and false) or (false and true) and not false }}') - assert tmpl.render() == 'False' - - def test_django_attr(self): - tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}') - assert tmpl.render() == '1|1' - - def test_conditional_expression(self): - tmpl = env.from_string('''{{ 0 if true else 1 }}''') - assert tmpl.render() == '0' - - def test_short_conditional_expression(self): - tmpl = env.from_string('<{{ 1 if false }}>') - assert tmpl.render() == '<>' - - tmpl = env.from_string('<{{ (1 if false).bar }}>') - self.assert_raises(UndefinedError, tmpl.render) - - def test_filter_priority(self): - tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}') - assert tmpl.render() == 'FOOBAR' - - def test_function_calls(self): - tests = [ - (True, '*foo, bar'), - (True, '*foo, *bar'), - (True, '*foo, bar=42'), - (True, '**foo, *bar'), - (True, '**foo, bar'), - (False, 'foo, bar'), - (False, 'foo, bar=42'), - (False, 'foo, bar=23, *args'), - (False, 'a, b=c, *d, **e'), - (False, '*foo, **bar') - ] - for should_fail, sig in tests: - if should_fail: - self.assert_raises(TemplateSyntaxError, - env.from_string, '{{ foo(%s) }}' % sig) - else: - env.from_string('foo(%s)' % sig) - - def test_tuple_expr(self): - for tmpl in [ - '{{ () }}', - '{{ (1, 2) }}', - '{{ (1, 2,) }}', - '{{ 1, }}', - '{{ 1, 2 }}', - '{% for foo, bar in seq %}...{% endfor %}', - '{% for x in foo, bar %}...{% endfor %}', - '{% for x in foo, %}...{% endfor %}' - ]: - assert env.from_string(tmpl) - - def test_trailing_comma(self): - tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}') - assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}' - - def test_block_end_name(self): - env.from_string('{% block foo %}...{% endblock foo %}') - self.assert_raises(TemplateSyntaxError, env.from_string, - '{% block x %}{% endblock y %}') - - def test_constant_casing(self): - for const in True, False, None: - tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % ( - str(const), str(const).lower(), str(const).upper() - )) - assert tmpl.render() == '%s|%s|' % (const, const) - - def test_test_chaining(self): - self.assert_raises(TemplateSyntaxError, env.from_string, - '{{ foo is string is sequence }}') - assert env.from_string('{{ 42 is string or 42 is number }}' - ).render() == 'True' - - def test_string_concatenation(self): - tmpl = env.from_string('{{ "foo" "bar" "baz" }}') - assert tmpl.render() == 'foobarbaz' - - def test_notin(self): - bar = range(100) - tmpl = env.from_string('''{{ not 42 in bar }}''') - assert tmpl.render(bar=bar) == text_type(not 42 in bar) - - def test_implicit_subscribed_tuple(self): - class Foo(object): - def __getitem__(self, x): - return x - t = env.from_string('{{ foo[1, 2] }}') - assert t.render(foo=Foo()) == u'(1, 2)' - - def test_raw2(self): - tmpl = env.from_string('{% raw %}{{ FOO }} and {% BAR %}{% endraw %}') - assert tmpl.render() == '{{ FOO }} and {% BAR %}' - - def test_const(self): - tmpl = env.from_string('{{ true }}|{{ false }}|{{ none }}|' - '{{ none is defined }}|{{ missing is defined }}') - assert tmpl.render() == 'True|False|None|True|False' - - def test_neg_filter_priority(self): - node = env.parse('{{ -1|foo }}') - assert isinstance(node.body[0].nodes[0], nodes.Filter) - assert isinstance(node.body[0].nodes[0].node, nodes.Neg) - - def test_const_assign(self): - constass1 = '''{% set true = 42 %}''' - constass2 = '''{% for none in seq %}{% endfor %}''' - for tmpl in constass1, constass2: - self.assert_raises(TemplateSyntaxError, env.from_string, tmpl) - - def test_localset(self): - tmpl = env.from_string('''{% set foo = 0 %}\ -{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\ -{{ foo }}''') - assert tmpl.render() == '0' - - def test_parse_unary(self): - tmpl = env.from_string('{{ -foo["bar"] }}') - assert tmpl.render(foo={'bar': 42}) == '-42' - tmpl = env.from_string('{{ -foo["bar"]|abs }}') - assert tmpl.render(foo={'bar': 42}) == '42' - - -class LstripBlocksTestCase(JinjaTestCase): - - def test_lstrip(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {% if True %}\n {% endif %}''') - assert tmpl.render() == "\n" - - def test_lstrip_trim(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string(''' {% if True %}\n {% endif %}''') - assert tmpl.render() == "" - - def test_no_lstrip(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {%+ if True %}\n {%+ endif %}''') - assert tmpl.render() == " \n " - - def test_lstrip_endline(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' hello{% if True %}\n goodbye{% endif %}''') - assert tmpl.render() == " hello\n goodbye" - - def test_lstrip_inline(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {% if True %}hello {% endif %}''') - assert tmpl.render() == 'hello ' - - def test_lstrip_nested(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {% if True %}a {% if True %}b {% endif %}c {% endif %}''') - assert tmpl.render() == 'a b c ' - - def test_lstrip_left_chars(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' abc {% if True %} - hello{% endif %}''') - assert tmpl.render() == ' abc \n hello' - - def test_lstrip_embeded_strings(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {% set x = " {% str %} " %}{{ x }}''') - assert tmpl.render() == ' {% str %} ' - - def test_lstrip_preserve_leading_newlines(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string('''\n\n\n{% set hello = 1 %}''') - assert tmpl.render() == '\n\n\n' - - def test_lstrip_comment(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(''' {# if True #} -hello - {#endif#}''') - assert tmpl.render() == '\nhello\n' - - def test_lstrip_angle_bracket_simple(self): - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string(''' <% if True %>hello <% endif %>''') - assert tmpl.render() == 'hello ' - - def test_lstrip_angle_bracket_comment(self): - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string(''' <%# if True %>hello <%# endif %>''') - assert tmpl.render() == 'hello ' - - def test_lstrip_angle_bracket(self): - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ - <%# regular comment %> - <% for item in seq %> -${item} ## the rest of the stuff - <% endfor %>''') - assert tmpl.render(seq=range(5)) == \ - ''.join('%s\n' % x for x in range(5)) - - def test_lstrip_angle_bracket_compact(self): - env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ - <%#regular comment%> - <%for item in seq%> -${item} ## the rest of the stuff - <%endfor%>''') - assert tmpl.render(seq=range(5)) == \ - ''.join('%s\n' % x for x in range(5)) - - def test_php_syntax_with_manual(self): - env = Environment('', '', '', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ - - - - ''') - assert tmpl.render(seq=range(5)) == '01234' - - def test_php_syntax(self): - env = Environment('', '', '', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ - - - - ''') - assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5)) - - def test_php_syntax_compact(self): - env = Environment('', '', '', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ - - - - ''') - assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5)) - - def test_erb_syntax(self): - env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', - lstrip_blocks=True, trim_blocks=True) - #env.from_string('') - #for n,r in env.lexer.rules.iteritems(): - # print n - #print env.lexer.rules['root'][0][0].pattern - #print "'%s'" % tmpl.render(seq=range(5)) - tmpl = env.from_string('''\ -<%# I'm a comment, I'm not interesting %> - <% for item in seq %> - <%= item %> - <% endfor %> -''') - assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5)) - - def test_erb_syntax_with_manual(self): - env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ -<%# I'm a comment, I'm not interesting %> - <% for item in seq -%> - <%= item %> - <%- endfor %>''') - assert tmpl.render(seq=range(5)) == '01234' - - def test_erb_syntax_no_lstrip(self): - env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ -<%# I'm a comment, I'm not interesting %> - <%+ for item in seq -%> - <%= item %> - <%- endfor %>''') - assert tmpl.render(seq=range(5)) == ' 01234' - - def test_comment_syntax(self): - env = Environment('', '${', '}', '', - lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string('''\ -\ - - ${item} -''') - assert tmpl.render(seq=range(5)) == '01234' - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TokenStreamTestCase)) - suite.addTest(unittest.makeSuite(LexerTestCase)) - suite.addTest(unittest.makeSuite(ParserTestCase)) - suite.addTest(unittest.makeSuite(SyntaxTestCase)) - suite.addTest(unittest.makeSuite(LstripBlocksTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/loader.py jinja2-2.8/jinja2/testsuite/loader.py --- jinja2-2.7.3/jinja2/testsuite/loader.py 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/loader.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.loader - ~~~~~~~~~~~~~~~~~~~~~~~ - - Test the loaders. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import os -import sys -import tempfile -import shutil -import unittest - -from jinja2.testsuite import JinjaTestCase, dict_loader, \ - package_loader, filesystem_loader, function_loader, \ - choice_loader, prefix_loader - -from jinja2 import Environment, loaders -from jinja2._compat import PYPY, PY2 -from jinja2.loaders import split_template_path -from jinja2.exceptions import TemplateNotFound - - -class LoaderTestCase(JinjaTestCase): - - def test_dict_loader(self): - env = Environment(loader=dict_loader) - tmpl = env.get_template('justdict.html') - assert tmpl.render().strip() == 'FOO' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - def test_package_loader(self): - env = Environment(loader=package_loader) - tmpl = env.get_template('test.html') - assert tmpl.render().strip() == 'BAR' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - def test_filesystem_loader(self): - env = Environment(loader=filesystem_loader) - tmpl = env.get_template('test.html') - assert tmpl.render().strip() == 'BAR' - tmpl = env.get_template('foo/test.html') - assert tmpl.render().strip() == 'FOO' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - def test_choice_loader(self): - env = Environment(loader=choice_loader) - tmpl = env.get_template('justdict.html') - assert tmpl.render().strip() == 'FOO' - tmpl = env.get_template('test.html') - assert tmpl.render().strip() == 'BAR' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - def test_function_loader(self): - env = Environment(loader=function_loader) - tmpl = env.get_template('justfunction.html') - assert tmpl.render().strip() == 'FOO' - self.assert_raises(TemplateNotFound, env.get_template, 'missing.html') - - def test_prefix_loader(self): - env = Environment(loader=prefix_loader) - tmpl = env.get_template('a/test.html') - assert tmpl.render().strip() == 'BAR' - tmpl = env.get_template('b/justdict.html') - assert tmpl.render().strip() == 'FOO' - self.assert_raises(TemplateNotFound, env.get_template, 'missing') - - def test_caching(self): - changed = False - class TestLoader(loaders.BaseLoader): - def get_source(self, environment, template): - return u'foo', None, lambda: not changed - env = Environment(loader=TestLoader(), cache_size=-1) - tmpl = env.get_template('template') - assert tmpl is env.get_template('template') - changed = True - assert tmpl is not env.get_template('template') - changed = False - - env = Environment(loader=TestLoader(), cache_size=0) - assert env.get_template('template') \ - is not env.get_template('template') - - env = Environment(loader=TestLoader(), cache_size=2) - t1 = env.get_template('one') - t2 = env.get_template('two') - assert t2 is env.get_template('two') - assert t1 is env.get_template('one') - t3 = env.get_template('three') - assert 'one' in env.cache - assert 'two' not in env.cache - assert 'three' in env.cache - - def test_dict_loader_cache_invalidates(self): - mapping = {'foo': "one"} - env = Environment(loader=loaders.DictLoader(mapping)) - assert env.get_template('foo').render() == "one" - mapping['foo'] = "two" - assert env.get_template('foo').render() == "two" - - def test_split_template_path(self): - assert split_template_path('foo/bar') == ['foo', 'bar'] - assert split_template_path('./foo/bar') == ['foo', 'bar'] - self.assert_raises(TemplateNotFound, split_template_path, '../foo') - - -class ModuleLoaderTestCase(JinjaTestCase): - archive = None - - def compile_down(self, zip='deflated', py_compile=False): - super(ModuleLoaderTestCase, self).setup() - log = [] - self.reg_env = Environment(loader=prefix_loader) - if zip is not None: - self.archive = tempfile.mkstemp(suffix='.zip')[1] - else: - self.archive = tempfile.mkdtemp() - self.reg_env.compile_templates(self.archive, zip=zip, - log_function=log.append, - py_compile=py_compile) - self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive)) - return ''.join(log) - - def teardown(self): - super(ModuleLoaderTestCase, self).teardown() - if hasattr(self, 'mod_env'): - if os.path.isfile(self.archive): - os.remove(self.archive) - else: - shutil.rmtree(self.archive) - self.archive = None - - def test_log(self): - log = self.compile_down() - assert 'Compiled "a/foo/test.html" as ' \ - 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log - assert 'Finished compiling templates' in log - assert 'Could not compile "a/syntaxerror.html": ' \ - 'Encountered unknown tag \'endif\'' in log - - def _test_common(self): - tmpl1 = self.reg_env.get_template('a/test.html') - tmpl2 = self.mod_env.get_template('a/test.html') - assert tmpl1.render() == tmpl2.render() - - tmpl1 = self.reg_env.get_template('b/justdict.html') - tmpl2 = self.mod_env.get_template('b/justdict.html') - assert tmpl1.render() == tmpl2.render() - - def test_deflated_zip_compile(self): - self.compile_down(zip='deflated') - self._test_common() - - def test_stored_zip_compile(self): - self.compile_down(zip='stored') - self._test_common() - - def test_filesystem_compile(self): - self.compile_down(zip=None) - self._test_common() - - def test_weak_references(self): - self.compile_down() - tmpl = self.mod_env.get_template('a/test.html') - key = loaders.ModuleLoader.get_template_key('a/test.html') - name = self.mod_env.loader.module.__name__ - - assert hasattr(self.mod_env.loader.module, key) - assert name in sys.modules - - # unset all, ensure the module is gone from sys.modules - self.mod_env = tmpl = None - - try: - import gc - gc.collect() - except: - pass - - assert name not in sys.modules - - # This test only makes sense on non-pypy python 2 - if PY2 and not PYPY: - def test_byte_compilation(self): - log = self.compile_down(py_compile=True) - assert 'Byte-compiled "a/test.html"' in log - tmpl1 = self.mod_env.get_template('a/test.html') - mod = self.mod_env.loader.module. \ - tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490 - assert mod.__file__.endswith('.pyc') - - def test_choice_loader(self): - log = self.compile_down() - - self.mod_env.loader = loaders.ChoiceLoader([ - self.mod_env.loader, - loaders.DictLoader({'DICT_SOURCE': 'DICT_TEMPLATE'}) - ]) - - tmpl1 = self.mod_env.get_template('a/test.html') - self.assert_equal(tmpl1.render(), 'BAR') - tmpl2 = self.mod_env.get_template('DICT_SOURCE') - self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE') - - def test_prefix_loader(self): - log = self.compile_down() - - self.mod_env.loader = loaders.PrefixLoader({ - 'MOD': self.mod_env.loader, - 'DICT': loaders.DictLoader({'test.html': 'DICT_TEMPLATE'}) - }) - - tmpl1 = self.mod_env.get_template('MOD/a/test.html') - self.assert_equal(tmpl1.render(), 'BAR') - tmpl2 = self.mod_env.get_template('DICT/test.html') - self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE') - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(LoaderTestCase)) - suite.addTest(unittest.makeSuite(ModuleLoaderTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/regression.py jinja2-2.8/jinja2/testsuite/regression.py --- jinja2-2.7.3/jinja2/testsuite/regression.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/regression.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.regression - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Tests corner cases and bugs. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \ - TemplateNotFound, PrefixLoader -from jinja2._compat import text_type - -env = Environment() - - -class CornerTestCase(JinjaTestCase): - - def test_assigned_scoping(self): - t = env.from_string(''' - {%- for item in (1, 2, 3, 4) -%} - [{{ item }}] - {%- endfor %} - {{- item -}} - ''') - assert t.render(item=42) == '[1][2][3][4]42' - - t = env.from_string(''' - {%- for item in (1, 2, 3, 4) -%} - [{{ item }}] - {%- endfor %} - {%- set item = 42 %} - {{- item -}} - ''') - assert t.render() == '[1][2][3][4]42' - - t = env.from_string(''' - {%- set item = 42 %} - {%- for item in (1, 2, 3, 4) -%} - [{{ item }}] - {%- endfor %} - {{- item -}} - ''') - assert t.render() == '[1][2][3][4]42' - - def test_closure_scoping(self): - t = env.from_string(''' - {%- set wrapper = "" %} - {%- for item in (1, 2, 3, 4) %} - {%- macro wrapper() %}[{{ item }}]{% endmacro %} - {{- wrapper() }} - {%- endfor %} - {{- wrapper -}} - ''') - assert t.render() == '[1][2][3][4]' - - t = env.from_string(''' - {%- for item in (1, 2, 3, 4) %} - {%- macro wrapper() %}[{{ item }}]{% endmacro %} - {{- wrapper() }} - {%- endfor %} - {%- set wrapper = "" %} - {{- wrapper -}} - ''') - assert t.render() == '[1][2][3][4]' - - t = env.from_string(''' - {%- for item in (1, 2, 3, 4) %} - {%- macro wrapper() %}[{{ item }}]{% endmacro %} - {{- wrapper() }} - {%- endfor %} - {{- wrapper -}} - ''') - assert t.render(wrapper=23) == '[1][2][3][4]23' - - -class BugTestCase(JinjaTestCase): - - def test_keyword_folding(self): - env = Environment() - env.filters['testing'] = lambda value, some: value + some - assert env.from_string("{{ 'test'|testing(some='stuff') }}") \ - .render() == 'teststuff' - - def test_extends_output_bugs(self): - env = Environment(loader=DictLoader({ - 'parent.html': '(({% block title %}{% endblock %}))' - })) - - t = env.from_string('{% if expr %}{% extends "parent.html" %}{% endif %}' - '[[{% block title %}title{% endblock %}]]' - '{% for item in [1, 2, 3] %}({{ item }}){% endfor %}') - assert t.render(expr=False) == '[[title]](1)(2)(3)' - assert t.render(expr=True) == '((title))' - - def test_urlize_filter_escaping(self): - tmpl = env.from_string('{{ "http://www.example.org/http://www.example.org/<foo' - - def test_loop_call_loop(self): - tmpl = env.from_string(''' - - {% macro test() %} - {{ caller() }} - {% endmacro %} - - {% for num1 in range(5) %} - {% call test() %} - {% for num2 in range(10) %} - {{ loop.index }} - {% endfor %} - {% endcall %} - {% endfor %} - - ''') - - assert tmpl.render().split() == [text_type(x) for x in range(1, 11)] * 5 - - def test_weird_inline_comment(self): - env = Environment(line_statement_prefix='%') - self.assert_raises(TemplateSyntaxError, env.from_string, - '% for item in seq {# missing #}\n...% endfor') - - def test_old_macro_loop_scoping_bug(self): - tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}' - '{% macro i() %}3{% endmacro %}{{ i() }}') - assert tmpl.render() == '123' - - def test_partial_conditional_assignments(self): - tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}') - assert tmpl.render(a=23) == '23' - assert tmpl.render(b=True) == '42' - - def test_stacked_locals_scoping_bug(self): - env = Environment(line_statement_prefix='#') - t = env.from_string('''\ -# for j in [1, 2]: -# set x = 1 -# for i in [1, 2]: -# print x -# if i % 2 == 0: -# set x = x + 1 -# endif -# endfor -# endfor -# if a -# print 'A' -# elif b -# print 'B' -# elif c == d -# print 'C' -# else -# print 'D' -# endif - ''') - assert t.render(a=0, b=False, c=42, d=42.0) == '1111C' - - def test_stacked_locals_scoping_bug_twoframe(self): - t = Template(''' - {% set x = 1 %} - {% for item in foo %} - {% if item == 1 %} - {% set x = 2 %} - {% endif %} - {% endfor %} - {{ x }} - ''') - rv = t.render(foo=[1]).strip() - assert rv == u'1' - - def test_call_with_args(self): - t = Template("""{% 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 %}""") - - assert [x.strip() for x in t.render(list_of_user=[{ - 'username':'apo', - 'realname':'something else', - 'description':'test' - }]).splitlines()] == [ - u'
    • apo

      ', - u'
      Realname
      ', - u'
      something else
      ', - u'
      Description
      ', - u'
      test
      ', - u'
      ', - u'
    ' - ] - - def test_empty_if_condition_fails(self): - self.assert_raises(TemplateSyntaxError, Template, '{% if %}....{% endif %}') - self.assert_raises(TemplateSyntaxError, Template, '{% if foo %}...{% elif %}...{% endif %}') - self.assert_raises(TemplateSyntaxError, Template, '{% for x in %}..{% endfor %}') - - def test_recursive_loop_bug(self): - tpl1 = Template(""" - {% for p in foo recursive%} - {{p.bar}} - {% for f in p.fields recursive%} - {{f.baz}} - {{p.bar}} - {% if f.rec %} - {{ loop(f.sub) }} - {% endif %} - {% endfor %} - {% endfor %} - """) - - tpl2 = Template(""" - {% for p in foo%} - {{p.bar}} - {% for f in p.fields recursive%} - {{f.baz}} - {{p.bar}} - {% if f.rec %} - {{ loop(f.sub) }} - {% endif %} - {% endfor %} - {% endfor %} - """) - - def test_else_loop_bug(self): - t = Template(''' - {% for x in y %} - {{ loop.index0 }} - {% else %} - {% for i in range(3) %}{{ i }}{% endfor %} - {% endfor %} - ''') - self.assertEqual(t.render(y=[]).strip(), '012') - - def test_correct_prefix_loader_name(self): - env = Environment(loader=PrefixLoader({ - 'foo': DictLoader({}) - })) - try: - env.get_template('foo/bar.html') - except TemplateNotFound as e: - assert e.name == 'foo/bar.html' - else: - assert False, 'expected error here' - - def test_contextfunction_callable_classes(self): - from jinja2.utils import contextfunction - class CallableClass(object): - @contextfunction - def __call__(self, ctx): - return ctx.resolve('hello') - - tpl = Template("""{{ callableclass() }}""") - output = tpl.render(callableclass = CallableClass(), hello = 'TEST') - expected = 'TEST' - - self.assert_equal(output, expected) - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(CornerTestCase)) - suite.addTest(unittest.makeSuite(BugTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/res/templates/broken.html jinja2-2.8/jinja2/testsuite/res/templates/broken.html --- jinja2-2.7.3/jinja2/testsuite/res/templates/broken.html 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/res/templates/broken.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -Before -{{ fail() }} -After diff -Nru jinja2-2.7.3/jinja2/testsuite/res/templates/foo/test.html jinja2-2.8/jinja2/testsuite/res/templates/foo/test.html --- jinja2-2.7.3/jinja2/testsuite/res/templates/foo/test.html 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/res/templates/foo/test.html 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -FOO diff -Nru jinja2-2.7.3/jinja2/testsuite/res/templates/syntaxerror.html jinja2-2.8/jinja2/testsuite/res/templates/syntaxerror.html --- jinja2-2.7.3/jinja2/testsuite/res/templates/syntaxerror.html 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/res/templates/syntaxerror.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -Foo -{% for item in broken %} - ... -{% endif %} diff -Nru jinja2-2.7.3/jinja2/testsuite/res/templates/test.html jinja2-2.8/jinja2/testsuite/res/templates/test.html --- jinja2-2.7.3/jinja2/testsuite/res/templates/test.html 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/res/templates/test.html 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -BAR diff -Nru jinja2-2.7.3/jinja2/testsuite/security.py jinja2-2.8/jinja2/testsuite/security.py --- jinja2-2.7.3/jinja2/testsuite/security.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/security.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,166 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.security - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - Checks the sandbox and other security features. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest - -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Environment -from jinja2.sandbox import SandboxedEnvironment, \ - ImmutableSandboxedEnvironment, unsafe -from jinja2 import Markup, escape -from jinja2.exceptions import SecurityError, TemplateSyntaxError, \ - TemplateRuntimeError -from jinja2._compat import text_type - - -class PrivateStuff(object): - - def bar(self): - return 23 - - @unsafe - def foo(self): - return 42 - - def __repr__(self): - return 'PrivateStuff' - - -class PublicStuff(object): - bar = lambda self: 23 - _foo = lambda self: 42 - - def __repr__(self): - return 'PublicStuff' - - -class SandboxTestCase(JinjaTestCase): - - def test_unsafe(self): - env = SandboxedEnvironment() - self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render, - foo=PrivateStuff()) - self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23') - - self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render, - foo=PublicStuff()) - self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23') - self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '') - self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '') - # security error comes from __class__ already. - self.assert_raises(SecurityError, env.from_string( - "{{ foo.__class__.__subclasses__() }}").render, foo=42) - - def test_immutable_environment(self): - env = ImmutableSandboxedEnvironment() - self.assert_raises(SecurityError, env.from_string( - '{{ [].append(23) }}').render) - self.assert_raises(SecurityError, env.from_string( - '{{ {1:2}.clear() }}').render) - - def test_restricted(self): - env = SandboxedEnvironment() - self.assert_raises(TemplateSyntaxError, env.from_string, - "{% for item.attribute in seq %}...{% endfor %}") - self.assert_raises(TemplateSyntaxError, env.from_string, - "{% for foo, bar.baz in seq %}...{% endfor %}") - - def test_markup_operations(self): - # adding two strings should escape the unsafe one - unsafe = '' - safe = Markup('username') - assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) - - # string interpolations are safe to use too - assert Markup('%s') % '' == \ - '<bad user>' - assert Markup('%(username)s') % { - 'username': '' - } == '<bad user>' - - # an escaped object is markup too - assert type(Markup('foo') + 'bar') is Markup - - # and it implements __html__ by returning itself - x = Markup("foo") - assert x.__html__() is x - - # it also knows how to treat __html__ objects - class Foo(object): - def __html__(self): - return 'awesome' - def __unicode__(self): - return 'awesome' - assert Markup(Foo()) == 'awesome' - assert Markup('%s') % Foo() == \ - 'awesome' - - # escaping and unescaping - assert escape('"<>&\'') == '"<>&'' - assert Markup("Foo & Bar").striptags() == "Foo & Bar" - assert Markup("<test>").unescape() == "" - - def test_template_data(self): - env = Environment(autoescape=True) - t = env.from_string('{% macro say_hello(name) %}' - '

    Hello {{ name }}!

    {% endmacro %}' - '{{ say_hello("foo") }}') - escaped_out = '

    Hello <blink>foo</blink>!

    ' - assert t.render() == escaped_out - assert text_type(t.module) == escaped_out - assert escape(t.module) == escaped_out - assert t.module.say_hello('foo') == escaped_out - assert escape(t.module.say_hello('foo')) == escaped_out - - def test_attr_filter(self): - env = SandboxedEnvironment() - tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}') - self.assert_raises(SecurityError, tmpl.render, cls=int) - - def test_binary_operator_intercepting(self): - def disable_op(left, right): - raise TemplateRuntimeError('that operator so does not work') - for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'): - env = SandboxedEnvironment() - env.binop_table['+'] = disable_op - t = env.from_string('{{ %s }}' % expr) - assert t.render(ctx) == rv - env.intercepted_binops = frozenset(['+']) - t = env.from_string('{{ %s }}' % expr) - try: - t.render(ctx) - except TemplateRuntimeError as e: - pass - else: - self.fail('expected runtime error') - - def test_unary_operator_intercepting(self): - def disable_op(arg): - raise TemplateRuntimeError('that operator so does not work') - for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'): - env = SandboxedEnvironment() - env.unop_table['-'] = disable_op - t = env.from_string('{{ %s }}' % expr) - assert t.render(ctx) == rv - env.intercepted_unops = frozenset(['-']) - t = env.from_string('{{ %s }}' % expr) - try: - t.render(ctx) - except TemplateRuntimeError as e: - pass - else: - self.fail('expected runtime error') - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SandboxTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/tests.py jinja2-2.8/jinja2/testsuite/tests.py --- jinja2-2.7.3/jinja2/testsuite/tests.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/tests.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.tests - ~~~~~~~~~~~~~~~~~~~~~~ - - Who tests the tests? - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import unittest -from jinja2.testsuite import JinjaTestCase - -from jinja2 import Markup, Environment - -env = Environment() - - -class TestsTestCase(JinjaTestCase): - - def test_defined(self): - tmpl = env.from_string('{{ missing is defined }}|{{ true is defined }}') - assert tmpl.render() == 'False|True' - - def test_even(self): - tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''') - assert tmpl.render() == 'False|True' - - def test_odd(self): - tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''') - assert tmpl.render() == 'True|False' - - def test_lower(self): - tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''') - assert tmpl.render() == 'True|False' - - def test_typechecks(self): - tmpl = env.from_string(''' - {{ 42 is undefined }} - {{ 42 is defined }} - {{ 42 is none }} - {{ none is none }} - {{ 42 is number }} - {{ 42 is string }} - {{ "foo" is string }} - {{ "foo" is sequence }} - {{ [1] is sequence }} - {{ range is callable }} - {{ 42 is callable }} - {{ range(5) is iterable }} - {{ {} is mapping }} - {{ mydict is mapping }} - {{ [] is mapping }} - ''') - class MyDict(dict): - pass - assert tmpl.render(mydict=MyDict()).split() == [ - 'False', 'True', 'False', 'True', 'True', 'False', - 'True', 'True', 'True', 'True', 'False', 'True', - 'True', 'True', 'False' - ] - - def test_sequence(self): - tmpl = env.from_string( - '{{ [1, 2, 3] is sequence }}|' - '{{ "foo" is sequence }}|' - '{{ 42 is sequence }}' - ) - assert tmpl.render() == 'True|True|False' - - def test_upper(self): - tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') - assert tmpl.render() == 'True|False' - - def test_sameas(self): - tmpl = env.from_string('{{ foo is sameas false }}|' - '{{ 0 is sameas false }}') - assert tmpl.render(foo=False) == 'True|False' - - def test_no_paren_for_arg1(self): - tmpl = env.from_string('{{ foo is sameas none }}') - assert tmpl.render(foo=None) == 'True' - - def test_escaped(self): - env = Environment(autoescape=True) - tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}') - assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestsTestCase)) - return suite diff -Nru jinja2-2.7.3/jinja2/testsuite/utils.py jinja2-2.8/jinja2/testsuite/utils.py --- jinja2-2.7.3/jinja2/testsuite/utils.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/jinja2/testsuite/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja2.testsuite.utils - ~~~~~~~~~~~~~~~~~~~~~~ - - Tests utilities jinja uses. - - :copyright: (c) 2010 by the Jinja Team. - :license: BSD, see LICENSE for more details. -""" -import gc -import unittest - -import pickle - -from jinja2.testsuite import JinjaTestCase - -from jinja2.utils import LRUCache, escape, object_type_repr - - -class LRUCacheTestCase(JinjaTestCase): - - def test_simple(self): - d = LRUCache(3) - d["a"] = 1 - d["b"] = 2 - d["c"] = 3 - d["a"] - d["d"] = 4 - assert len(d) == 3 - assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d - - def test_pickleable(self): - cache = LRUCache(2) - cache["foo"] = 42 - cache["bar"] = 23 - cache["foo"] - - for protocol in range(3): - copy = pickle.loads(pickle.dumps(cache, protocol)) - assert copy.capacity == cache.capacity - assert copy._mapping == cache._mapping - assert copy._queue == cache._queue - - -class HelpersTestCase(JinjaTestCase): - - def test_object_type_repr(self): - class X(object): - pass - self.assert_equal(object_type_repr(42), 'int object') - self.assert_equal(object_type_repr([]), 'list object') - self.assert_equal(object_type_repr(X()), - 'jinja2.testsuite.utils.X object') - self.assert_equal(object_type_repr(None), 'None') - self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis') - - -class MarkupLeakTestCase(JinjaTestCase): - - def test_markup_leaks(self): - counts = set() - for count in range(20): - for item in range(1000): - escape("foo") - escape("") - escape(u"foo") - escape(u"") - counts.add(len(gc.get_objects())) - assert len(counts) == 1, 'ouch, c extension seems to leak objects' - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(LRUCacheTestCase)) - suite.addTest(unittest.makeSuite(HelpersTestCase)) - - # this test only tests the c extension - if not hasattr(escape, 'func_code'): - suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) - - return suite diff -Nru jinja2-2.7.3/jinja2/utils.py jinja2-2.8/jinja2/utils.py --- jinja2-2.7.3/jinja2/utils.py 2014-06-06 16:47:05.000000000 +0000 +++ jinja2-2.8/jinja2/utils.py 2015-05-25 11:38:06.000000000 +0000 @@ -11,8 +11,9 @@ import re import errno from collections import deque +from threading import Lock from jinja2._compat import text_type, string_types, implements_iterator, \ - allocate_lock, url_quote + url_quote _word_split_re = re.compile(r'(\s+)') @@ -149,7 +150,7 @@ try: return open(filename, mode) except IOError as e: - if e.errno not in (errno.ENOENT, errno.EISDIR): + if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): raise @@ -182,7 +183,7 @@ return pformat(obj) -def urlize(text, trim_url_limit=None, nofollow=False): +def urlize(text, trim_url_limit=None, nofollow=False, target=None): """Converts any URLs in text into clickable links. Works on http://, https:// and www. links. Links can have trailing punctuation (periods, commas, close-parens) and leading punctuation (opening parens) and @@ -193,12 +194,18 @@ If nofollow is True, the URLs in link text will get a rel="nofollow" attribute. + + If target is not None, a target attribute will be added to the link. """ trim_url = lambda x, limit=trim_url_limit: limit is not None \ and (x[:limit] + (len(x) >=limit and '...' or '')) or x words = _word_split_re.split(text_type(escape(text))) nofollow_attr = nofollow and ' rel="nofollow"' or '' + if target is not None and isinstance(target, string_types): + target_attr = ' target="%s"' % target + else: + target_attr = '' for i, word in enumerate(words): match = _punctuation_re.match(word) if match: @@ -213,12 +220,12 @@ middle.endswith('.net') or middle.endswith('.com') )): - middle = '%s' % (middle, - nofollow_attr, trim_url(middle)) + middle = '%s' % (middle, + nofollow_attr, target_attr, trim_url(middle)) if middle.startswith('http://') or \ middle.startswith('https://'): - middle = '%s' % (middle, - nofollow_attr, trim_url(middle)) + middle = '%s' % (middle, + nofollow_attr, target_attr, trim_url(middle)) if '@' in middle and not middle.startswith('www.') and \ not ':' in middle and _simple_email_re.match(middle): middle = '%s' % (middle, middle) @@ -228,7 +235,7 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100): - """Generate some lorem impsum for the template.""" + """Generate some lorem ipsum for the template.""" from jinja2.constants import LOREM_IPSUM_WORDS from random import choice, randrange words = LOREM_IPSUM_WORDS.split() @@ -276,7 +283,7 @@ return Markup(u'\n'.join(u'

    %s

    ' % escape(x) for x in result)) -def unicode_urlencode(obj, charset='utf-8'): +def unicode_urlencode(obj, charset='utf-8', for_qs=False): """URL escapes a single bytestring or unicode string with the given charset if applicable to URL safe quoting under all rules that need to be considered under all supported Python versions. @@ -288,7 +295,11 @@ obj = text_type(obj) if isinstance(obj, text_type): obj = obj.encode(charset) - return text_type(url_quote(obj)) + safe = for_qs and b'' or b'/' + rv = text_type(url_quote(obj, safe)) + if for_qs: + rv = rv.replace('%20', '+') + return rv class LRUCache(object): @@ -309,7 +320,7 @@ self._popleft = self._queue.popleft self._pop = self._queue.pop self._remove = self._queue.remove - self._wlock = allocate_lock() + self._wlock = Lock() self._append = self._queue.append def __getstate__(self): diff -Nru jinja2-2.7.3/Jinja2.egg-info/PKG-INFO jinja2-2.8/Jinja2.egg-info/PKG-INFO --- jinja2-2.7.3/Jinja2.egg-info/PKG-INFO 2014-06-06 16:49:12.000000000 +0000 +++ jinja2-2.8/Jinja2.egg-info/PKG-INFO 2015-07-26 17:49:41.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: Jinja2 -Version: 2.7.3 +Version: 2.8 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 @@ -49,7 +49,11 @@ 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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: HTML diff -Nru jinja2-2.7.3/Jinja2.egg-info/requires.txt jinja2-2.8/Jinja2.egg-info/requires.txt --- jinja2-2.7.3/Jinja2.egg-info/requires.txt 2014-06-06 16:49:12.000000000 +0000 +++ jinja2-2.8/Jinja2.egg-info/requires.txt 2015-07-26 17:49:41.000000000 +0000 @@ -1,4 +1,4 @@ -markupsafe +MarkupSafe [i18n] -Babel>=0.8 +Babel>=0.8 \ No newline at end of file diff -Nru jinja2-2.7.3/Jinja2.egg-info/SOURCES.txt jinja2-2.8/Jinja2.egg-info/SOURCES.txt --- jinja2-2.7.3/Jinja2.egg-info/SOURCES.txt 2014-06-06 16:49:13.000000000 +0000 +++ jinja2-2.8/Jinja2.egg-info/SOURCES.txt 2015-07-26 17:49:41.000000000 +0000 @@ -4,7 +4,6 @@ MANIFEST.in Makefile README.rst -run-tests.py setup.cfg setup.py Jinja2.egg-info/PKG-INFO @@ -14,7 +13,9 @@ Jinja2.egg-info/not-zip-safe Jinja2.egg-info/requires.txt Jinja2.egg-info/top_level.txt +artwork/.DS_Store artwork/jinjalogo.svg +docs/.DS_Store docs/Makefile docs/api.rst docs/cache_extension.py @@ -102,25 +103,4 @@ jinja2/sandbox.py jinja2/tests.py jinja2/utils.py -jinja2/visitor.py -jinja2/testsuite/__init__.py -jinja2/testsuite/api.py -jinja2/testsuite/bytecode_cache.py -jinja2/testsuite/core_tags.py -jinja2/testsuite/debug.py -jinja2/testsuite/doctests.py -jinja2/testsuite/ext.py -jinja2/testsuite/filters.py -jinja2/testsuite/imports.py -jinja2/testsuite/inheritance.py -jinja2/testsuite/lexnparse.py -jinja2/testsuite/loader.py -jinja2/testsuite/regression.py -jinja2/testsuite/security.py -jinja2/testsuite/tests.py -jinja2/testsuite/utils.py -jinja2/testsuite/res/__init__.py -jinja2/testsuite/res/templates/broken.html -jinja2/testsuite/res/templates/syntaxerror.html -jinja2/testsuite/res/templates/test.html -jinja2/testsuite/res/templates/foo/test.html \ No newline at end of file +jinja2/visitor.py \ No newline at end of file diff -Nru jinja2-2.7.3/Makefile jinja2-2.8/Makefile --- jinja2-2.7.3/Makefile 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/Makefile 2015-05-25 09:40:18.000000000 +0000 @@ -1,5 +1,5 @@ test: - python run-tests.py + py.test develop: pip install --editable . @@ -14,8 +14,8 @@ $(MAKE) -C docs html dirhtml latex $(MAKE) -C docs/_build/latex all-pdf cd docs/_build/; mv html jinja-docs; zip -r jinja-docs.zip jinja-docs; mv jinja-docs html - scp -r docs/_build/dirhtml/* pocoo.org:/var/www/jinja.pocoo.org/docs/ - scp -r docs/_build/latex/Jinja2.pdf pocoo.org:/var/www/jinja.pocoo.org/docs/jinja-docs.pdf - scp -r docs/_build/jinja-docs.zip pocoo.org:/var/www/jinja.pocoo.org/docs/ + scp -r docs/_build/dirhtml/* flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/ + scp -r docs/_build/latex/Jinja2.pdf flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/jinja-docs.pdf + scp -r docs/_build/jinja-docs.zip flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/ .PHONY: test diff -Nru jinja2-2.7.3/PKG-INFO jinja2-2.8/PKG-INFO --- jinja2-2.7.3/PKG-INFO 2014-06-06 16:49:13.000000000 +0000 +++ jinja2-2.8/PKG-INFO 2015-07-26 17:49:41.000000000 +0000 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: Jinja2 -Version: 2.7.3 +Version: 2.8 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 @@ -49,7 +49,11 @@ 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: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing :: Markup :: HTML diff -Nru jinja2-2.7.3/README.rst jinja2-2.8/README.rst --- jinja2-2.7.3/README.rst 2014-06-06 16:45:43.000000000 +0000 +++ jinja2-2.8/README.rst 2015-05-25 09:40:18.000000000 +0000 @@ -23,10 +23,10 @@ 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. +Application logic is for the controller, but don't make the template designer's +life difficult by restricting functionality too much. -For more informations visit the new `Jinja2 webpage`_ and `documentation`_. +For more information visit the new `Jinja2 webpage`_ and `documentation`_. The `Jinja2 tip`_ is installable via `easy_install` with ``easy_install Jinja2==dev``. @@ -34,5 +34,16 @@ .. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security) .. _Django: http://www.djangoproject.com/ .. _Jinja2 webpage: http://jinja.pocoo.org/ -.. _documentation: http://jinja.pocoo.org/2/documentation/ -.. _Jinja2 tip: http://dev.pocoo.org/hg/jinja2-main/archive/tip.tar.gz#egg=Jinja2-dev +.. _documentation: http://jinja.pocoo.org/docs/ +.. _Jinja2 tip: http://jinja.pocoo.org/docs/intro/#as-a-python-egg-via-easy-install + +Builds +------ + ++---------------------+------------------------------------------------------------------------------+ +| ``master`` | .. image:: https://travis-ci.org/mitsuhiko/jinja2.svg?branch=master | +| | :target: https://travis-ci.org/mitsuhiko/jinja2 | ++---------------------+------------------------------------------------------------------------------+ +| ``2.7-maintenance`` | .. image:: https://travis-ci.org/mitsuhiko/jinja2.svg?branch=2.7-maintenance | +| | :target: https://travis-ci.org/mitsuhiko/jinja2 | ++---------------------+------------------------------------------------------------------------------+ diff -Nru jinja2-2.7.3/run-tests.py jinja2-2.8/run-tests.py --- jinja2-2.7.3/run-tests.py 2013-07-26 16:00:44.000000000 +0000 +++ jinja2-2.8/run-tests.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -#!/usr/bin/env python -import sys, os -sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) -from jinja2.testsuite import main -main() diff -Nru jinja2-2.7.3/setup.cfg jinja2-2.8/setup.cfg --- jinja2-2.7.3/setup.cfg 2014-06-06 16:49:13.000000000 +0000 +++ jinja2-2.8/setup.cfg 2015-07-26 17:49:41.000000000 +0000 @@ -1,8 +1,14 @@ +[wheel] +universal = 1 + +[aliases] +release = egg_info -RDb '' + +[pytest] +norecursedirs = .* *.egg *.egg-info env* artwork docs examples + [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 -[aliases] -release = egg_info -RDb '' - diff -Nru jinja2-2.7.3/setup.py jinja2-2.8/setup.py --- jinja2-2.7.3/setup.py 2014-06-06 16:49:12.000000000 +0000 +++ jinja2-2.8/setup.py 2015-07-26 17:49:40.000000000 +0000 @@ -35,29 +35,12 @@ .. _Jinja2 webpage: http://jinja.pocoo.org/ .. _documentation: http://jinja.pocoo.org/2/documentation/ """ -import sys - from setuptools import setup -# ignore the old '--with-speedups' flag -try: - speedups_pos = sys.argv.index('--with-speedups') -except ValueError: - pass -else: - del sys.argv[speedups_pos] - sys.stderr.write('*' * 74 + '\n') - sys.stderr.write('WARNING:\n') - sys.stderr.write(' the --with-speedups flag is deprecated\n') - sys.stderr.write(' For the actual speedups install the MarkupSafe ' - 'package.\n') - sys.stderr.write('*' * 74 + '\n') - - setup( name='Jinja2', - version='2.7.3', + version='2.8', url='http://jinja.pocoo.org/', license='BSD', author='Armin Ronacher', @@ -74,15 +57,18 @@ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Text Processing :: Markup :: HTML' ], - packages=['jinja2', 'jinja2.testsuite', 'jinja2.testsuite.res'], - install_requires=['markupsafe'], + packages=['jinja2'], + install_requires=['MarkupSafe'], extras_require={'i18n': ['Babel>=0.8']}, - test_suite='jinja2.testsuite.suite', include_package_data=True, entry_points=""" [babel.extractors]