From 85ddd3b3e5c594214c43eb1932e6dad203fde56b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 14 Mar 2007 17:34:34 +0100 Subject: [PATCH] [svn] doc changes + changes in the i18n system of jinja --HG-- branch : trunk --- docs/src/designerdoc.txt | 42 ++++++++++++++++++++++++++++ docs/src/index.txt | 2 ++ docs/src/recipies.txt | 55 +++++++++++++++++++++++++++++++++++++ jinja/datastructure.py | 28 +++++++++++++++---- jinja/defaults.py | 4 +-- jinja/environment.py | 23 +++++++++++++--- jinja/translators/python.py | 6 ++-- jinja/utils.py | 25 +++++++++++++++-- www/download.html | 2 +- www/index.html | 6 ++-- 10 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 docs/src/recipies.txt diff --git a/docs/src/designerdoc.txt b/docs/src/designerdoc.txt index bc53750..7f0c96c 100644 --- a/docs/src/designerdoc.txt +++ b/docs/src/designerdoc.txt @@ -493,6 +493,48 @@ template but outside of a visible block (thus outside of any block) will be available in all blocks below. This allows you to use `include` statements to load often used macros at once. +Undefined Variables +=================== + +If you have already worked with python you probably know about the fact that +undefined variables raise an exception. This is different in Jinja. There is a +special value called `undefined` that represents values that do not exist. + +This special variable works complete different from any variables you maybe +know. If you print it using ``{{ variable }}`` it will not appear because it's +literally empty. If you try to iterate over it, it will work. But no items +are returned. Comparing this value to any other value results in `false`. +Even if you compare it to itself: + +.. sourcecode:: jinja + + {{ undefined == undefined }} + will return false. Not even undefined is undefined :) + Use `is defined` / `is not defined`: + + {{ undefined is not defined }} + will return true. + +There are also some additional rules regarding this special value. Any +mathematical operators (``+``, ``-``, ``*``, ``/``) return the operand +as result: + +.. sourcecode:: jinja + + {{ undefined + "foo" }} + returns "foo" + + {{ undefined - 42 }} + returns 42. Note: not -42! + +In any expression `undefined` evaluates to `false`. It has no length, all +attribute calls return undefined, calling too: + +.. sourcecode:: jinja + + {{ undefined.attribute().attribute_too[42] }} + still returns `undefined`. + Internationalization ==================== diff --git a/docs/src/index.txt b/docs/src/index.txt index a826c2b..21a012b 100644 --- a/docs/src/index.txt +++ b/docs/src/index.txt @@ -25,3 +25,5 @@ Welcome in the Jinja documentation. - `Syntax Reference `_ - `Differences To Django `_ + + - `Recipies `_ diff --git a/docs/src/recipies.txt b/docs/src/recipies.txt new file mode 100644 index 0000000..f444f00 --- /dev/null +++ b/docs/src/recipies.txt @@ -0,0 +1,55 @@ +======== +Recipies +======== + +Here are some typical recipes for the usage of Jinja templates. + +Alternating Rows +================ + +If you want to have different styles for each row of a table or +list you can use the ``cycle`` tag: + +.. sourcecode:: html+jinja + +
    + {% for row in sequence %} +
  • {{ row|e }}
  • + {% endfor %} +
+ +``cycle`` can take an unlimited amount of strings. Each time this +tag is encountered the next item from the list is rendered. +If you pass it just one argument it's meant to be a sequence. + +Active Menu Item +================ + +Often you want to have a navigation bar with an active navigation +item. This is really simple to achieve. Because ``set`` tags outside +of ``blocks`` are global you can do something like this: + +**layout.html**: + + .. sourcecode:: html+jinja + + {% set navigation_bar = [ + ('/', 'index', 'Index'), + ('/downloads/', 'downloads', 'Downloads'), + ('/about/', 'about', 'About') + ] %} + ... + + ... + +**index.html**: + + .. sourcecode:: jinja + + {% extends "layout.html" %} + {% set active_page = "index" %} 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 %} -

Philosphy

+

Philosophy

- 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.

Features

    -- 2.26.2