From: Armin Ronacher
+ {% for row in sequence %}
+
+
+``cycle`` can take an unlimited amount of strings. Each time this
+tag is encountered the next item from the list is rendered.
+If you pass it just one argument it's meant to be a sequence.
+
+Active Menu Item
+================
+
+Often you want to have a navigation bar with an active navigation
+item. This is really simple to achieve. Because ``set`` tags outside
+of ``blocks`` are global you can do something like this:
+
+**layout.html**:
+
+ .. sourcecode:: html+jinja
+
+ {% set navigation_bar = [
+ ('/', 'index', 'Index'),
+ ('/downloads/', 'downloads', 'Downloads'),
+ ('/about/', 'about', 'About')
+ ] %}
+ ...
+
+ ...
+
+**index.html**:
+
+ .. sourcecode:: jinja
+
+ {% extends "layout.html" %}
+ {% set active_page = "index" %}
diff --git a/jinja/datastructure.py b/jinja/datastructure.py
index c15519f..9871b55 100644
--- a/jinja/datastructure.py
+++ b/jinja/datastructure.py
@@ -18,6 +18,22 @@ except NameError:
from jinja.exceptions import TemplateRuntimeError
+def contextcallable(f):
+ """
+ Mark a function context callable.
+ """
+ f.jinja_context_callable = True
+ return f
+
+
+def unsafe(f):
+ """
+ Mark function as unsafe.
+ """
+ f.jinja_unsafe_call = True
+ return f
+
+
class UndefinedType(object):
"""
An object that does not exist.
@@ -62,7 +78,13 @@ class UndefinedType(object):
return 0
def __float__(self):
- return 1
+ return 0.0
+
+ def __eq__(self, other):
+ return False
+
+ def __ne__(self, other):
+ return True
def __call__(self, *args, **kwargs):
return self
@@ -121,10 +143,6 @@ class Context(object):
# cache object used for filters and tests
self.cache = {}
- def get_translator(self):
- """Return the translator for i18n."""
- return FakeTranslator()
-
def pop(self):
"""Pop the last layer from the stack and return it."""
rv = self._stack.pop()
diff --git a/jinja/defaults.py b/jinja/defaults.py
index ddcc82b..b634dad 100644
--- a/jinja/defaults.py
+++ b/jinja/defaults.py
@@ -10,10 +10,10 @@
"""
from jinja.filters import FILTERS as DEFAULT_FILTERS
from jinja.tests import TESTS as DEFAULT_TESTS
-from jinja.utils import debug_context
+from jinja.utils import debug_context, safe_range
DEFAULT_NAMESPACE = {
- 'range': range,
+ 'range': safe_range,
'debug': debug_context
}
diff --git a/jinja/environment.py b/jinja/environment.py
index 78862b8..ee988b2 100644
--- a/jinja/environment.py
+++ b/jinja/environment.py
@@ -12,7 +12,7 @@ import re
from jinja.lexer import Lexer
from jinja.parser import Parser
from jinja.loaders import LoaderWrapper
-from jinja.datastructure import Undefined, Context, Markup
+from jinja.datastructure import Undefined, Context, Markup, FakeTranslator
from jinja.utils import escape
from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException
from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
@@ -102,6 +102,16 @@ class Environment(object):
except UnicodeError:
return str(value).decode(self.charset, 'ignore')
+ def get_translator(self, context):
+ """
+ Return the translator for i18n.
+
+ A translator is an object that provides the two functions
+ ``gettext(string)`` and ``ngettext(singular, plural, n)``. Note
+ that both of them have to return unicode!
+ """
+ return FakeTranslator()
+
def apply_filters(self, value, context, filters):
"""
Apply a list of filters on the variable.
@@ -150,7 +160,7 @@ class Environment(object):
node = getattr(node, name)
return node
- def call_function(self, f, args, kwargs, dyn_args, dyn_kwargs):
+ def call_function(self, f, context, args, kwargs, dyn_args, dyn_kwargs):
"""
Function call helper. Called for all functions that are passed
any arguments.
@@ -162,15 +172,20 @@ class Environment(object):
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
+ if getattr(f, 'jinja_context_callable', False):
+ args = (self, context) + args
return f(*args, **kwargs)
- def call_function_simple(self, f):
+ def call_function_simple(self, f, context):
"""
- Function call without arguments.
+ Function call without arguments. Because of the smaller signature and
+ fewer logic here we have a bit of redundant code.
"""
if getattr(f, 'jinja_unsafe_call', False) or \
getattr(f, 'alters_data', False):
raise SecurityException('unsafe function %r called' % f.__name__)
+ if getattr(f, 'jinja_context_callable', False):
+ return f(self, context)
return f()
def finish_var(self, value):
diff --git a/jinja/translators/python.py b/jinja/translators/python.py
index 4caddd9..6e2ef01 100644
--- a/jinja/translators/python.py
+++ b/jinja/translators/python.py
@@ -314,7 +314,7 @@ class PythonTranslator(Translator):
# add translation helpers if required
if self.require_translations:
lines.append(
- ' translator = context.get_translator()\n'
+ ' translator = environment.get_translator(context)\n'
' def translate(s, p=None, n=None, r=None):\n'
' if p is None:\n'
' return translator.gettext(s) % (r or {})\n'
@@ -716,8 +716,8 @@ class PythonTranslator(Translator):
else:
args.append(self.handle_node(arg))
if not (args or kwargs or star_args or dstar_args):
- return 'call_function_simple(%s)' % self.handle_node(node.node)
- return 'call_function(%s, %s, {%s}, %s, %s)' % (
+ return 'call_function_simple(%s, context)' % self.handle_node(node.node)
+ return 'call_function(%s, context, %s, {%s}, %s, %s)' % (
self.handle_node(node.node),
_to_tuple(args),
', '.join(['%r: %s' % i for i in kwargs.iteritems()]),
diff --git a/jinja/utils.py b/jinja/utils.py
index 262db19..d0f5706 100644
--- a/jinja/utils.py
+++ b/jinja/utils.py
@@ -23,6 +23,9 @@ try:
except ImportError:
deque = None
+#: number of maximal range items
+MAX_RANGE = 1000000
+
_debug_info_re = re.compile(r'^\s*\# DEBUG\(filename=(.*?), lineno=(.*?)\)$')
_escape_pairs = {
@@ -118,14 +121,30 @@ def find_translations(environment, source):
queue.extend(node.getChildNodes())
-def debug_context():
+def debug_context(env, context):
"""
Use this function in templates to get a printed context.
- Use this only in templates because it touches the stack.
"""
- context = sys._getframe(2).f_locals['context']
from pprint import pformat
return pformat(context.to_dict())
+debug_context.jinja_context_callable = True
+
+
+def safe_range(start, stop=None, step=None):
+ """
+ "Safe" form of range that does not generate too large lists.
+ """
+ # this also works with None since None is always smaller than
+ # any other value.
+ if start > MAX_RANGE:
+ start = MAX_RANGE
+ if stop > MAX_RANGE:
+ stop = MAX_RANGE
+ if step is None:
+ step = 1
+ if stop is None:
+ return range(0, start, step)
+ return range(start, stop, step)
# python2.4 and lower has a bug regarding joining of broken generators
diff --git a/www/download.html b/www/download.html
index 277b46e..379c717 100644
--- a/www/download.html
+++ b/www/download.html
@@ -26,7 +26,7 @@
easy_install,
you can do it using this command:
easy_install Pygments+
easy_install Jinja
You can also get the development source from subversion using this command:
diff --git a/www/index.html b/www/index.html index c4fb8b8..abe55ab 100644 --- a/www/index.html +++ b/www/index.html @@ -40,10 +40,10 @@ </ul> {% endblock %} -- Application logic is for the controller but don't try to make the live for the - template designer too hard by giving him too less functionality. + 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.