changeset 606:9ada030ad986

Changed the default error handling mode to "strict".
author cmlenz
date Mon, 27 Aug 2007 20:05:31 +0000
parents bc5faca93699
children 6da11936ad83
files UPGRADE.txt doc/index.txt doc/templates.txt doc/upgrade.txt genshi/template/base.py genshi/template/eval.py genshi/template/interpolation.py genshi/template/loader.py genshi/template/markup.py genshi/template/plugin.py genshi/template/tests/eval.py genshi/template/text.py
diffstat 11 files changed, 111 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -20,6 +20,7 @@
 for output generation on the web. The major feature is a template language,
 which is heavily inspired by Kid.
 
+* `Upgrading from Previous Versions <upgrade.html>`_
 * `Markup Streams <streams.html>`_
 * `Templating Basics <templates.html>`_
 * `XML Template Language <xml-templates.html>`_
--- a/doc/templates.txt
+++ b/doc/templates.txt
@@ -231,7 +231,7 @@
 
 Code blocks can import modules, define classes and functions, and basically do
 anything you can do in normal Python code. What code blocks can *not* do is to
-produce content that is included directly in the generated page.
+produce content that is emitted directly tp the generated output.
 
 .. note:: Using the ``print`` statement will print to the standard output
           stream, just as it does for other Python code in your application.
@@ -261,13 +261,56 @@
 Error Handling
 ==============
 
-By default, Genshi allows you to access variables that are not defined, without
-raising a ``NameError`` exception as regular Python code would:
+By default, Genshi raises an ``UndefinedError`` if a template expression
+attempts to access a variable that is not defined:
 
 .. code-block:: pycon
 
   >>> from genshi.template import MarkupTemplate
   >>> tmpl = MarkupTemplate('<p>${doh}</p>')
+  >>> tmpl.generate().render('xhtml')
+  Traceback (most recent call last):
+    ...
+  UndefinedError: "doh" not defined
+
+You can change this behavior by setting the variable lookup mode to "lenient".
+In that case, accessing undefined variables returns an `Undefined` object,
+meaning that the expression does not fail immediately. See below for details.
+
+If you need to check whether a variable exists in the template context, use the
+defined_ or the value_of_ function described below. To check for existence of
+attributes on an object, or keys in a dictionary, use the ``hasattr()``,
+``getattr()`` or ``get()`` functions, or the ``in`` operator, just as you would
+in regular Python code:
+
+  >>> from genshi.template import MarkupTemplate
+  >>> tmpl = MarkupTemplate('<p>${defined("doh")}</p>')
+  >>> print tmpl.generate().render('xhtml')
+  <p>False</p>
+
+.. note:: Lenient error handling was the default in Genshi prior to version 0.5.
+          Strict mode was introduced in version 0.4, and became the default in
+          0.5. The reason for this change was that the lenient error handling
+          was masking actual errors in templates, thereby also making it harder
+          to debug some problems.
+
+
+.. _`lenient`:
+
+Lenient Mode
+------------
+
+If you instruct Genshi to use the lenient variable lookup mode, it allows you
+to access variables that are not defined, without raising an ``UndefinedError``.
+
+This mode can be chosen by passing the ``lookup='lenient'`` keyword argument to
+the template initializer, or by passing the ``variable_lookup='lenient'``
+keyword argument to the ``TemplateLoader`` initializer:
+
+.. code-block:: pycon
+
+  >>> from genshi.template import MarkupTemplate
+  >>> tmpl = MarkupTemplate('<p>${doh}</p>', lookup='lenient')
   >>> print tmpl.generate().render('xhtml')
   <p></p>
 
@@ -277,7 +320,7 @@
 .. code-block:: pycon
 
   >>> from genshi.template import MarkupTemplate
-  >>> tmpl = MarkupTemplate('<p>${doh.oops}</p>')
+  >>> tmpl = MarkupTemplate('<p>${doh.oops}</p>', lookup='lenient')
   >>> print tmpl.generate().render('xhtml')
   Traceback (most recent call last):
     ...
@@ -289,42 +332,14 @@
 .. code-block:: pycon
 
   >>> from genshi.template import MarkupTemplate
-  >>> tmpl = MarkupTemplate('<p>${type(doh) is not Undefined}</p>')
+  >>> tmpl = MarkupTemplate('<p>${type(doh) is not Undefined}</p>',
+  ...                       lookup='lenient')
   >>> print tmpl.generate().render('xhtml')
   <p>False</p>
 
 Alternatively, the built-in functions defined_ or value_of_ can be used in this
 case.
 
-Strict Mode
------------
-
-In addition to the default "lenient" error handling, Genshi lets you use a less
-forgiving mode if you prefer errors blowing up loudly instead of being ignored
-silently.
-
-This mode can be chosen by passing the ``lookup='strict'`` keyword argument to
-the template initializer, or by passing the ``variable_lookup='strict'`` keyword
-argument to the ``TemplateLoader`` initializer:
-
-.. code-block:: pycon
-
-  >>> from genshi.template import MarkupTemplate
-  >>> tmpl = MarkupTemplate('<p>${doh}</p>', lookup='strict')
-  >>> print tmpl.generate().render('xhtml')
-  Traceback (most recent call last):
-    ...
-  UndefinedError: "doh" not defined
-
-When using strict mode, any reference to an undefined variable, as well as
-trying to access an non-existing item or attribute of an object, will cause an
-``UndefinedError`` to be raised immediately.
-
-.. note:: While this mode is currently not the default, it may be promoted to
-          the default in future versions of Genshi. In general, the default
-          lenient error handling mode can be considered dangerous as it silently
-          ignores typos.
-
 Custom Modes
 ------------
 
rename from UPGRADE.txt
rename to doc/upgrade.txt
--- a/UPGRADE.txt
+++ b/doc/upgrade.txt
@@ -1,19 +1,35 @@
 Upgrading Genshi
 ================
 
+
+.. contents:: Contents
+   :depth: 2
+.. sectnum::
+
+
 Upgrading from Genshi 0.4.x to 0.5.x
 ------------------------------------
 
-Genshi 0.5 introduces a new, alternative syntax for text templates, which is
-more flexible and powerful compared to the old syntax. For backwards
-compatibility, this new syntax is not used by default, though it will be in
-a future version. It is recommended that you migrate to using this new syntax.
-To do so, simply rename any references in your code to `TextTemplate` to
-`NewTextTemplate`. To explicitly use the old syntax, use `OldTextTemplate`
-instead, so that you can be sure you'll be using the same language when the
+Genshi 0.5 introduces a new, alternative syntax for text templates,
+which is more flexible and powerful compared to the old syntax. For
+backwards compatibility, this new syntax is not used by default,
+though it will be in a future version. It is recommended that you
+migrate to using this new syntax. To do so, simply rename any
+references in your code to ``TextTemplate`` to ``NewTextTemplate``. To
+explicitly use the old syntax, use ``OldTextTemplate`` instead, so
+that you can be sure you'll be using the same language when the
 default in Genshi is changed (at least until the old implementation is
 completely removed).
 
+The default error handling mode has been changed to "strict". This
+means that accessing variables not defined in the template data will
+now generate an immediate exception, as will accessing object
+attributes or dictionary keys that don't exist. If your templates rely
+on the old lenient behavior, you can configure Genshi to use that
+instead. See the documentation for details on how to do that. But be
+warned that lenient error handling may be removed completely in a
+future release.
+
 
 Upgrading from Genshi 0.3.x to 0.4.x
 ------------------------------------
@@ -21,19 +37,20 @@
 The modules ``genshi.filters`` and ``genshi.template`` have been
 refactored into packages containing multiple modules. While code using
 the regular APIs should continue to work without problems, you should
-make sure to remove any leftover traces of the ``template.py`` file on
-the installation path. This is not necessary when Genshi was installed
-as a Python egg.
+make sure to remove any leftover traces of the files ``filters.py``
+and ``template.py`` in the ``genshi`` package on the installation
+path (including the corresponding ``.pyc`` files). This is not
+necessary when Genshi was installed as a Python egg.
 
 Results of evaluating template expressions are no longer implicitly
 called if they are callable. If you have been using that feature, you
 will need to add the parenthesis to actually call the function.
 
-Instances of `genshi.core.Attrs` are now immutable. Filters
+Instances of ``genshi.core.Attrs`` are now immutable. Filters
 manipulating the attributes in a stream may need to be updated. Also,
-the `Attrs` class no longer automatically wraps all attribute names
-in `QName` objects, so users of the `Attrs` class need to do this
-themselves. See the documentation of the `Attrs` class for more
+the ``Attrs`` class no longer automatically wraps all attribute names
+in ``QName`` objects, so users of the ``Attrs`` class need to do this
+themselves. See the documentation of the ``Attrs`` class for more
 information.
 
 
@@ -44,22 +61,22 @@
 name change means that you will have to adjust your import statements
 and the namespace URI of XML templates, among other things:
 
- * The package name was changed from "markup" to "genshi". Please
-   adjust any import statements referring to the old package name.
- * The namespace URI for directives in Genshi XML templates has changed
-   from http://markup.edgewall.org/ to http://genshi.edgewall.org/.
-   Please update the xmlns:py declaration in your template files
-   accordingly.
+* The package name was changed from "markup" to "genshi". Please
+  adjust any import statements referring to the old package name.
+* The namespace URI for directives in Genshi XML templates has changed
+  from ``http://markup.edgewall.org/`` to
+  ``http://genshi.edgewall.org/``. Please update the ``xmlns:py``
+  declaration in your template files accordingly.
 
 Furthermore, due to the inclusion of a text-based template language,
-the class:
+the class::
 
-  `markup.template.Template`
+  markup.template.Template
 
-has been renamed to:
+has been renamed to::
 
-  `genshi.template.MarkupTemplate`
+  genshi.template.MarkupTemplate
 
 If you've been using the Template class directly, you'll need to
-update your code (a simple find/replace should do--the API itself
+update your code (a simple find/replace should do—the API itself
 did not change).
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -292,7 +292,7 @@
     serializer = None
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient', allow_exec=True):
+                 encoding=None, lookup='strict', allow_exec=True):
         """Initialize a template from either a string, a file-like object, or
         an already parsed markup stream.
         
@@ -307,8 +307,8 @@
         :param loader: the `TemplateLoader` to use for loading included
                        templates
         :param encoding: the encoding of the `source`
-        :param lookup: the variable lookup mechanism; either "lenient" (the
-                       default), "strict", or a custom lookup class
+        :param lookup: the variable lookup mechanism; either "strict" (the
+                       default), "lenient", or a custom lookup class
         :param allow_exec: whether Python code blocks in templates should be
                            allowed
         
--- a/genshi/template/eval.py
+++ b/genshi/template/eval.py
@@ -38,7 +38,7 @@
     """Abstract base class for the `Expression` and `Suite` classes."""
     __slots__ = ['source', 'code', 'ast', '_globals']
 
-    def __init__(self, source, filename=None, lineno=-1, lookup='lenient',
+    def __init__(self, source, filename=None, lineno=-1, lookup='strict',
                  xform=None):
         """Create the code object, either from a string, or from an AST node.
         
@@ -48,8 +48,8 @@
                          the code
         :param lineno: the number of the line on which the code was found
         :param lookup: the lookup class that defines how variables are looked
-                       up in the context. Can be either `LenientLookup` (the
-                       default), `StrictLookup`, or a custom lookup class
+                       up in the context; can be either "strict" (the default),
+                       "lenient", or a custom lookup class
         :param xform: the AST transformer that should be applied to the code;
                       if `None`, the appropriate transformation is chosen
                       depending on the mode
