From 94dd3d0f667c6239d8eb59c02f0762f5b7d82f40 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 14 Mar 2007 18:43:57 +0100 Subject: [PATCH] [svn] again doc updates --HG-- branch : trunk --- docs/src/contextenv.txt | 136 ++++++++++++++++++++++++++++++++++++++++ docs/src/devintro.txt | 3 + docs/src/index.txt | 4 ++ docs/src/objects.txt | 110 ++++++++++++++++++++++++++++++++ jinja/datastructure.py | 8 ++- 5 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 docs/src/contextenv.txt create mode 100644 docs/src/objects.txt diff --git a/docs/src/contextenv.txt b/docs/src/contextenv.txt new file mode 100644 index 0000000..28ca2e2 --- /dev/null +++ b/docs/src/contextenv.txt @@ -0,0 +1,136 @@ +======================= +Context and Environment +======================= + +The two central objects in Jinja are the `Environment` and `Context`. Both +are designed to be subclassed by applications if they need to extend Jinja. + +Environment +=========== + +The initialization parameters are already covered in the `Quickstart`_ thus +not repeated here. + +But beside those configurable instance variables there are some functions used +in the template evaluation code you may want to override: + +**def** `to_unicode` *(self, value)*: + + Called to convert variables to unicode. Per default this checks if the + value is already unicode. If not it's converted to unicode using the + charset defined on the environment. + + Also `None` is converted into an empty string per default. + +**def** `get_translator` *(self, context)*: + + Return the translator used for i18n. A translator is an object that + provides the two functions ``gettext(string)`` and + ``ngettext(singular, plural, n)``. Both of those functions have to + behave like the `ugettext` and `nugettext` functions described in the + python `gettext documentation`_. + + If you don't provide a translator a default one is used to switch + between singular and plural forms. + +**def** `apply_filters` *(self, value, context, filters)*: + + Now this function is a bit tricky and you usually don't have to override + it. It's used to apply filters on a value. The Jinja expression + ``{{ foo|escape|replace('a', 'b') }}`` calls the function with the + value of `foo` as first parameter, the current context as second and + a list of filters as third. The list looks like this: + + .. sourcecode:: python + + [('escape', ()), ('replace', (u'a', u'b'))] + + As you can see the filter `escape` is called without arguments whereas + `replace` is called with the two literal strings ``a`` and ``b``, both + unicode. The filters for the names are stored on ``self.filters`` in a + dict. Missing filters should raise a `FilterNotFound` exception. + +**def** `perform_test` *(self, context, testname, args, value, invert)*: + + Like `apply_filters` you usually don't override this one. It's the + callback function for tests (``foo is bar`` / ``foo is not bar``). + + The first parameter is the current contex, the second the name of + the test to perform. the third a tuple of arguments, the fourth is + the value to test. The last one is `True` if the test was performed + with the `is not` operator, `False` if with the `is` operator. + + Missing tests should raise a `TestNotFound` exception. + +**def** `get_attribute` *(self, obj, attributes)*: + + Get `attributes` from the object provided. The default implementation + performs security tests for each attribute. + +**def** `call_function` *(self, f, context, args, kwargs, dyn_args, dyn_kwargs)*: + + Call a function `f` with the arguments `args`, `kwargs`, `dyn_args` and + `dyn_kwargs` where `args` is a tuple and `kwargs` a dict. If `dyn_args` + is not `None` you have to add it to the arguments, if `dyn_kwargs` is + not `None` you have to update the `kwargs` with it. + + The default implementation performs some security checks. + +**def** `call_function_simple` *(self, f, context)*: + + Like `call_function` but without arguments. + +**def** `finish_var` *(self, value)*: + + Postprocess a variable before it's sent to the template. + +.. admonition:: Note + + The Enviornment class is defined in `jinja.environment.Environment` + but imported into the `jinja` package because it's often used. + +Context +======= + +Jinja wraps the variables passed to the template in a special class called a +context. This context supports variables on multiple layers and lazy (deferred) +objects. Often your application has a request object, database connection +object or something similar you want to access in filters, functions etc. + +Beacause of that you can easily subclass a context to add additional variables +or to change the way it behaves. + +**def** `pop` *(self)*: + + Pop the outermost layer and return it. + +**def** `push` *(self, data=None)*: + + Push a dict to the stack or an empty layer. + + Has to return the pushed object. + +**def** `to_dict` *(self)*: + + Flatten the context and convert it into a dict. + +**def** `__getitem__` *(self, name)*: + + Resolve an item. Per default this also resolves `Deferred` objects. + +**def** `__setitem__` *(self, name, value)*: + + Set an item in the outermost layer. + +**def** `__delitem__` *(self, name)*: + + Delete an item in the outermost layer. Do not raise exceptions if + the value does not exist. + +**def** `__contains__` *(self, name)*: + + Return `True` if `name` exists in the context. + + +.. _Quickstart: devintro.txt +.. _gettext documentation: http://docs.python.org/lib/module-gettext.html diff --git a/docs/src/devintro.txt b/docs/src/devintro.txt index 74d6dd0..f843834 100644 --- a/docs/src/devintro.txt +++ b/docs/src/devintro.txt @@ -60,6 +60,8 @@ Here the possible initialization parameters: `filters` dict of filters or the default filters if not defined. `tests` dict of tests of the default tests if not defined. +`context_class` the context class this template should use. See + the `context documentation`_ for more details. ========================= ================================================== All of these variables except those marked with a star (*) are modifiable after @@ -133,6 +135,7 @@ Writing tests is explained in the `test development`_ section. .. _installation: installation.txt +.. _context documentation: contextenv.txt .. _translators: translators.txt .. _loader: loaders.txt .. _filter development: filters.txt diff --git a/docs/src/index.txt b/docs/src/index.txt index 21a012b..ef8cab5 100644 --- a/docs/src/index.txt +++ b/docs/src/index.txt @@ -16,6 +16,10 @@ Welcome in the Jinja documentation. - `Test Functions `_ + - `Global Objects `_ + + - `Context and Environment `_ + - `Translators `_ - `Framework Integration `_ diff --git a/docs/src/objects.txt b/docs/src/objects.txt new file mode 100644 index 0000000..357a3d6 --- /dev/null +++ b/docs/src/objects.txt @@ -0,0 +1,110 @@ +============== +Global objects +============== + +This section covers the behavior of global objects in the Jinja namespace and +also the behavior of their attributes. + +Functions +========= + +`Filters`_ and `Tests`_ are special kind of functions you cannot call without +and argument. Global functions are different. Those are really just ordinary +python functions. You can register them on the global namespace easily: + +.. sourcecode:: python + + def my_function(): + return [1, 2, 3] + + env.globals['my_function'] = my_function + +In the template you can not call it like this: + +.. sourcecode:: jinja + + {{ my_function() }} + +Of couse you can pass any argument to it. But what if you want to access the +context or environment? In that case you have to decorate a function or mark +it (both work exactly the same, but the decorator way is probably nicer if you +have python >= 2.4): + +.. sourcecode:: python + + from cgi import escape + from pprint import pformat + from jinja.datastructure import contextcallable + + @contextcallable + def printcontext(env, context): + result = [] + for key, value in context.to_dict(): + result.append('%s%r' % + (escape(unicode(key)), escape(pformat(value)))) + return '%s
' % u'\n'.join(result) + +If this function is registered in the environment namespace then and called +from the template it should return the content of the context as simple html +template. Of course you can modify the context too. For more informations about +the context object have a look at the `context object`_ documentation. + +Deferred Values +=============== + +You can pass functions to the template that are called to provide values at +first access. Say you want to pass the list of recently written comments to +all templates. But not necessarily every template will render that. In that +situation you can create a function that returns that value and wrap it in an +`Deferred` object. The first time a template accesses the variable then the +`Deferred` object is resolved: + +.. sourcecode:: python + + from jinja.datastructure import Deferred + from yourapplication.models import Comments + + def get_recent_comments(env, context, name): + # do what ever you want here. + return Comments.get_recent(10) + + env.globals['recent_comments'] = Deferred(get_recent_comments) + +The function is always called with the same arguments. The first one is the +current environment, the second the context and the third is the name of the +variable. In this example ``recent_comments``. + +Unsafe Methods / Attributes +=========================== + +Because Jinja is sandboxed it provides several ways to prevent unsafe attribute +access. You can mark both attributes and methods as unsafe: + +.. sourcecode:: python + + from jinja.datastructure import unsafe + + class MyModel(...): + + # just give access to a and b. Default is all + jinja_allowed_attributes = ['a', 'b'] + + def __init__(self, ...): + ... + self.a = ... + self.b = ... + + # python2.4 way of marking methods + @unsafe + def save(self): + """Save the model.""" + + # python2.3 way for the same + def delete(self): + """Delete the model.""" + delete.jinja_unsafe_call = True + + +.. _Filters: filters.txt +.. _Tests: tests.txt +.. _context object: contextenv.txt diff --git a/jinja/datastructure.py b/jinja/datastructure.py index 9871b55..39625c3 100644 --- a/jinja/datastructure.py +++ b/jinja/datastructure.py @@ -117,7 +117,7 @@ class Deferred(object): self.factory = factory def __call__(self, context, name): - return self.factory(context, name) + return self.factory(context.environment, context, name) class Markup(unicode): @@ -192,6 +192,12 @@ class Context(object): if name in self.current: del self.current[name] + def __contains__(self, name): + for layer in self._stack: + if name in layer: + return True + return False + def __repr__(self): tmp = {} for d in self._stack: -- 2.26.2