summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-09-11 10:15:13 -0500
committerJason Madden <jamadden@gmail.com>2017-09-11 10:20:36 -0500
commit408c2912c4bce039944f62760ab6bba9e091e871 (patch)
tree6bf3fdb808000ee38bdc1597de28677edfcfc098
parentd97f1b37d541554ad85e15dd824a84d3e3883f4c (diff)
downloadzope-exceptions-408c2912c4bce039944f62760ab6bba9e091e871.tar.gz
Update narrative docs; be sure rendering works as expected via testsissue8
Also provide more motivation and differentiation between the various forms of annotating. Fixes #8.
-rw-r--r--.travis.yml2
-rw-r--r--docs/narr.rst132
2 files changed, 95 insertions, 39 deletions
diff --git a/.travis.yml b/.travis.yml
index 19a4c83..61d4409 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ python:
- 3.4
- 3.5
- 3.6
- - pypy-5.4.1
+ - pypy
install:
- pip install -U pip setuptools
- pip install -U coverage coveralls
diff --git a/docs/narr.rst b/docs/narr.rst
index 6c79cd3..a61ec72 100644
--- a/docs/narr.rst
+++ b/docs/narr.rst
@@ -1,22 +1,66 @@
-Using :mod:`zope.exceptions`
-============================
+==============================
+ Using :mod:`zope.exceptions`
+==============================
This module extends the standard library's :mod:`traceback` module, allowing
application code to add additional information to the formatted tracebacks
using specially-named variables in the scope of a given frame.
+We will use examples of a rendering function that's meant to produce
+some output given a template and a set of options. But this rendering
+function is quite broken and produces a useless exception, so when we
+call it we'd like to be able to provide some more contextual information.
+
+.. doctest::
+
+ >>> import sys
+ >>> def render(source, options):
+ ... raise Exception("Failed to render")
Annotating Application Code
----------------------------
+===========================
-:py:data:`__traceback_supplement__`
-+++++++++++++++++++++++++++++++++++
+:py:data:`__traceback_info__`
+-----------------------------
+
+This variable can only be defined at local scope. It will be converted to a
+string when added to the formatted traceback.
+
+.. doctest::
+
+ >>> def render_w_info(template_file, options):
+ ... with open(template_file) as f:
+ ... source = f.read()
+ ... __traceback_info__ = '%s\n\n%s' % (template_file, source)
+ ... render(source, options)
+
+
+This is convenient for quickly adding context information in an
+unstructured way, especially if you already have a string, or an
+object with a custom ``__str__`` or ``__repr__`` that provides the
+information you need (tuples of multiple such items also work well).
+However, if you need to format a string to produce readable
+information, as in the example above, this may have an undesirable
+runtime cost because it is calculated even when no traceback is
+formatted. For such cases, ``__traceback_supplement__`` may be helpful.
-This variable can be defined either at either local or global scope.
-The formatting functions treat objects with this name as though they support
-the :py:class:`~zope.exceptions.interfaces.ITracebackSupplement` interface.
-The various attributes (all optional) of that interface will be used to
-add structured information to the formatted traceback.
+:py:data:`__traceback_supplement__`
+-----------------------------------
+
+This variable can be defined either at either local or global (module)
+scope. Unlike ``__traceback__info__`` this is structured data. It must
+consist of a sequence containing a function and the arguments to pass
+to that function. At runtime, only if a traceback needs to be
+formatted will the function be called, with the arguments, to produce
+a *supplement object*. Because the construction of the object is
+delayed until needed, this can be a less expensive way to produce
+lots of useful information with minimal runtime overhead.
+
+The formatting functions treat the resulting supplement object as if
+it supports the
+:py:class:`~zope.exceptions.interfaces.ITracebackSupplement`
+interface. The various attributes (all optional) of that interface
+will be used to add structured information to the formatted traceback.
For example, assuming your code renders a template:
@@ -24,58 +68,61 @@ For example, assuming your code renders a template:
>>> import os
>>> class Supplement(object):
- ... def __init__(self, template_file, source):
+ ... def __init__(self, template_file, options):
... self.source_url = 'file://%s' % os.path.abspath(template_file)
- ... self.source = source
+ ... self.options = options
+ ... self.expression = 'an expression'
... def getInfo(self):
- ... return self.source
+ ... return "Options: " + str(self.options)
>>> def render_w_supplement(template_file, options):
- ... source = open(template_file).read()
- ... __traceback_supplement__ = Supplement(template_file, source)
+ ... with open(template_file) as f:
+ ... source = f.read()
+ ... __traceback_supplement__ = Supplement, template_file, options
+ ... render(source, options)
-Here, the filename and source of the template will be rendered as part of
+Here, the filename and options of the template will be rendered as part of
the traceback.
-
-:py:data:`__traceback_info__`
-+++++++++++++++++++++++++++++
-
-This variable can only be defined at local scope. It will be converted to a
-string when added to the formatted traceback.
-
-.. doctest::
-
- >>> def render_w_info(template_file, options):
- ... source = open(template_file).read()
- ... __traceback_info__ = '%s\n\n%s' % (template_file, source)
+.. note:: If there is an exception calling the constructor function,
+ no supplement will be formatted, and (by default) the
+ exception will be printed on ``sys.stderr``.
API Functions
--------------
+=============
Three API functions support these features when formatting Python
exceptions and their associated tracebacks:
:py:func:`~zope.exceptions.exceptionformatter.format_exception`
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+---------------------------------------------------------------
-Use this API to format an exception and traceback as a string, using
+Use this API to format an exception and traceback as a list of strings, using
the special annotations. E.g.:
.. doctest::
- >>> import sys
+
>>> from zope.exceptions import format_exception
>>> try:
- ... raise ValueError('demo')
+ ... render_w_info('docs/narr.rst', {})
... except:
... t, v, tb = sys.exc_info()
... report = format_exception(t, v, tb)
... del tb # avoid a leak
... # Now do something with report, e.g., send e-mail.
+ >>> print('\n'.join(report))
+ Traceback (most recent call last):
+ <BLANKLINE>
+ Module <doctest default[1]>, line 2, in <module>
+ render_w_info('docs/narr.rst', {})
+ <BLANKLINE>
+ Module <doctest default[0]>, line 5, in render_w_info
+ - __traceback_info__: docs/narr.rst
+ ...
:py:func:`~zope.exceptions.exceptionformatter.print_exception`
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+--------------------------------------------------------------
Use this API to write the formated exception and traceback to a file-like
object, using the special annotations. E.g.:
@@ -84,15 +131,24 @@ object, using the special annotations. E.g.:
>>> from zope.exceptions import print_exception
>>> try:
- ... raise ValueError('demo')
+ ... render_w_supplement('docs/narr.rst', {})
... except:
... t, v, tb = sys.exc_info()
- ... print_exception(t, v, tb, file=sys.stderr)
+ ... print_exception(t, v, tb, file=sys.stdout)
... del tb # avoid a leak
-
+ Traceback (most recent call last):
+ File "<doctest default[1]>", line 2, in <module>
+ render_w_supplement('docs/narr.rst', {})
+ File "<doctest default[2]>", line 5, in render_w_supplement
+ - file:///...
+ - Expression: an expression
+ Options: {}
+ File "<doctest default[1]>", line 2, in render
+ render_w_supplement('docs/narr.rst', {})
+ Exception: Failed to render
:py:func:`~zope.exceptions.exceptionformatter.extract_stack`
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+------------------------------------------------------------
Use this API to format just the traceback as a list of string,s using the
special annotations. E.g.: