From 762079cd25921596b9e1227e6c3bad6d465c430f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 8 May 2008 23:57:56 +0200 Subject: [PATCH] more updates on the extension API --HG-- branch : trunk --- docs/_static/metal.png | Bin 0 -> 21543 bytes docs/_static/style.css | 5 +- docs/api.rst | 2 +- docs/cache_extension.py | 39 +++++++-------- docs/extensions.rst | 104 ++++++++++++++++++++-------------------- jinja2/environment.py | 9 ++++ jinja2/ext.py | 74 ++++++++++++++-------------- jinja2/nodes.py | 3 +- jinja2/parser.py | 11 ++++- 9 files changed, 132 insertions(+), 115 deletions(-) create mode 100644 docs/_static/metal.png diff --git a/docs/_static/metal.png b/docs/_static/metal.png new file mode 100644 index 0000000000000000000000000000000000000000..97166f131933d6808f8098ba09075c4f973dbe6c GIT binary patch literal 21543 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 NPFq9&ezh7R;y>T5HH!cM literal 0 HcmV?d00001 diff --git a/docs/_static/style.css b/docs/_static/style.css index 46748d0..097b229 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -179,7 +179,7 @@ a:hover { } pre { - background-color: #f9f9f9; + background: #ededed url(metal.png); border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; padding: 5px; @@ -296,7 +296,8 @@ table.indextable dl dd a { dl.function dt, dl.class dt, -dl.exception dt { +dl.exception dt, +dl.method dt { font-weight: normal; } diff --git a/docs/api.rst b/docs/api.rst index 82bb531..4ce79ff 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -48,7 +48,7 @@ High Level API -------------- .. autoclass:: jinja2.environment.Environment([options]) - :members: from_string, get_template, join_path, parse, lex + :members: from_string, get_template, join_path, parse, lex, extend .. attribute:: shared diff --git a/docs/cache_extension.py b/docs/cache_extension.py index 19839fe..c9ed92c 100644 --- a/docs/cache_extension.py +++ b/docs/cache_extension.py @@ -2,19 +2,18 @@ from jinja2 import nodes from jinja2.ext import Extension -class CacheExtension(Extension): - """Adds support for fragment caching to Jinja2.""" +class FragmentCacheExtension(Extension): + # a set of names that trigger the extension. tags = set(['cache']) def __init__(self, environment): - Extension.__init__(self, environment) + super(FragmentCacheExtension, self).__init__(environment) - # default dummy implementations. If the class does not implement - # those methods we add some noop defaults. - if not hasattr(environment, 'add_fragment_to_cache'): - environment.add_fragment_to_cache = lambda n, v, t: None - if not hasattr(environment, 'load_fragment_from_cache'): - environment.load_fragment_from_cache = lambda n: None + # add the defaults to the environment + environment.extend( + fragment_cache_prefix='', + fragment_cache=None + ) def parse(self, parser): # the first token is the token that started the tag. In our case @@ -26,13 +25,10 @@ class CacheExtension(Extension): # now we parse a single expression that is used as cache key. args = [parser.parse_expression()] - # if there is a comma, someone provided the timeout. parse the - # timeout then - if parser.stream.current.type is 'comma': - parser.stream.next() + # if there is a comma, the user provided a timeout. If not use + # None as second parameter. + if parser.skip_comma(): args.append(parser.parse_expression()) - - # otherwise set the timeout to `None` else: args.append(nodes.Const(None)) @@ -49,13 +45,14 @@ class CacheExtension(Extension): def _cache_support(self, name, timeout, caller): """Helper callback.""" - # try to load the block from the cache - rv = self.environment.load_fragment_from_cache(name) - if rv is not None: - return rv + key = self.environment.fragment_cache_prefix + name + # try to load the block from the cache # if there is no fragment in the cache, render it and store # it in the cache. - rv = caller() - self.environment.add_fragment_to_cache(name, rv, timeout) + rv = self.environment.fragment_cache.get(key) + if rv is None: + return rv + rv = caller() + self.environment.fragment_cache.add(key, rv, timeout) return rv diff --git a/docs/extensions.rst b/docs/extensions.rst index 094afa9..7be9f12 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -3,8 +3,6 @@ Extensions ========== -.. module:: jinja2.ext - 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 code into a reusable class like adding support for internationalization. @@ -32,9 +30,46 @@ 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 `_`, `gettext` and `ngettext` functions are added to -the template globals. A internationalized application has to override those -methods with more useful versions. +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 optoinally a `ngettext` function into the +namespace. Either globally or for each rendering. + +After enabling of the extension the environment provides the following +additional methods: + +.. method:: jinja2.Environment.install_gettext_translations(translations) + + Installs a translation globally for that environment. The tranlations + 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. + +.. method:: jinja2.Environment.install_null_translations() + + Install dummy gettext functions. This is useful if you want to prepare + the application for internationalization but don't want to implement the + full internationalization system yet. + +.. method:: jinja2.Environment.uninstall_gettext_translations() + + Uninstall the translations again. + +.. method:: jinja2.Environment.extract_translations(source) + + Extract localizable strings from the given template node or source. + + For every string found this function yields a ``(lineno, function, + message)`` tuple, where: + + * `lineno` is the number of the line on which the string was found, + * `function` is the name of the `gettext` function used (if the + string was extracted from embedded Python code), and + * `message` is the string itself (a `unicode` object, or a tuple + of `unicode` objects for functions with multiple string arguments). + + If `Babel`_ is installed :ref:`the babel integration ` + can be used to extract strings for babel. For a web application that is available in multiple languages but gives all the users the same language (for example a multilingual forum software @@ -43,40 +78,16 @@ translation methods to the environment at environment generation time:: translations = get_gettext_translations() env = Environment(extensions=['jinja.ext.i18n']) - env.globals.update( - gettext=translations.ugettext, - ngettext=translations.ungettext - ) + env.install_gettext_translations(translations) The `get_gettext_translations` function would return the translator for the -current configuration. Keep in mind that Jinja2 uses unicode internally so -you must pass the `ugettext` and `ungettext` functions to the template. - -The default `_` function injected by the extension calls `gettext` -automatically. - -If you want to pass the gettext function into the context at render time -because you don't know the language/translations earlier and the optimizer -is enabled (which it is per default), you have to unregister the `gettext` -and `ugettext` functions first:: - - del env.globals['gettext'], env.globals['ugettext'] - -Jinja2 also provides a way to extract recognized strings. For one the -`jinja.ext` module provides a function that can return all the occurences -of gettext calls in a node (as returned by :meth:`Environment.parse`): - -.. autofunction:: extract_from_ast - -If `babel`_ is installed :ref:`the babel integration ` -can be used to. +current configuration. (For example by using `gettext.find`) The usage of the `i18n` extension for template designers is covered as part :ref:`of the template documentation `. - .. _gettext: http://docs.python.org/dev/library/gettext -.. _babel: http://babel.edgewall.org/ +.. _Babel: http://babel.edgewall.org/ .. _writing-extensions: @@ -84,6 +95,8 @@ The usage of the `i18n` extension for template designers is covered as part Writing Extensions ------------------ +.. module:: jinja2.ext + By writing extensions you can add custom tags to Jinja2. This is a non trival 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 @@ -92,32 +105,19 @@ useful, another one would be fragment caching. Example Extension ~~~~~~~~~~~~~~~~~ -The following example implements a `cache` tag for Jinja2: +The following example implements a `cache` tag for Jinja2 by using the +`Werkzeug`_ caching contrib module: .. literalinclude:: cache_extension.py :language: python -In order to use the cache extension it makes sense to subclass the environment -to implement the `add_fragment_to_cache` and `load_fragment_from_cache` -methods. The following example shows how to use the `Werkzeug`_ caching -with the extension from above:: +And here is how you use it in an environment:: from jinja2 import Environment from werkzeug.contrib.cache import SimpleCache - cache = SimpleCache() - cache_prefix = 'tempalte_fragment/' - - class MyEnvironment(Environment): - - def __init__(self): - Environment.__init__(self, extensions=[CacheExtension]) - - def add_fragment_to_cache(self, key, value, timeout): - cache.add(cache_prefix + key, value, timeout) - - def load_fragment_from_cache(self, key): - return cache.get(cache_prefix + key) + env = Environment(extensions=[FragmentCacheExtension]) + env.fragment_cache = SimpleCache() .. _Werkzeug: http://werkzeug.pocoo.org/ @@ -147,8 +147,8 @@ expressions of different types. The following methods may be used by extensions: .. autoclass:: jinja2.parser.Parser - :members: parse_expression, parse_tuple, parse_statements, ignore_colon, - free_identifier + :members: parse_expression, parse_tuple, parse_statements, skip_colon, + skip_comma, free_identifier .. attribute:: filename diff --git a/jinja2/environment.py b/jinja2/environment.py index e771dd6..2fbe217 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -222,6 +222,15 @@ class Environment(object): _environment_sanity_check(self) + def extend(self, **attributes): + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.iteritems(): + if not hasattr(self, key): + setattr(self, key, value) + def overlay(self, block_start_string=missing, block_end_string=missing, variable_start_string=missing, variable_end_string=missing, comment_start_string=missing, comment_end_string=missing, diff --git a/jinja2/ext.py b/jinja2/ext.py index 22adf82..f60b85a 100644 --- a/jinja2/ext.py +++ b/jinja2/ext.py @@ -40,6 +40,17 @@ class Extension(object): may not store environment specific data on `self`. The reason for this is that an extension can be bound to another environment (for overlays) by creating a copy and reassigning the `environment` attribute. + + As extensions are created by the environment they cannot accept any + arguments for configuration. One may want to work around that by using + a factory function, but that is not possible as extensions are identified + by their import name. The correct way to configure the extension is + storing the configuration values on the environment. Because this way the + environment ends up acting as central configuration storage the + attributes may clash which is why extensions have to ensure that the names + they choose for configuration are not too generic. ``prefix`` for example + is a terrible name, ``fragment_cache_prefix`` on the other hand is a good + name as includes the name of the extension (fragment cache). """ __metaclass__ = ExtensionRegistry @@ -74,49 +85,40 @@ class Extension(object): return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno) -class CacheExtension(Extension): - """An example extension that adds cacheable blocks.""" - tags = set(['cache']) +class InternationalizationExtension(Extension): + """This extension adds gettext support to Jinja2.""" + tags = set(['trans']) def __init__(self, environment): Extension.__init__(self, environment) - environment.globals['__cache_ext_support'] = self.cache_support - - def cache_support(self, name, timeout, caller): - """Helper for the cache_fragment function.""" - if not hasattr(environment, 'cache_support'): - return caller() - args = [name] - if timeout is not None: - args.append(timeout) - return self.environment.cache_support(generate=caller, *args) + environment.globals['_'] = contextfunction(lambda c, x: c['gettext'](x)) + environment.extend( + install_gettext_translations=self._install, + install_null_translations=self._install_null, + uninstall_gettext_translations=self._uninstall, + extract_translations=self._extract + ) - def parse(self, parser): - lineno = parser.stream.next().lineno - args = [parser.parse_expression()] - if parser.stream.current.type is 'comma': - parser.stream.next() - args.append(parser.parse_expression()) - else: - args.append(nodes.Const(None, lineno=lineno)) - body = parser.parse_statements(('name:endcache',), drop_needle=True) - return nodes.CallBlock( - nodes.Call(nodes.Name('__cache_ext_support', 'load', lineno=lineno), - args, [], None, None), [], [], body, lineno=lineno + def _install(self, translations): + self.environment.globals.update( + gettext=translations.ugettext, + ngettext=translations.ungettext ) + def _install_null(self): + self.environment.globals.update( + gettext=lambda x: x, + ngettext=lambda s, p, n: (n != 1 and (p,) or (s,))[0] + ) -class InternationalizationExtension(Extension): - """This extension adds gettext support to Jinja.""" - tags = set(['trans']) + def _uninstall(self, translations): + for key in 'gettext', 'ngettext': + self.environment.globals.pop(key, None) - def __init__(self, environment): - Extension.__init__(self, environment) - environment.globals.update({ - '_': contextfunction(lambda c, x: c['gettext'](x)), - 'gettext': lambda x: x, - 'ngettext': lambda s, p, n: (s, p)[n != 1] - }) + def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): + if isinstance(source, basestring): + source = self.environment.parse(source) + return extract_from_ast(source, gettext_functions) def parse(self, parser): """Parse a translatable tag.""" @@ -132,7 +134,7 @@ class InternationalizationExtension(Extension): parser.stream.expect('comma') # skip colon for python compatibility - if parser.ignore_colon(): + if parser.skip_colon(): break name = parser.stream.expect('name') diff --git a/jinja2/nodes.py b/jinja2/nodes.py index 0d921a7..2519682 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -742,7 +742,8 @@ class ImportedName(Expr): class InternalName(Expr): """An internal name in the compiler. You cannot create these nodes - yourself but the parser provides a `free_identifier` method that creates + yourself but the parser provides a + :meth:`~jinja2.parser.Parser.free_identifier` method that creates a new identifier for you. This identifier is not available from the template and is not threated specially by the compiler. """ diff --git a/jinja2/parser.py b/jinja2/parser.py index 91a848b..427cb05 100644 --- a/jinja2/parser.py +++ b/jinja2/parser.py @@ -42,13 +42,20 @@ class Parser(object): 'rparen') or \ self.stream.current.test('name:in') - def ignore_colon(self): + def skip_colon(self): """If there is a colon, skip it and return `True`, else `False`.""" if self.stream.current.type is 'colon': self.stream.next() return True return False + def skip_comma(self): + """If there is a comma, skip it and return `True`, else `False`.""" + if self.stream.current.type is 'comma': + self.stream.next() + return True + return False + def free_identifier(self, lineno=None): """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" self._last_identifier += 1 @@ -100,7 +107,7 @@ class Parser(object): can be set to `True` and the end token is removed. """ # the first token may be a colon for python compatibility - self.ignore_colon() + self.skip_colon() # in the future it would be possible to add whole code sections # by adding some sort of end of statement token and parsing those here. -- 2.26.2