[svn] again doc updates
authorArmin Ronacher <armin.ronacher@active-4.com>
Wed, 14 Mar 2007 17:43:57 +0000 (18:43 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Wed, 14 Mar 2007 17:43:57 +0000 (18:43 +0100)
--HG--
branch : trunk

docs/src/contextenv.txt [new file with mode: 0644]
docs/src/devintro.txt
docs/src/index.txt
docs/src/objects.txt [new file with mode: 0644]
jinja/datastructure.py

diff --git a/docs/src/contextenv.txt b/docs/src/contextenv.txt
new file mode 100644 (file)
index 0000000..28ca2e2
--- /dev/null
@@ -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
index 74d6dd0c1ee3c52a5a51fc388462643cd04af748..f8438342a87d9dcb014cb2229b4b079905f734db 100644 (file)
@@ -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
index 21a012b05fb94843eff362cc17180a56ad0dc617..ef8cab5fe4194c027f65494b45a32f4f33b5d03d 100644 (file)
@@ -16,6 +16,10 @@ Welcome in the Jinja documentation.
 
   - `Test Functions <tests.txt>`_
 
+  - `Global Objects <objects.txt>`_
+
+  - `Context and Environment <contextenv.txt>`_
+
   - `Translators <translators.txt>`_
 
   - `Framework Integration <frameworks.txt>`_
diff --git a/docs/src/objects.txt b/docs/src/objects.txt
new file mode 100644 (file)
index 0000000..357a3d6
--- /dev/null
@@ -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('<tr><th>%s</th><td>%r</td></tr>' %
+                          (escape(unicode(key)), escape(pformat(value))))
+        return '<table>%s</table>' % 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
index 9871b55f376f460bd44a88e61c3c17ec3c30d4d0..39625c3c7f633bf40d85c28980540738a942d5ff 100644 (file)
@@ -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: