diff --git a/CHANGES.txt b/CHANGES.txt index 7d8765a..b1533c7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,8 @@ Changes ======= -Next release ------------- +0.6.0 (2010-05-02) +------------------ - (Hopefully) fix intermittent datetime-granularity-related test failures. @@ -11,10 +11,18 @@ Next release error message formatting, which may impact you if you were feeding colander an error message template. +- Depend on ``translationstring`` package for internationalization. + - New argument to ``colander.String`` constructor: ``allow_empty``. This is a boolean representing whether an empty string is a valid value during deserialization, defaulting to ``False``. +- Add minimal documentation about the composition of a + colander.Invalid exception to the narrative docs. + +- Add (existing, but previously non-API) colander.Invalid attributes + to its interface within the API documentation. + 0.5.2 (2010-04-09) ------------------ diff --git a/colander/__init__.py b/colander/__init__.py index 2165ad6..1039cbb 100644 --- a/colander/__init__.py +++ b/colander/__init__.py @@ -30,8 +30,8 @@ class Invalid(Exception): argument, defaulting to ``None``. The ``msg`` argument is a freeform field indicating the error circumstance. - The construct additionally may receive a ``value`` keyword, - indicating the value related to the error. + The constructor additionally may receive a an optional ``value`` + keyword, indicating the value related to the error. """ pos = None parent = None @@ -91,7 +91,11 @@ class Invalid(Exception): raise KeyError(name) def paths(self): - """ Return all paths through the exception graph """ + """ A generator which returns each path through the exception + graph. Each path is represented as a tuple of exception + nodes. Within each tuple, the leftmost item will represent + the root schema node, the rightmost item will represent the + leaf schema node.""" def traverse(node, stack): stack.append(node) @@ -112,8 +116,8 @@ class Invalid(Exception): return str(self.node.name) def asdict(self): - """ Return a dictionary containing an error report for this - exception""" + """ Return a dictionary containing a basic + (non-language-translated) error report for this exception""" paths = self.paths() errors = {} for path in paths: diff --git a/docs/.static/repoze.css b/docs/.static/repoze.css index 79926b1..dd7646c 100644 --- a/docs/.static/repoze.css +++ b/docs/.static/repoze.css @@ -20,3 +20,14 @@ div.related a { color: #dad3bd !important; } +/* override the justify text align of the default */ + +div.body p { + text-align: left !important; +} + +/* fix google chrome
 tag renderings */
+
+pre {
+   line-height: normal !important;
+}
diff --git a/docs/api.rst b/docs/api.rst
index eb93379..fa16581 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -9,6 +9,45 @@ Exceptions
   .. autoclass:: Invalid
      :members:
 
+     .. attribute:: parent
+
+        A reference to the parent exception.
+
+     .. attribute:: pos
+
+        An integer representing the position of this exception's
+        schema node relative to all other child nodes of this
+        exception's parent schema node.  For example, if this
+        exception is related to the third child node of its parent's
+        schema, ``pos`` might be the integer ``3``.  ``pos`` may also
+        be ``None``, in which case this exception is the root
+        exception.
+
+     .. attribute:: children
+
+        A list of child exceptions.  Each element in this list (if
+        any) will also be an :exc:`colander.Invalid` exception,
+        recursively, representing the error circumstances for a
+        particular schema deserialization.
+
+     .. attribute:: msg
+
+       A ``str`` or ``unicode`` object, or a *translation string*
+       instance representing a freeform error value set by a
+       particular type during an unsuccessful deserialization.  If
+       this exception is only structural (only exists to be a parent
+       to some inner child exception), this value will be ``None``.
+
+     .. attribute:: node
+
+       The schema node to which this exception relates.
+
+     .. attribute:: value
+
+       An attribute not used internally by Colander, but which can be
+       used by higher-level systems to attach arbitrary values to
+       Colander exception nodes.
+
 Validators
 ~~~~~~~~~~
 
diff --git a/docs/index.rst b/docs/index.rst
index 3510c1d..bded907 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -40,7 +40,8 @@ of objects, including:
 - A Python ``datetime.date`` object.
 
 Colander allows additional data structures to be serialized and
-deserialized by allowing a developer to define new "types".
+deserialized by allowing a developer to define new "types".  Its
+internal error messages are internationalizable.
 
 Defining A Colander Schema
 --------------------------
@@ -340,6 +341,62 @@ This will print something like:
     'friends.1.0':'"t" is not a number',
     'phones.0.location:'"bar" is not one of "home", "work"'}
 
+:exc:`colander.Invalid` Exceptions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The exceptions raised by Colander during deserialization are instances
+of the :exc:`colander.Invalid` exception class.  We saw previously
+that instances of this exception class have a
+:meth:`colander.Invalid.asdict` method which returns a dictionary of
+error messages.  This dictionary is composed by Colander by walking
+the *exception tree*.  The exception tree is composed entirely of
+:exc:`colander.Invalid` exceptions.
+
+While the :meth:`colander.Invalid.asdict` method is useful for simple
+error reporting, a more complex application, such as a form library
+that uses Colander as an underlying schema system, may need to do
+error reporting in a different way.  In particular, such a system may
+need to present the errors next to a field in a form. It may need to
+translate error messages to another language.  To do these things
+effectively, it will almost certainly need to walk and introspect the
+exception graph manually. 
+
+The :exc:`colander.Invalid` exceptions raised by Colander validation
+are very rich.  They contain detailed information about the
+circumstances of an error.  If you write a system based on Colander
+that needs to display and format Colander exceptions specially, you
+will need to get comfy with the Invalid exception API.  
+
+When a validation-related error occurs during deserialization, each
+node in the schema that had an error (and any of its parents) will be
+represented by a corresponding :class:`colander.Invalid` exception.
+To support this behavior, each :exc:`colander.Invalid` exception has a
+``children`` attribute which is a list.  Each element in this list (if
+any) will also be an :exc:`colander.Invalid` exception, recursively,
+representing the error circumstances for a particular schema
+deserialization.
+
+Each exception in the graph has a ``msg`` attribute, which will either
+be the value ``None``, a ``str`` or ``unicode`` object, or a
+*translation string* instance representing a freeform error value set
+by a particular type during an unsuccessful deserialization.
+Exceptions that exist purely for structure will have a ``msg``
+attribute with the value ``None``.  Each exception instance will also
+have an attribute named ``node``, representing the schema node to
+which the exception is related.
+
+.. note:: Translation strings are objects which behave like Unicode
+  objects but have extra metadata associated with them for use in
+  translation systems.  See `http://docs.repoze.org/translationstring/
+  `_ for documentation
+  about translation strings.  All error messages used by Colander
+  internally are translation strings, which means they can be
+  translated to other languages.  In particular, they are suitable for
+  use as gettext *message ids*.
+
+See the :class:`colander.Invalid` API documentation for more
+information.
+
 Serialization
 -------------
 
diff --git a/setup.py b/setup.py
index 92de748..828a0b7 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
 requires = ['iso8601', 'translationstring']
 
 setup(name='colander',
-      version='0.5.2',
+      version='0.6.0',
       description=('A simple schema-based serialization and deserialization '
                    'library'),
       long_description=README + '\n\n' +  CHANGES,