--- a/genshi/template/interpolation.py
+++ b/genshi/template/interpolation.py
@@ -31,7 +31,7 @@
 PREFIX = '$'
 
 def interpolate(text, basedir=None, filename=None, lineno=-1, offset=0,
-                lookup='lenient'):
+                lookup='strict'):
     """Parse the given string and extract expressions.
     
     This function is a generator that yields `TEXT` events for literal strings,
--- a/genshi/template/loader.py
+++ b/genshi/template/loader.py
@@ -77,7 +77,7 @@
     """
     def __init__(self, search_path=None, auto_reload=False,
                  default_encoding=None, max_cache_size=25, default_class=None,
-                 variable_lookup='lenient', allow_exec=True, callback=None):
+                 variable_lookup='strict', allow_exec=True, callback=None):
         """Create the template laoder.
         
         :param search_path: a list of absolute path names that should be
@@ -91,8 +91,8 @@
                                cache
         :param default_class: the default `Template` subclass to use when
                               instantiating templates
-        :param variable_lookup: the variable lookup mechanism; either "lenient"
-                                (the default), "strict", or a custom lookup
+        :param variable_lookup: the variable lookup mechanism; either "strict"
+                                (the default), "lenient", or a custom lookup
                                 class
         :param allow_exec: whether to allow Python code blocks in templates
         :param callback: (optional) a callback function that is invoked after a
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -67,7 +67,7 @@
     serializer = 'xml'
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient', allow_exec=True):
+                 encoding=None, lookup='strict', allow_exec=True):
         Template.__init__(self, source, basedir=basedir, filename=filename,
                           loader=loader, encoding=encoding, lookup=lookup,
                           allow_exec=allow_exec)
--- a/genshi/template/plugin.py
+++ b/genshi/template/plugin.py
@@ -62,7 +62,7 @@
         if loader_callback and not callable(loader_callback):
             raise ConfigurationError('loader callback must be a function')
 
-        lookup_errors = options.get('genshi.lookup_errors', 'lenient')
+        lookup_errors = options.get('genshi.lookup_errors', 'strict')
         if lookup_errors not in ('lenient', 'strict'):
             raise ConfigurationError('Unknown lookup errors mode "%s"' %
                                      lookup_errors)
--- a/genshi/template/tests/eval.py
+++ b/genshi/template/tests/eval.py
@@ -321,7 +321,8 @@
         self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)}))
 
     def test_access_undefined(self):
-        expr = Expression("nothing", filename='index.html', lineno=50)
+        expr = Expression("nothing", filename='index.html', lineno=50,
+                          lookup='lenient')
         retval = expr.evaluate({})
         assert isinstance(retval, Undefined)
         self.assertEqual('nothing', retval._name)
@@ -332,7 +333,8 @@
             def __repr__(self):
                 return '<Something>'
         something = Something()
-        expr = Expression('something.nil', filename='index.html', lineno=50)
+        expr = Expression('something.nil', filename='index.html', lineno=50,
+                          lookup='lenient')
         retval = expr.evaluate({'something': something})
         assert isinstance(retval, Undefined)
         self.assertEqual('nil', retval._name)
@@ -351,7 +353,8 @@
             def __repr__(self):
                 return '<Something>'
         something = Something()
-        expr = Expression('something["nil"]', filename='index.html', lineno=50)
+        expr = Expression('something["nil"]', filename='index.html', lineno=50,
+                          lookup='lenient')
         retval = expr.evaluate({'something': something})
         assert isinstance(retval, Undefined)
         self.assertEqual('nil', retval._name)
--- a/genshi/template/text.py
+++ b/genshi/template/text.py
@@ -128,7 +128,7 @@
     _ESCAPE_RE = r'\\\n|\\(\\)|\\(%s)|\\(%s)'
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient', allow_exec=False,
+                 encoding=None, lookup='strict', allow_exec=False,
                  delims=('{%', '%}', '{#', '#}')):
         self.delimiters = delims
         Template.__init__(self, source, basedir=basedir, filename=filename,
Copyright (C) 2012-2017 Edgewall Software