Imported from SVN by Bitbucket
This commit is contained in:
6
Paste-1.7.5.1-py2.6.egg/paste/exceptions/__init__.py
Executable file
6
Paste-1.7.5.1-py2.6.egg/paste/exceptions/__init__.py
Executable file
@@ -0,0 +1,6 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
"""
|
||||
Package for catching exceptions and displaying annotated exception
|
||||
reports
|
||||
"""
|
||||
526
Paste-1.7.5.1-py2.6.egg/paste/exceptions/collector.py
Executable file
526
Paste-1.7.5.1-py2.6.egg/paste/exceptions/collector.py
Executable file
@@ -0,0 +1,526 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# This software is subject to the provisions of the Zope Public License,
|
||||
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
## Originally zExceptions.ExceptionFormatter from Zope;
|
||||
## Modified by Ian Bicking, Imaginary Landscape, 2005
|
||||
"""
|
||||
An exception collector that finds traceback information plus
|
||||
supplements
|
||||
"""
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
import time
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
import linecache
|
||||
from paste.exceptions import serial_number_generator
|
||||
import warnings
|
||||
|
||||
DEBUG_EXCEPTION_FORMATTER = True
|
||||
DEBUG_IDENT_PREFIX = 'E-'
|
||||
FALLBACK_ENCODING = 'UTF-8'
|
||||
|
||||
__all__ = ['collect_exception', 'ExceptionCollector']
|
||||
|
||||
class ExceptionCollector(object):
|
||||
|
||||
"""
|
||||
Produces a data structure that can be used by formatters to
|
||||
display exception reports.
|
||||
|
||||
Magic variables:
|
||||
|
||||
If you define one of these variables in your local scope, you can
|
||||
add information to tracebacks that happen in that context. This
|
||||
allows applications to add all sorts of extra information about
|
||||
the context of the error, including URLs, environmental variables,
|
||||
users, hostnames, etc. These are the variables we look for:
|
||||
|
||||
``__traceback_supplement__``:
|
||||
You can define this locally or globally (unlike all the other
|
||||
variables, which must be defined locally).
|
||||
|
||||
``__traceback_supplement__`` is a tuple of ``(factory, arg1,
|
||||
arg2...)``. When there is an exception, ``factory(arg1, arg2,
|
||||
...)`` is called, and the resulting object is inspected for
|
||||
supplemental information.
|
||||
|
||||
``__traceback_info__``:
|
||||
This information is added to the traceback, usually fairly
|
||||
literally.
|
||||
|
||||
``__traceback_hide__``:
|
||||
If set and true, this indicates that the frame should be
|
||||
hidden from abbreviated tracebacks. This way you can hide
|
||||
some of the complexity of the larger framework and let the
|
||||
user focus on their own errors.
|
||||
|
||||
By setting it to ``'before'``, all frames before this one will
|
||||
be thrown away. By setting it to ``'after'`` then all frames
|
||||
after this will be thrown away until ``'reset'`` is found. In
|
||||
each case the frame where it is set is included, unless you
|
||||
append ``'_and_this'`` to the value (e.g.,
|
||||
``'before_and_this'``).
|
||||
|
||||
Note that formatters will ignore this entirely if the frame
|
||||
that contains the error wouldn't normally be shown according
|
||||
to these rules.
|
||||
|
||||
``__traceback_reporter__``:
|
||||
This should be a reporter object (see the reporter module),
|
||||
or a list/tuple of reporter objects. All reporters found this
|
||||
way will be given the exception, innermost first.
|
||||
|
||||
``__traceback_decorator__``:
|
||||
This object (defined in a local or global scope) will get the
|
||||
result of this function (the CollectedException defined
|
||||
below). It may modify this object in place, or return an
|
||||
entirely new object. This gives the object the ability to
|
||||
manipulate the traceback arbitrarily.
|
||||
|
||||
The actually interpretation of these values is largely up to the
|
||||
reporters and formatters.
|
||||
|
||||
``collect_exception(*sys.exc_info())`` will return an object with
|
||||
several attributes:
|
||||
|
||||
``frames``:
|
||||
A list of frames
|
||||
``exception_formatted``:
|
||||
The formatted exception, generally a full traceback
|
||||
``exception_type``:
|
||||
The type of the exception, like ``ValueError``
|
||||
``exception_value``:
|
||||
The string value of the exception, like ``'x not in list'``
|
||||
``identification_code``:
|
||||
A hash of the exception data meant to identify the general
|
||||
exception, so that it shares this code with other exceptions
|
||||
that derive from the same problem. The code is a hash of
|
||||
all the module names and function names in the traceback,
|
||||
plus exception_type. This should be shown to users so they
|
||||
can refer to the exception later. (@@: should it include a
|
||||
portion that allows identification of the specific instance
|
||||
of the exception as well?)
|
||||
|
||||
The list of frames goes innermost first. Each frame has these
|
||||
attributes; some values may be None if they could not be
|
||||
determined.
|
||||
|
||||
``modname``:
|
||||
the name of the module
|
||||
``filename``:
|
||||
the filename of the module
|
||||
``lineno``:
|
||||
the line of the error
|
||||
``revision``:
|
||||
the contents of __version__ or __revision__
|
||||
``name``:
|
||||
the function name
|
||||
``supplement``:
|
||||
an object created from ``__traceback_supplement__``
|
||||
``supplement_exception``:
|
||||
a simple traceback of any exception ``__traceback_supplement__``
|
||||
created
|
||||
``traceback_info``:
|
||||
the str() of any ``__traceback_info__`` variable found in the local
|
||||
scope (@@: should it str()-ify it or not?)
|
||||
``traceback_hide``:
|
||||
the value of any ``__traceback_hide__`` variable
|
||||
``traceback_log``:
|
||||
the value of any ``__traceback_log__`` variable
|
||||
|
||||
|
||||
``__traceback_supplement__`` is thrown away, but a fixed
|
||||
set of attributes are captured; each of these attributes is
|
||||
optional.
|
||||
|
||||
``object``:
|
||||
the name of the object being visited
|
||||
``source_url``:
|
||||
the original URL requested
|
||||
``line``:
|
||||
the line of source being executed (for interpreters, like ZPT)
|
||||
``column``:
|
||||
the column of source being executed
|
||||
``expression``:
|
||||
the expression being evaluated (also for interpreters)
|
||||
``warnings``:
|
||||
a list of (string) warnings to be displayed
|
||||
``getInfo``:
|
||||
a function/method that takes no arguments, and returns a string
|
||||
describing any extra information
|
||||
``extraData``:
|
||||
a function/method that takes no arguments, and returns a
|
||||
dictionary. The contents of this dictionary will not be
|
||||
displayed in the context of the traceback, but globally for
|
||||
the exception. Results will be grouped by the keys in the
|
||||
dictionaries (which also serve as titles). The keys can also
|
||||
be tuples of (importance, title); in this case the importance
|
||||
should be ``important`` (shows up at top), ``normal`` (shows
|
||||
up somewhere; unspecified), ``supplemental`` (shows up at
|
||||
bottom), or ``extra`` (shows up hidden or not at all).
|
||||
|
||||
These are used to create an object with attributes of the same
|
||||
names (``getInfo`` becomes a string attribute, not a method).
|
||||
``__traceback_supplement__`` implementations should be careful to
|
||||
produce values that are relatively static and unlikely to cause
|
||||
further errors in the reporting system -- any complex
|
||||
introspection should go in ``getInfo()`` and should ultimately
|
||||
return a string.
|
||||
|
||||
Note that all attributes are optional, and under certain
|
||||
circumstances may be None or may not exist at all -- the collector
|
||||
can only do a best effort, but must avoid creating any exceptions
|
||||
itself.
|
||||
|
||||
Formatters may want to use ``__traceback_hide__`` as a hint to
|
||||
hide frames that are part of the 'framework' or underlying system.
|
||||
There are a variety of rules about special values for this
|
||||
variables that formatters should be aware of.
|
||||
|
||||
TODO:
|
||||
|
||||
More attributes in __traceback_supplement__? Maybe an attribute
|
||||
that gives a list of local variables that should also be
|
||||
collected? Also, attributes that would be explicitly meant for
|
||||
the entire request, not just a single frame. Right now some of
|
||||
the fixed set of attributes (e.g., source_url) are meant for this
|
||||
use, but there's no explicit way for the supplement to indicate
|
||||
new values, e.g., logged-in user, HTTP referrer, environment, etc.
|
||||
Also, the attributes that do exist are Zope/Web oriented.
|
||||
|
||||
More information on frames? cgitb, for instance, produces
|
||||
extensive information on local variables. There exists the
|
||||
possibility that getting this information may cause side effects,
|
||||
which can make debugging more difficult; but it also provides
|
||||
fodder for post-mortem debugging. However, the collector is not
|
||||
meant to be configurable, but to capture everything it can and let
|
||||
the formatters be configurable. Maybe this would have to be a
|
||||
configuration value, or maybe it could be indicated by another
|
||||
magical variable (which would probably mean 'show all local
|
||||
variables below this frame')
|
||||
"""
|
||||
|
||||
show_revisions = 0
|
||||
|
||||
def __init__(self, limit=None):
|
||||
self.limit = limit
|
||||
|
||||
def getLimit(self):
|
||||
limit = self.limit
|
||||
if limit is None:
|
||||
limit = getattr(sys, 'tracebacklimit', None)
|
||||
return limit
|
||||
|
||||
def getRevision(self, globals):
|
||||
if not self.show_revisions:
|
||||
return None
|
||||
revision = globals.get('__revision__', None)
|
||||
if revision is None:
|
||||
# Incorrect but commonly used spelling
|
||||
revision = globals.get('__version__', None)
|
||||
|
||||
if revision is not None:
|
||||
try:
|
||||
revision = str(revision).strip()
|
||||
except:
|
||||
revision = '???'
|
||||
return revision
|
||||
|
||||
def collectSupplement(self, supplement, tb):
|
||||
result = {}
|
||||
|
||||
for name in ('object', 'source_url', 'line', 'column',
|
||||
'expression', 'warnings'):
|
||||
result[name] = getattr(supplement, name, None)
|
||||
|
||||
func = getattr(supplement, 'getInfo', None)
|
||||
if func:
|
||||
result['info'] = func()
|
||||
else:
|
||||
result['info'] = None
|
||||
func = getattr(supplement, 'extraData', None)
|
||||
if func:
|
||||
result['extra'] = func()
|
||||
else:
|
||||
result['extra'] = None
|
||||
return SupplementaryData(**result)
|
||||
|
||||
def collectLine(self, tb, extra_data):
|
||||
f = tb.tb_frame
|
||||
lineno = tb.tb_lineno
|
||||
co = f.f_code
|
||||
filename = co.co_filename
|
||||
name = co.co_name
|
||||
globals = f.f_globals
|
||||
locals = f.f_locals
|
||||
if not hasattr(locals, 'has_key'):
|
||||
# Something weird about this frame; it's not a real dict
|
||||
warnings.warn(
|
||||
"Frame %s has an invalid locals(): %r" % (
|
||||
globals.get('__name__', 'unknown'), locals))
|
||||
locals = {}
|
||||
data = {}
|
||||
data['modname'] = globals.get('__name__', None)
|
||||
data['filename'] = filename
|
||||
data['lineno'] = lineno
|
||||
data['revision'] = self.getRevision(globals)
|
||||
data['name'] = name
|
||||
data['tbid'] = id(tb)
|
||||
|
||||
# Output a traceback supplement, if any.
|
||||
if locals.has_key('__traceback_supplement__'):
|
||||
# Use the supplement defined in the function.
|
||||
tbs = locals['__traceback_supplement__']
|
||||
elif globals.has_key('__traceback_supplement__'):
|
||||
# Use the supplement defined in the module.
|
||||
# This is used by Scripts (Python).
|
||||
tbs = globals['__traceback_supplement__']
|
||||
else:
|
||||
tbs = None
|
||||
if tbs is not None:
|
||||
factory = tbs[0]
|
||||
args = tbs[1:]
|
||||
try:
|
||||
supp = factory(*args)
|
||||
data['supplement'] = self.collectSupplement(supp, tb)
|
||||
if data['supplement'].extra:
|
||||
for key, value in data['supplement'].extra.items():
|
||||
extra_data.setdefault(key, []).append(value)
|
||||
except:
|
||||
if DEBUG_EXCEPTION_FORMATTER:
|
||||
out = StringIO()
|
||||
traceback.print_exc(file=out)
|
||||
text = out.getvalue()
|
||||
data['supplement_exception'] = text
|
||||
# else just swallow the exception.
|
||||
|
||||
try:
|
||||
tbi = locals.get('__traceback_info__', None)
|
||||
if tbi is not None:
|
||||
data['traceback_info'] = str(tbi)
|
||||
except:
|
||||
pass
|
||||
|
||||
marker = []
|
||||
for name in ('__traceback_hide__', '__traceback_log__',
|
||||
'__traceback_decorator__'):
|
||||
try:
|
||||
tbh = locals.get(name, globals.get(name, marker))
|
||||
if tbh is not marker:
|
||||
data[name[2:-2]] = tbh
|
||||
except:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
def collectExceptionOnly(self, etype, value):
|
||||
return traceback.format_exception_only(etype, value)
|
||||
|
||||
def collectException(self, etype, value, tb, limit=None):
|
||||
# The next line provides a way to detect recursion.
|
||||
__exception_formatter__ = 1
|
||||
frames = []
|
||||
ident_data = []
|
||||
traceback_decorators = []
|
||||
if limit is None:
|
||||
limit = self.getLimit()
|
||||
n = 0
|
||||
extra_data = {}
|
||||
while tb is not None and (limit is None or n < limit):
|
||||
if tb.tb_frame.f_locals.get('__exception_formatter__'):
|
||||
# Stop recursion. @@: should make a fake ExceptionFrame
|
||||
frames.append('(Recursive formatException() stopped)\n')
|
||||
break
|
||||
data = self.collectLine(tb, extra_data)
|
||||
frame = ExceptionFrame(**data)
|
||||
frames.append(frame)
|
||||
if frame.traceback_decorator is not None:
|
||||
traceback_decorators.append(frame.traceback_decorator)
|
||||
ident_data.append(frame.modname or '?')
|
||||
ident_data.append(frame.name or '?')
|
||||
tb = tb.tb_next
|
||||
n = n + 1
|
||||
ident_data.append(str(etype))
|
||||
ident = serial_number_generator.hash_identifier(
|
||||
' '.join(ident_data), length=5, upper=True,
|
||||
prefix=DEBUG_IDENT_PREFIX)
|
||||
|
||||
result = CollectedException(
|
||||
frames=frames,
|
||||
exception_formatted=self.collectExceptionOnly(etype, value),
|
||||
exception_type=etype,
|
||||
exception_value=self.safeStr(value),
|
||||
identification_code=ident,
|
||||
date=time.localtime(),
|
||||
extra_data=extra_data)
|
||||
if etype is ImportError:
|
||||
extra_data[('important', 'sys.path')] = [sys.path]
|
||||
for decorator in traceback_decorators:
|
||||
try:
|
||||
new_result = decorator(result)
|
||||
if new_result is not None:
|
||||
result = new_result
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
def safeStr(self, obj):
|
||||
try:
|
||||
return str(obj)
|
||||
except UnicodeEncodeError:
|
||||
try:
|
||||
return unicode(obj).encode(FALLBACK_ENCODING, 'replace')
|
||||
except UnicodeEncodeError:
|
||||
# This is when something is really messed up, but this can
|
||||
# happen when the __str__ of an object has to handle unicode
|
||||
return repr(obj)
|
||||
|
||||
limit = 200
|
||||
|
||||
class Bunch(object):
|
||||
|
||||
"""
|
||||
A generic container
|
||||
"""
|
||||
|
||||
def __init__(self, **attrs):
|
||||
for name, value in attrs.items():
|
||||
setattr(self, name, value)
|
||||
|
||||
def __repr__(self):
|
||||
name = '<%s ' % self.__class__.__name__
|
||||
name += ' '.join(['%s=%r' % (name, str(value)[:30])
|
||||
for name, value in self.__dict__.items()
|
||||
if not name.startswith('_')])
|
||||
return name + '>'
|
||||
|
||||
class CollectedException(Bunch):
|
||||
"""
|
||||
This is the result of collection the exception; it contains copies
|
||||
of data of interest.
|
||||
"""
|
||||
# A list of frames (ExceptionFrame instances), innermost last:
|
||||
frames = []
|
||||
# The result of traceback.format_exception_only; this looks
|
||||
# like a normal traceback you'd see in the interactive interpreter
|
||||
exception_formatted = None
|
||||
# The *string* representation of the type of the exception
|
||||
# (@@: should we give the # actual class? -- we can't keep the
|
||||
# actual exception around, but the class should be safe)
|
||||
# Something like 'ValueError'
|
||||
exception_type = None
|
||||
# The string representation of the exception, from ``str(e)``.
|
||||
exception_value = None
|
||||
# An identifier which should more-or-less classify this particular
|
||||
# exception, including where in the code it happened.
|
||||
identification_code = None
|
||||
# The date, as time.localtime() returns:
|
||||
date = None
|
||||
# A dictionary of supplemental data:
|
||||
extra_data = {}
|
||||
|
||||
class SupplementaryData(Bunch):
|
||||
"""
|
||||
The result of __traceback_supplement__. We don't keep the
|
||||
supplement object around, for fear of GC problems and whatnot.
|
||||
(@@: Maybe I'm being too superstitious about copying only specific
|
||||
information over)
|
||||
"""
|
||||
|
||||
# These attributes are copied from the object, or left as None
|
||||
# if the object doesn't have these attributes:
|
||||
object = None
|
||||
source_url = None
|
||||
line = None
|
||||
column = None
|
||||
expression = None
|
||||
warnings = None
|
||||
# This is the *return value* of supplement.getInfo():
|
||||
info = None
|
||||
|
||||
class ExceptionFrame(Bunch):
|
||||
"""
|
||||
This represents one frame of the exception. Each frame is a
|
||||
context in the call stack, typically represented by a line
|
||||
number and module name in the traceback.
|
||||
"""
|
||||
|
||||
# The name of the module; can be None, especially when the code
|
||||
# isn't associated with a module.
|
||||
modname = None
|
||||
# The filename (@@: when no filename, is it None or '?'?)
|
||||
filename = None
|
||||
# Line number
|
||||
lineno = None
|
||||
# The value of __revision__ or __version__ -- but only if
|
||||
# show_revision = True (by defaut it is false). (@@: Why not
|
||||
# collect this?)
|
||||
revision = None
|
||||
# The name of the function with the error (@@: None or '?' when
|
||||
# unknown?)
|
||||
name = None
|
||||
# A SupplementaryData object, if __traceback_supplement__ was found
|
||||
# (and produced no errors)
|
||||
supplement = None
|
||||
# If accessing __traceback_supplement__ causes any error, the
|
||||
# plain-text traceback is stored here
|
||||
supplement_exception = None
|
||||
# The str() of any __traceback_info__ value found
|
||||
traceback_info = None
|
||||
# The value of __traceback_hide__
|
||||
traceback_hide = False
|
||||
# The value of __traceback_decorator__
|
||||
traceback_decorator = None
|
||||
# The id() of the traceback scope, can be used to reference the
|
||||
# scope for use elsewhere
|
||||
tbid = None
|
||||
|
||||
def get_source_line(self, context=0):
|
||||
"""
|
||||
Return the source of the current line of this frame. You
|
||||
probably want to .strip() it as well, as it is likely to have
|
||||
leading whitespace.
|
||||
|
||||
If context is given, then that many lines on either side will
|
||||
also be returned. E.g., context=1 will give 3 lines.
|
||||
"""
|
||||
if not self.filename or not self.lineno:
|
||||
return None
|
||||
lines = []
|
||||
for lineno in range(self.lineno-context, self.lineno+context+1):
|
||||
lines.append(linecache.getline(self.filename, lineno))
|
||||
return ''.join(lines)
|
||||
|
||||
if hasattr(sys, 'tracebacklimit'):
|
||||
limit = min(limit, sys.tracebacklimit)
|
||||
|
||||
col = ExceptionCollector()
|
||||
|
||||
def collect_exception(t, v, tb, limit=None):
|
||||
"""
|
||||
Collection an exception from ``sys.exc_info()``.
|
||||
|
||||
Use like::
|
||||
|
||||
try:
|
||||
blah blah
|
||||
except:
|
||||
exc_data = collect_exception(*sys.exc_info())
|
||||
"""
|
||||
return col.collectException(t, v, tb, limit=limit)
|
||||
460
Paste-1.7.5.1-py2.6.egg/paste/exceptions/errormiddleware.py
Executable file
460
Paste-1.7.5.1-py2.6.egg/paste/exceptions/errormiddleware.py
Executable file
@@ -0,0 +1,460 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
Error handler middleware
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
import cgi
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
from paste.exceptions import formatter, collector, reporter
|
||||
from paste import wsgilib
|
||||
from paste import request
|
||||
|
||||
__all__ = ['ErrorMiddleware', 'handle_exception']
|
||||
|
||||
class _NoDefault(object):
|
||||
def __repr__(self):
|
||||
return '<NoDefault>'
|
||||
NoDefault = _NoDefault()
|
||||
|
||||
class ErrorMiddleware(object):
|
||||
|
||||
"""
|
||||
Error handling middleware
|
||||
|
||||
Usage::
|
||||
|
||||
error_catching_wsgi_app = ErrorMiddleware(wsgi_app)
|
||||
|
||||
Settings:
|
||||
|
||||
``debug``:
|
||||
If true, then tracebacks will be shown in the browser.
|
||||
|
||||
``error_email``:
|
||||
an email address (or list of addresses) to send exception
|
||||
reports to
|
||||
|
||||
``error_log``:
|
||||
a filename to append tracebacks to
|
||||
|
||||
``show_exceptions_in_wsgi_errors``:
|
||||
If true, then errors will be printed to ``wsgi.errors``
|
||||
(frequently a server error log, or stderr).
|
||||
|
||||
``from_address``, ``smtp_server``, ``error_subject_prefix``, ``smtp_username``, ``smtp_password``, ``smtp_use_tls``:
|
||||
variables to control the emailed exception reports
|
||||
|
||||
``error_message``:
|
||||
When debug mode is off, the error message to show to users.
|
||||
|
||||
``xmlhttp_key``:
|
||||
When this key (default ``_``) is in the request GET variables
|
||||
(not POST!), expect that this is an XMLHttpRequest, and the
|
||||
response should be more minimal; it should not be a complete
|
||||
HTML page.
|
||||
|
||||
Environment Configuration:
|
||||
|
||||
``paste.throw_errors``:
|
||||
If this setting in the request environment is true, then this
|
||||
middleware is disabled. This can be useful in a testing situation
|
||||
where you don't want errors to be caught and transformed.
|
||||
|
||||
``paste.expected_exceptions``:
|
||||
When this middleware encounters an exception listed in this
|
||||
environment variable and when the ``start_response`` has not
|
||||
yet occurred, the exception will be re-raised instead of being
|
||||
caught. This should generally be set by middleware that may
|
||||
(but probably shouldn't be) installed above this middleware,
|
||||
and wants to get certain exceptions. Exceptions raised after
|
||||
``start_response`` have been called are always caught since
|
||||
by definition they are no longer expected.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, application, global_conf=None,
|
||||
debug=NoDefault,
|
||||
error_email=None,
|
||||
error_log=None,
|
||||
show_exceptions_in_wsgi_errors=NoDefault,
|
||||
from_address=None,
|
||||
smtp_server=None,
|
||||
smtp_username=None,
|
||||
smtp_password=None,
|
||||
smtp_use_tls=False,
|
||||
error_subject_prefix=None,
|
||||
error_message=None,
|
||||
xmlhttp_key=None):
|
||||
from paste.util import converters
|
||||
self.application = application
|
||||
# @@: global_conf should be handled elsewhere in a separate
|
||||
# function for the entry point
|
||||
if global_conf is None:
|
||||
global_conf = {}
|
||||
if debug is NoDefault:
|
||||
debug = converters.asbool(global_conf.get('debug'))
|
||||
if show_exceptions_in_wsgi_errors is NoDefault:
|
||||
show_exceptions_in_wsgi_errors = converters.asbool(global_conf.get('show_exceptions_in_wsgi_errors'))
|
||||
self.debug_mode = converters.asbool(debug)
|
||||
if error_email is None:
|
||||
error_email = (global_conf.get('error_email')
|
||||
or global_conf.get('admin_email')
|
||||
or global_conf.get('webmaster_email')
|
||||
or global_conf.get('sysadmin_email'))
|
||||
self.error_email = converters.aslist(error_email)
|
||||
self.error_log = error_log
|
||||
self.show_exceptions_in_wsgi_errors = show_exceptions_in_wsgi_errors
|
||||
if from_address is None:
|
||||
from_address = global_conf.get('error_from_address', 'errors@localhost')
|
||||
self.from_address = from_address
|
||||
if smtp_server is None:
|
||||
smtp_server = global_conf.get('smtp_server', 'localhost')
|
||||
self.smtp_server = smtp_server
|
||||
self.smtp_username = smtp_username or global_conf.get('smtp_username')
|
||||
self.smtp_password = smtp_password or global_conf.get('smtp_password')
|
||||
self.smtp_use_tls = smtp_use_tls or converters.asbool(global_conf.get('smtp_use_tls'))
|
||||
self.error_subject_prefix = error_subject_prefix or ''
|
||||
if error_message is None:
|
||||
error_message = global_conf.get('error_message')
|
||||
self.error_message = error_message
|
||||
if xmlhttp_key is None:
|
||||
xmlhttp_key = global_conf.get('xmlhttp_key', '_')
|
||||
self.xmlhttp_key = xmlhttp_key
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""
|
||||
The WSGI application interface.
|
||||
"""
|
||||
# We want to be careful about not sending headers twice,
|
||||
# and the content type that the app has committed to (if there
|
||||
# is an exception in the iterator body of the response)
|
||||
if environ.get('paste.throw_errors'):
|
||||
return self.application(environ, start_response)
|
||||
environ['paste.throw_errors'] = True
|
||||
|
||||
try:
|
||||
__traceback_supplement__ = Supplement, self, environ
|
||||
sr_checker = ResponseStartChecker(start_response)
|
||||
app_iter = self.application(environ, sr_checker)
|
||||
return self.make_catching_iter(app_iter, environ, sr_checker)
|
||||
except:
|
||||
exc_info = sys.exc_info()
|
||||
try:
|
||||
for expect in environ.get('paste.expected_exceptions', []):
|
||||
if isinstance(exc_info[1], expect):
|
||||
raise
|
||||
start_response('500 Internal Server Error',
|
||||
[('content-type', 'text/html')],
|
||||
exc_info)
|
||||
# @@: it would be nice to deal with bad content types here
|
||||
response = self.exception_handler(exc_info, environ)
|
||||
return [response]
|
||||
finally:
|
||||
# clean up locals...
|
||||
exc_info = None
|
||||
|
||||
def make_catching_iter(self, app_iter, environ, sr_checker):
|
||||
if isinstance(app_iter, (list, tuple)):
|
||||
# These don't raise
|
||||
return app_iter
|
||||
return CatchingIter(app_iter, environ, sr_checker, self)
|
||||
|
||||
def exception_handler(self, exc_info, environ):
|
||||
simple_html_error = False
|
||||
if self.xmlhttp_key:
|
||||
get_vars = wsgilib.parse_querystring(environ)
|
||||
if dict(get_vars).get(self.xmlhttp_key):
|
||||
simple_html_error = True
|
||||
return handle_exception(
|
||||
exc_info, environ['wsgi.errors'],
|
||||
html=True,
|
||||
debug_mode=self.debug_mode,
|
||||
error_email=self.error_email,
|
||||
error_log=self.error_log,
|
||||
show_exceptions_in_wsgi_errors=self.show_exceptions_in_wsgi_errors,
|
||||
error_email_from=self.from_address,
|
||||
smtp_server=self.smtp_server,
|
||||
smtp_username=self.smtp_username,
|
||||
smtp_password=self.smtp_password,
|
||||
smtp_use_tls=self.smtp_use_tls,
|
||||
error_subject_prefix=self.error_subject_prefix,
|
||||
error_message=self.error_message,
|
||||
simple_html_error=simple_html_error)
|
||||
|
||||
class ResponseStartChecker(object):
|
||||
def __init__(self, start_response):
|
||||
self.start_response = start_response
|
||||
self.response_started = False
|
||||
|
||||
def __call__(self, *args):
|
||||
self.response_started = True
|
||||
self.start_response(*args)
|
||||
|
||||
class CatchingIter(object):
|
||||
|
||||
"""
|
||||
A wrapper around the application iterator that will catch
|
||||
exceptions raised by the a generator, or by the close method, and
|
||||
display or report as necessary.
|
||||
"""
|
||||
|
||||
def __init__(self, app_iter, environ, start_checker, error_middleware):
|
||||
self.app_iterable = app_iter
|
||||
self.app_iterator = iter(app_iter)
|
||||
self.environ = environ
|
||||
self.start_checker = start_checker
|
||||
self.error_middleware = error_middleware
|
||||
self.closed = False
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
__traceback_supplement__ = (
|
||||
Supplement, self.error_middleware, self.environ)
|
||||
if self.closed:
|
||||
raise StopIteration
|
||||
try:
|
||||
return self.app_iterator.next()
|
||||
except StopIteration:
|
||||
self.closed = True
|
||||
close_response = self._close()
|
||||
if close_response is not None:
|
||||
return close_response
|
||||
else:
|
||||
raise StopIteration
|
||||
except:
|
||||
self.closed = True
|
||||
close_response = self._close()
|
||||
exc_info = sys.exc_info()
|
||||
response = self.error_middleware.exception_handler(
|
||||
exc_info, self.environ)
|
||||
if close_response is not None:
|
||||
response += (
|
||||
'<hr noshade>Error in .close():<br>%s'
|
||||
% close_response)
|
||||
|
||||
if not self.start_checker.response_started:
|
||||
self.start_checker('500 Internal Server Error',
|
||||
[('content-type', 'text/html')],
|
||||
exc_info)
|
||||
|
||||
return response
|
||||
|
||||
def close(self):
|
||||
# This should at least print something to stderr if the
|
||||
# close method fails at this point
|
||||
if not self.closed:
|
||||
self._close()
|
||||
|
||||
def _close(self):
|
||||
"""Close and return any error message"""
|
||||
if not hasattr(self.app_iterable, 'close'):
|
||||
return None
|
||||
try:
|
||||
self.app_iterable.close()
|
||||
return None
|
||||
except:
|
||||
close_response = self.error_middleware.exception_handler(
|
||||
sys.exc_info(), self.environ)
|
||||
return close_response
|
||||
|
||||
|
||||
class Supplement(object):
|
||||
|
||||
"""
|
||||
This is a supplement used to display standard WSGI information in
|
||||
the traceback.
|
||||
"""
|
||||
|
||||
def __init__(self, middleware, environ):
|
||||
self.middleware = middleware
|
||||
self.environ = environ
|
||||
self.source_url = request.construct_url(environ)
|
||||
|
||||
def extraData(self):
|
||||
data = {}
|
||||
cgi_vars = data[('extra', 'CGI Variables')] = {}
|
||||
wsgi_vars = data[('extra', 'WSGI Variables')] = {}
|
||||
hide_vars = ['paste.config', 'wsgi.errors', 'wsgi.input',
|
||||
'wsgi.multithread', 'wsgi.multiprocess',
|
||||
'wsgi.run_once', 'wsgi.version',
|
||||
'wsgi.url_scheme']
|
||||
for name, value in self.environ.items():
|
||||
if name.upper() == name:
|
||||
if value:
|
||||
cgi_vars[name] = value
|
||||
elif name not in hide_vars:
|
||||
wsgi_vars[name] = value
|
||||
if self.environ['wsgi.version'] != (1, 0):
|
||||
wsgi_vars['wsgi.version'] = self.environ['wsgi.version']
|
||||
proc_desc = tuple([int(bool(self.environ[key]))
|
||||
for key in ('wsgi.multiprocess',
|
||||
'wsgi.multithread',
|
||||
'wsgi.run_once')])
|
||||
wsgi_vars['wsgi process'] = self.process_combos[proc_desc]
|
||||
wsgi_vars['application'] = self.middleware.application
|
||||
if 'paste.config' in self.environ:
|
||||
data[('extra', 'Configuration')] = dict(self.environ['paste.config'])
|
||||
return data
|
||||
|
||||
process_combos = {
|
||||
# multiprocess, multithread, run_once
|
||||
(0, 0, 0): 'Non-concurrent server',
|
||||
(0, 1, 0): 'Multithreaded',
|
||||
(1, 0, 0): 'Multiprocess',
|
||||
(1, 1, 0): 'Multi process AND threads (?)',
|
||||
(0, 0, 1): 'Non-concurrent CGI',
|
||||
(0, 1, 1): 'Multithread CGI (?)',
|
||||
(1, 0, 1): 'CGI',
|
||||
(1, 1, 1): 'Multi thread/process CGI (?)',
|
||||
}
|
||||
|
||||
def handle_exception(exc_info, error_stream, html=True,
|
||||
debug_mode=False,
|
||||
error_email=None,
|
||||
error_log=None,
|
||||
show_exceptions_in_wsgi_errors=False,
|
||||
error_email_from='errors@localhost',
|
||||
smtp_server='localhost',
|
||||
smtp_username=None,
|
||||
smtp_password=None,
|
||||
smtp_use_tls=False,
|
||||
error_subject_prefix='',
|
||||
error_message=None,
|
||||
simple_html_error=False,
|
||||
):
|
||||
"""
|
||||
For exception handling outside of a web context
|
||||
|
||||
Use like::
|
||||
|
||||
import sys
|
||||
from paste.exceptions.errormiddleware import handle_exception
|
||||
try:
|
||||
do stuff
|
||||
except:
|
||||
handle_exception(
|
||||
sys.exc_info(), sys.stderr, html=False, ...other config...)
|
||||
|
||||
If you want to report, but not fully catch the exception, call
|
||||
``raise`` after ``handle_exception``, which (when given no argument)
|
||||
will reraise the exception.
|
||||
"""
|
||||
reported = False
|
||||
exc_data = collector.collect_exception(*exc_info)
|
||||
extra_data = ''
|
||||
if error_email:
|
||||
rep = reporter.EmailReporter(
|
||||
to_addresses=error_email,
|
||||
from_address=error_email_from,
|
||||
smtp_server=smtp_server,
|
||||
smtp_username=smtp_username,
|
||||
smtp_password=smtp_password,
|
||||
smtp_use_tls=smtp_use_tls,
|
||||
subject_prefix=error_subject_prefix)
|
||||
rep_err = send_report(rep, exc_data, html=html)
|
||||
if rep_err:
|
||||
extra_data += rep_err
|
||||
else:
|
||||
reported = True
|
||||
if error_log:
|
||||
rep = reporter.LogReporter(
|
||||
filename=error_log)
|
||||
rep_err = send_report(rep, exc_data, html=html)
|
||||
if rep_err:
|
||||
extra_data += rep_err
|
||||
else:
|
||||
reported = True
|
||||
if show_exceptions_in_wsgi_errors:
|
||||
rep = reporter.FileReporter(
|
||||
file=error_stream)
|
||||
rep_err = send_report(rep, exc_data, html=html)
|
||||
if rep_err:
|
||||
extra_data += rep_err
|
||||
else:
|
||||
reported = True
|
||||
else:
|
||||
error_stream.write('Error - %s: %s\n' % (
|
||||
exc_data.exception_type, exc_data.exception_value))
|
||||
if html:
|
||||
if debug_mode and simple_html_error:
|
||||
return_error = formatter.format_html(
|
||||
exc_data, include_hidden_frames=False,
|
||||
include_reusable=False, show_extra_data=False)
|
||||
reported = True
|
||||
elif debug_mode and not simple_html_error:
|
||||
error_html = formatter.format_html(
|
||||
exc_data,
|
||||
include_hidden_frames=True,
|
||||
include_reusable=False)
|
||||
head_html = formatter.error_css + formatter.hide_display_js
|
||||
return_error = error_template(
|
||||
head_html, error_html, extra_data)
|
||||
extra_data = ''
|
||||
reported = True
|
||||
else:
|
||||
msg = error_message or '''
|
||||
An error occurred. See the error logs for more information.
|
||||
(Turn debug on to display exception reports here)
|
||||
'''
|
||||
return_error = error_template('', msg, '')
|
||||
else:
|
||||
return_error = None
|
||||
if not reported and error_stream:
|
||||
err_report = formatter.format_text(exc_data, show_hidden_frames=True)
|
||||
err_report += '\n' + '-'*60 + '\n'
|
||||
error_stream.write(err_report)
|
||||
if extra_data:
|
||||
error_stream.write(extra_data)
|
||||
return return_error
|
||||
|
||||
def send_report(rep, exc_data, html=True):
|
||||
try:
|
||||
rep.report(exc_data)
|
||||
except:
|
||||
output = StringIO()
|
||||
traceback.print_exc(file=output)
|
||||
if html:
|
||||
return """
|
||||
<p>Additionally an error occurred while sending the %s report:
|
||||
|
||||
<pre>%s</pre>
|
||||
</p>""" % (
|
||||
cgi.escape(str(rep)), output.getvalue())
|
||||
else:
|
||||
return (
|
||||
"Additionally an error occurred while sending the "
|
||||
"%s report:\n%s" % (str(rep), output.getvalue()))
|
||||
else:
|
||||
return ''
|
||||
|
||||
def error_template(head_html, exception, extra):
|
||||
return '''
|
||||
<html>
|
||||
<head>
|
||||
<title>Server Error</title>
|
||||
%s
|
||||
</head>
|
||||
<body>
|
||||
<h1>Server Error</h1>
|
||||
%s
|
||||
%s
|
||||
</body>
|
||||
</html>''' % (head_html, exception, extra)
|
||||
|
||||
def make_error_middleware(app, global_conf, **kw):
|
||||
return ErrorMiddleware(app, global_conf=global_conf, **kw)
|
||||
|
||||
doc_lines = ErrorMiddleware.__doc__.splitlines(True)
|
||||
for i in range(len(doc_lines)):
|
||||
if doc_lines[i].strip().startswith('Settings'):
|
||||
make_error_middleware.__doc__ = ''.join(doc_lines[i:])
|
||||
break
|
||||
del i, doc_lines
|
||||
564
Paste-1.7.5.1-py2.6.egg/paste/exceptions/formatter.py
Executable file
564
Paste-1.7.5.1-py2.6.egg/paste/exceptions/formatter.py
Executable file
@@ -0,0 +1,564 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
Formatters for the exception data that comes from ExceptionCollector.
|
||||
"""
|
||||
# @@: TODO:
|
||||
# Use this: http://www.zope.org/Members/tino/VisualTraceback/VisualTracebackNews
|
||||
|
||||
import cgi
|
||||
import re
|
||||
from paste.util import PySourceColor
|
||||
|
||||
def html_quote(s):
|
||||
return cgi.escape(str(s), True)
|
||||
|
||||
class AbstractFormatter(object):
|
||||
|
||||
general_data_order = ['object', 'source_url']
|
||||
|
||||
def __init__(self, show_hidden_frames=False,
|
||||
include_reusable=True,
|
||||
show_extra_data=True,
|
||||
trim_source_paths=()):
|
||||
self.show_hidden_frames = show_hidden_frames
|
||||
self.trim_source_paths = trim_source_paths
|
||||
self.include_reusable = include_reusable
|
||||
self.show_extra_data = show_extra_data
|
||||
|
||||
def format_collected_data(self, exc_data):
|
||||
general_data = {}
|
||||
if self.show_extra_data:
|
||||
for name, value_list in exc_data.extra_data.items():
|
||||
if isinstance(name, tuple):
|
||||
importance, title = name
|
||||
else:
|
||||
importance, title = 'normal', name
|
||||
for value in value_list:
|
||||
general_data[(importance, name)] = self.format_extra_data(
|
||||
importance, title, value)
|
||||
lines = []
|
||||
frames = self.filter_frames(exc_data.frames)
|
||||
for frame in frames:
|
||||
sup = frame.supplement
|
||||
if sup:
|
||||
if sup.object:
|
||||
general_data[('important', 'object')] = self.format_sup_object(
|
||||
sup.object)
|
||||
if sup.source_url:
|
||||
general_data[('important', 'source_url')] = self.format_sup_url(
|
||||
sup.source_url)
|
||||
if sup.line:
|
||||
lines.append(self.format_sup_line_pos(sup.line, sup.column))
|
||||
if sup.expression:
|
||||
lines.append(self.format_sup_expression(sup.expression))
|
||||
if sup.warnings:
|
||||
for warning in sup.warnings:
|
||||
lines.append(self.format_sup_warning(warning))
|
||||
if sup.info:
|
||||
lines.extend(self.format_sup_info(sup.info))
|
||||
if frame.supplement_exception:
|
||||
lines.append('Exception in supplement:')
|
||||
lines.append(self.quote_long(frame.supplement_exception))
|
||||
if frame.traceback_info:
|
||||
lines.append(self.format_traceback_info(frame.traceback_info))
|
||||
filename = frame.filename
|
||||
if filename and self.trim_source_paths:
|
||||
for path, repl in self.trim_source_paths:
|
||||
if filename.startswith(path):
|
||||
filename = repl + filename[len(path):]
|
||||
break
|
||||
lines.append(self.format_source_line(filename or '?', frame))
|
||||
source = frame.get_source_line()
|
||||
long_source = frame.get_source_line(2)
|
||||
if source:
|
||||
lines.append(self.format_long_source(
|
||||
source, long_source))
|
||||
etype = exc_data.exception_type
|
||||
if not isinstance(etype, basestring):
|
||||
etype = etype.__name__
|
||||
exc_info = self.format_exception_info(
|
||||
etype,
|
||||
exc_data.exception_value)
|
||||
data_by_importance = {'important': [], 'normal': [],
|
||||
'supplemental': [], 'extra': []}
|
||||
for (importance, name), value in general_data.items():
|
||||
data_by_importance[importance].append(
|
||||
(name, value))
|
||||
for value in data_by_importance.values():
|
||||
value.sort()
|
||||
return self.format_combine(data_by_importance, lines, exc_info)
|
||||
|
||||
def filter_frames(self, frames):
|
||||
"""
|
||||
Removes any frames that should be hidden, according to the
|
||||
values of traceback_hide, self.show_hidden_frames, and the
|
||||
hidden status of the final frame.
|
||||
"""
|
||||
if self.show_hidden_frames:
|
||||
return frames
|
||||
new_frames = []
|
||||
hidden = False
|
||||
for frame in frames:
|
||||
hide = frame.traceback_hide
|
||||
# @@: It would be nice to signal a warning if an unknown
|
||||
# hide string was used, but I'm not sure where to put
|
||||
# that warning.
|
||||
if hide == 'before':
|
||||
new_frames = []
|
||||
hidden = False
|
||||
elif hide == 'before_and_this':
|
||||
new_frames = []
|
||||
hidden = False
|
||||
continue
|
||||
elif hide == 'reset':
|
||||
hidden = False
|
||||
elif hide == 'reset_and_this':
|
||||
hidden = False
|
||||
continue
|
||||
elif hide == 'after':
|
||||
hidden = True
|
||||
elif hide == 'after_and_this':
|
||||
hidden = True
|
||||
continue
|
||||
elif hide:
|
||||
continue
|
||||
elif hidden:
|
||||
continue
|
||||
new_frames.append(frame)
|
||||
if frames[-1] not in new_frames:
|
||||
# We must include the last frame; that we don't indicates
|
||||
# that the error happened where something was "hidden",
|
||||
# so we just have to show everything
|
||||
return frames
|
||||
return new_frames
|
||||
|
||||
def pretty_string_repr(self, s):
|
||||
"""
|
||||
Formats the string as a triple-quoted string when it contains
|
||||
newlines.
|
||||
"""
|
||||
if '\n' in s:
|
||||
s = repr(s)
|
||||
s = s[0]*3 + s[1:-1] + s[-1]*3
|
||||
s = s.replace('\\n', '\n')
|
||||
return s
|
||||
else:
|
||||
return repr(s)
|
||||
|
||||
def long_item_list(self, lst):
|
||||
"""
|
||||
Returns true if the list contains items that are long, and should
|
||||
be more nicely formatted.
|
||||
"""
|
||||
how_many = 0
|
||||
for item in lst:
|
||||
if len(repr(item)) > 40:
|
||||
how_many += 1
|
||||
if how_many >= 3:
|
||||
return True
|
||||
return False
|
||||
|
||||
class TextFormatter(AbstractFormatter):
|
||||
|
||||
def quote(self, s):
|
||||
return s
|
||||
def quote_long(self, s):
|
||||
return s
|
||||
def emphasize(self, s):
|
||||
return s
|
||||
def format_sup_object(self, obj):
|
||||
return 'In object: %s' % self.emphasize(self.quote(repr(obj)))
|
||||
def format_sup_url(self, url):
|
||||
return 'URL: %s' % self.quote(url)
|
||||
def format_sup_line_pos(self, line, column):
|
||||
if column:
|
||||
return self.emphasize('Line %i, Column %i' % (line, column))
|
||||
else:
|
||||
return self.emphasize('Line %i' % line)
|
||||
def format_sup_expression(self, expr):
|
||||
return self.emphasize('In expression: %s' % self.quote(expr))
|
||||
def format_sup_warning(self, warning):
|
||||
return 'Warning: %s' % self.quote(warning)
|
||||
def format_sup_info(self, info):
|
||||
return [self.quote_long(info)]
|
||||
def format_source_line(self, filename, frame):
|
||||
return 'File %r, line %s in %s' % (
|
||||
filename, frame.lineno or '?', frame.name or '?')
|
||||
def format_long_source(self, source, long_source):
|
||||
return self.format_source(source)
|
||||
def format_source(self, source_line):
|
||||
return ' ' + self.quote(source_line.strip())
|
||||
def format_exception_info(self, etype, evalue):
|
||||
return self.emphasize(
|
||||
'%s: %s' % (self.quote(etype), self.quote(evalue)))
|
||||
def format_traceback_info(self, info):
|
||||
return info
|
||||
|
||||
def format_combine(self, data_by_importance, lines, exc_info):
|
||||
lines[:0] = [value for n, value in data_by_importance['important']]
|
||||
lines.append(exc_info)
|
||||
for name in 'normal', 'supplemental', 'extra':
|
||||
lines.extend([value for n, value in data_by_importance[name]])
|
||||
return self.format_combine_lines(lines)
|
||||
|
||||
def format_combine_lines(self, lines):
|
||||
return '\n'.join(lines)
|
||||
|
||||
def format_extra_data(self, importance, title, value):
|
||||
if isinstance(value, str):
|
||||
s = self.pretty_string_repr(value)
|
||||
if '\n' in s:
|
||||
return '%s:\n%s' % (title, s)
|
||||
else:
|
||||
return '%s: %s' % (title, s)
|
||||
elif isinstance(value, dict):
|
||||
lines = ['\n', title, '-'*len(title)]
|
||||
items = value.items()
|
||||
items.sort()
|
||||
for n, v in items:
|
||||
try:
|
||||
v = repr(v)
|
||||
except Exception, e:
|
||||
v = 'Cannot display: %s' % e
|
||||
v = truncate(v)
|
||||
lines.append(' %s: %s' % (n, v))
|
||||
return '\n'.join(lines)
|
||||
elif (isinstance(value, (list, tuple))
|
||||
and self.long_item_list(value)):
|
||||
parts = [truncate(repr(v)) for v in value]
|
||||
return '%s: [\n %s]' % (
|
||||
title, ',\n '.join(parts))
|
||||
else:
|
||||
return '%s: %s' % (title, truncate(repr(value)))
|
||||
|
||||
class HTMLFormatter(TextFormatter):
|
||||
|
||||
def quote(self, s):
|
||||
return html_quote(s)
|
||||
def quote_long(self, s):
|
||||
return '<pre>%s</pre>' % self.quote(s)
|
||||
def emphasize(self, s):
|
||||
return '<b>%s</b>' % s
|
||||
def format_sup_url(self, url):
|
||||
return 'URL: <a href="%s">%s</a>' % (url, url)
|
||||
def format_combine_lines(self, lines):
|
||||
return '<br>\n'.join(lines)
|
||||
def format_source_line(self, filename, frame):
|
||||
name = self.quote(frame.name or '?')
|
||||
return 'Module <span class="module" title="%s">%s</span>:<b>%s</b> in <code>%s</code>' % (
|
||||
filename, frame.modname or '?', frame.lineno or '?',
|
||||
name)
|
||||
return 'File %r, line %s in <tt>%s</tt>' % (
|
||||
filename, frame.lineno, name)
|
||||
def format_long_source(self, source, long_source):
|
||||
q_long_source = str2html(long_source, False, 4, True)
|
||||
q_source = str2html(source, True, 0, False)
|
||||
return ('<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, \'long\')" href="#"><< </a>%s</code>'
|
||||
'<code class="source" source-type="short"><a onclick="return switch_source(this, \'short\')" class="switch_source" href="#">>> </a>%s</code>'
|
||||
% (q_long_source,
|
||||
q_source))
|
||||
def format_source(self, source_line):
|
||||
return ' <code class="source">%s</code>' % self.quote(source_line.strip())
|
||||
def format_traceback_info(self, info):
|
||||
return '<pre>%s</pre>' % self.quote(info)
|
||||
|
||||
def format_extra_data(self, importance, title, value):
|
||||
if isinstance(value, str):
|
||||
s = self.pretty_string_repr(value)
|
||||
if '\n' in s:
|
||||
return '%s:<br><pre>%s</pre>' % (title, self.quote(s))
|
||||
else:
|
||||
return '%s: <tt>%s</tt>' % (title, self.quote(s))
|
||||
elif isinstance(value, dict):
|
||||
return self.zebra_table(title, value)
|
||||
elif (isinstance(value, (list, tuple))
|
||||
and self.long_item_list(value)):
|
||||
return '%s: <tt>[<br>\n %s]</tt>' % (
|
||||
title, ',<br> '.join(map(self.quote, map(repr, value))))
|
||||
else:
|
||||
return '%s: <tt>%s</tt>' % (title, self.quote(repr(value)))
|
||||
|
||||
def format_combine(self, data_by_importance, lines, exc_info):
|
||||
lines[:0] = [value for n, value in data_by_importance['important']]
|
||||
lines.append(exc_info)
|
||||
for name in 'normal', 'supplemental':
|
||||
lines.extend([value for n, value in data_by_importance[name]])
|
||||
if data_by_importance['extra']:
|
||||
lines.append(
|
||||
'<script type="text/javascript">\nshow_button(\'extra_data\', \'extra data\');\n</script>\n' +
|
||||
'<div id="extra_data" class="hidden-data">\n')
|
||||
lines.extend([value for n, value in data_by_importance['extra']])
|
||||
lines.append('</div>')
|
||||
text = self.format_combine_lines(lines)
|
||||
if self.include_reusable:
|
||||
return error_css + hide_display_js + text
|
||||
else:
|
||||
# Usually because another error is already on this page,
|
||||
# and so the js & CSS are unneeded
|
||||
return text
|
||||
|
||||
def zebra_table(self, title, rows, table_class="variables"):
|
||||
if isinstance(rows, dict):
|
||||
rows = rows.items()
|
||||
rows.sort()
|
||||
table = ['<table class="%s">' % table_class,
|
||||
'<tr class="header"><th colspan="2">%s</th></tr>'
|
||||
% self.quote(title)]
|
||||
odd = False
|
||||
for name, value in rows:
|
||||
try:
|
||||
value = repr(value)
|
||||
except Exception, e:
|
||||
value = 'Cannot print: %s' % e
|
||||
odd = not odd
|
||||
table.append(
|
||||
'<tr class="%s"><td>%s</td>'
|
||||
% (odd and 'odd' or 'even', self.quote(name)))
|
||||
table.append(
|
||||
'<td><tt>%s</tt></td></tr>'
|
||||
% make_wrappable(self.quote(truncate(value))))
|
||||
table.append('</table>')
|
||||
return '\n'.join(table)
|
||||
|
||||
hide_display_js = r'''
|
||||
<script type="text/javascript">
|
||||
function hide_display(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el.className == "hidden-data") {
|
||||
el.className = "";
|
||||
return true;
|
||||
} else {
|
||||
el.className = "hidden-data";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
document.write('<style type="text/css">\n');
|
||||
document.write('.hidden-data {display: none}\n');
|
||||
document.write('</style>\n');
|
||||
function show_button(toggle_id, name) {
|
||||
document.write('<a href="#' + toggle_id
|
||||
+ '" onclick="javascript:hide_display(\'' + toggle_id
|
||||
+ '\')" class="button">' + name + '</a><br>');
|
||||
}
|
||||
|
||||
function switch_source(el, hide_type) {
|
||||
while (el) {
|
||||
if (el.getAttribute &&
|
||||
el.getAttribute('source-type') == hide_type) {
|
||||
break;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
if (! el) {
|
||||
return false;
|
||||
}
|
||||
el.style.display = 'none';
|
||||
if (hide_type == 'long') {
|
||||
while (el) {
|
||||
if (el.getAttribute &&
|
||||
el.getAttribute('source-type') == 'short') {
|
||||
break;
|
||||
}
|
||||
el = el.nextSibling;
|
||||
}
|
||||
} else {
|
||||
while (el) {
|
||||
if (el.getAttribute &&
|
||||
el.getAttribute('source-type') == 'long') {
|
||||
break;
|
||||
}
|
||||
el = el.previousSibling;
|
||||
}
|
||||
}
|
||||
if (el) {
|
||||
el.style.display = '';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
</script>'''
|
||||
|
||||
|
||||
error_css = """
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
tr.header {
|
||||
background-color: #006;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
tr.even {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
table.variables td {
|
||||
vertical-align: top;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background-color: #ccc;
|
||||
border: 2px outset #aaa;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.button:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
code.source {
|
||||
color: #006;
|
||||
}
|
||||
|
||||
a.switch_source {
|
||||
color: #090;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.switch_source:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.source-highlight {
|
||||
background-color: #ff9;
|
||||
}
|
||||
|
||||
</style>
|
||||
"""
|
||||
|
||||
def format_html(exc_data, include_hidden_frames=False, **ops):
|
||||
if not include_hidden_frames:
|
||||
return HTMLFormatter(**ops).format_collected_data(exc_data)
|
||||
short_er = format_html(exc_data, show_hidden_frames=False, **ops)
|
||||
# @@: This should have a way of seeing if the previous traceback
|
||||
# was actually trimmed at all
|
||||
ops['include_reusable'] = False
|
||||
ops['show_extra_data'] = False
|
||||
long_er = format_html(exc_data, show_hidden_frames=True, **ops)
|
||||
text_er = format_text(exc_data, show_hidden_frames=True, **ops)
|
||||
return """
|
||||
%s
|
||||
<br>
|
||||
<script type="text/javascript">
|
||||
show_button('full_traceback', 'full traceback')
|
||||
</script>
|
||||
<div id="full_traceback" class="hidden-data">
|
||||
%s
|
||||
</div>
|
||||
<br>
|
||||
<script type="text/javascript">
|
||||
show_button('text_version', 'text version')
|
||||
</script>
|
||||
<div id="text_version" class="hidden-data">
|
||||
<textarea style="width: 100%%" rows=10 cols=60>%s</textarea>
|
||||
</div>
|
||||
""" % (short_er, long_er, cgi.escape(text_er))
|
||||
|
||||
def format_text(exc_data, **ops):
|
||||
return TextFormatter(**ops).format_collected_data(exc_data)
|
||||
|
||||
whitespace_re = re.compile(r' +')
|
||||
pre_re = re.compile(r'</?pre.*?>')
|
||||
error_re = re.compile(r'<h3>ERROR: .*?</h3>')
|
||||
|
||||
def str2html(src, strip=False, indent_subsequent=0,
|
||||
highlight_inner=False):
|
||||
"""
|
||||
Convert a string to HTML. Try to be really safe about it,
|
||||
returning a quoted version of the string if nothing else works.
|
||||
"""
|
||||
try:
|
||||
return _str2html(src, strip=strip,
|
||||
indent_subsequent=indent_subsequent,
|
||||
highlight_inner=highlight_inner)
|
||||
except:
|
||||
return html_quote(src)
|
||||
|
||||
def _str2html(src, strip=False, indent_subsequent=0,
|
||||
highlight_inner=False):
|
||||
if strip:
|
||||
src = src.strip()
|
||||
orig_src = src
|
||||
try:
|
||||
src = PySourceColor.str2html(src, form='snip')
|
||||
src = error_re.sub('', src)
|
||||
src = pre_re.sub('', src)
|
||||
src = re.sub(r'^[\n\r]{0,1}', '', src)
|
||||
src = re.sub(r'[\n\r]{0,1}$', '', src)
|
||||
except:
|
||||
src = html_quote(orig_src)
|
||||
lines = src.splitlines()
|
||||
if len(lines) == 1:
|
||||
return lines[0]
|
||||
indent = ' '*indent_subsequent
|
||||
for i in range(1, len(lines)):
|
||||
lines[i] = indent+lines[i]
|
||||
if highlight_inner and i == len(lines)/2:
|
||||
lines[i] = '<span class="source-highlight">%s</span>' % lines[i]
|
||||
src = '<br>\n'.join(lines)
|
||||
src = whitespace_re.sub(
|
||||
lambda m: ' '*(len(m.group(0))-1) + ' ', src)
|
||||
return src
|
||||
|
||||
def truncate(string, limit=1000):
|
||||
"""
|
||||
Truncate the string to the limit number of
|
||||
characters
|
||||
"""
|
||||
if len(string) > limit:
|
||||
return string[:limit-20]+'...'+string[-17:]
|
||||
else:
|
||||
return string
|
||||
|
||||
def make_wrappable(html, wrap_limit=60,
|
||||
split_on=';?&@!$#-/\\"\''):
|
||||
# Currently using <wbr>, maybe should use ​
|
||||
# http://www.cs.tut.fi/~jkorpela/html/nobr.html
|
||||
if len(html) <= wrap_limit:
|
||||
return html
|
||||
words = html.split()
|
||||
new_words = []
|
||||
for word in words:
|
||||
wrapped_word = ''
|
||||
while len(word) > wrap_limit:
|
||||
for char in split_on:
|
||||
if char in word:
|
||||
first, rest = word.split(char, 1)
|
||||
wrapped_word += first+char+'<wbr>'
|
||||
word = rest
|
||||
break
|
||||
else:
|
||||
for i in range(0, len(word), wrap_limit):
|
||||
wrapped_word += word[i:i+wrap_limit]+'<wbr>'
|
||||
word = ''
|
||||
wrapped_word += word
|
||||
new_words.append(wrapped_word)
|
||||
return ' '.join(new_words)
|
||||
|
||||
def make_pre_wrappable(html, wrap_limit=60,
|
||||
split_on=';?&@!$#-/\\"\''):
|
||||
"""
|
||||
Like ``make_wrappable()`` but intended for text that will
|
||||
go in a ``<pre>`` block, so wrap on a line-by-line basis.
|
||||
"""
|
||||
lines = html.splitlines()
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
if len(line) > wrap_limit:
|
||||
for char in split_on:
|
||||
if char in line:
|
||||
parts = line.split(char)
|
||||
line = '<wbr>'.join(parts)
|
||||
break
|
||||
new_lines.append(line)
|
||||
return '\n'.join(lines)
|
||||
141
Paste-1.7.5.1-py2.6.egg/paste/exceptions/reporter.py
Executable file
141
Paste-1.7.5.1-py2.6.egg/paste/exceptions/reporter.py
Executable file
@@ -0,0 +1,141 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from email.MIMEText import MIMEText
|
||||
from email.MIMEMultipart import MIMEMultipart
|
||||
import smtplib
|
||||
import time
|
||||
try:
|
||||
from socket import sslerror
|
||||
except ImportError:
|
||||
sslerror = None
|
||||
from paste.exceptions import formatter
|
||||
|
||||
class Reporter(object):
|
||||
|
||||
def __init__(self, **conf):
|
||||
for name, value in conf.items():
|
||||
if not hasattr(self, name):
|
||||
raise TypeError(
|
||||
"The keyword argument %s was not expected"
|
||||
% name)
|
||||
setattr(self, name, value)
|
||||
self.check_params()
|
||||
|
||||
def check_params(self):
|
||||
pass
|
||||
|
||||
def format_date(self, exc_data):
|
||||
return time.strftime('%c', exc_data.date)
|
||||
|
||||
def format_html(self, exc_data, **kw):
|
||||
return formatter.format_html(exc_data, **kw)
|
||||
|
||||
def format_text(self, exc_data, **kw):
|
||||
return formatter.format_text(exc_data, **kw)
|
||||
|
||||
class EmailReporter(Reporter):
|
||||
|
||||
to_addresses = None
|
||||
from_address = None
|
||||
smtp_server = 'localhost'
|
||||
smtp_username = None
|
||||
smtp_password = None
|
||||
smtp_use_tls = False
|
||||
subject_prefix = ''
|
||||
|
||||
def report(self, exc_data):
|
||||
msg = self.assemble_email(exc_data)
|
||||
server = smtplib.SMTP(self.smtp_server)
|
||||
if self.smtp_use_tls:
|
||||
server.ehlo()
|
||||
server.starttls()
|
||||
server.ehlo()
|
||||
if self.smtp_username and self.smtp_password:
|
||||
server.login(self.smtp_username, self.smtp_password)
|
||||
server.sendmail(self.from_address,
|
||||
self.to_addresses, msg.as_string())
|
||||
try:
|
||||
server.quit()
|
||||
except sslerror:
|
||||
# sslerror is raised in tls connections on closing sometimes
|
||||
pass
|
||||
|
||||
def check_params(self):
|
||||
if not self.to_addresses:
|
||||
raise ValueError("You must set to_addresses")
|
||||
if not self.from_address:
|
||||
raise ValueError("You must set from_address")
|
||||
if isinstance(self.to_addresses, (str, unicode)):
|
||||
self.to_addresses = [self.to_addresses]
|
||||
|
||||
def assemble_email(self, exc_data):
|
||||
short_html_version = self.format_html(
|
||||
exc_data, show_hidden_frames=False)
|
||||
long_html_version = self.format_html(
|
||||
exc_data, show_hidden_frames=True)
|
||||
text_version = self.format_text(
|
||||
exc_data, show_hidden_frames=False)
|
||||
msg = MIMEMultipart()
|
||||
msg.set_type('multipart/alternative')
|
||||
msg.preamble = msg.epilogue = ''
|
||||
text_msg = MIMEText(text_version)
|
||||
text_msg.set_type('text/plain')
|
||||
text_msg.set_param('charset', 'ASCII')
|
||||
msg.attach(text_msg)
|
||||
html_msg = MIMEText(short_html_version)
|
||||
html_msg.set_type('text/html')
|
||||
# @@: Correct character set?
|
||||
html_msg.set_param('charset', 'UTF-8')
|
||||
html_long = MIMEText(long_html_version)
|
||||
html_long.set_type('text/html')
|
||||
html_long.set_param('charset', 'UTF-8')
|
||||
msg.attach(html_msg)
|
||||
msg.attach(html_long)
|
||||
subject = '%s: %s' % (exc_data.exception_type,
|
||||
formatter.truncate(str(exc_data.exception_value)))
|
||||
msg['Subject'] = self.subject_prefix + subject
|
||||
msg['From'] = self.from_address
|
||||
msg['To'] = ', '.join(self.to_addresses)
|
||||
return msg
|
||||
|
||||
class LogReporter(Reporter):
|
||||
|
||||
filename = None
|
||||
show_hidden_frames = True
|
||||
|
||||
def check_params(self):
|
||||
assert self.filename is not None, (
|
||||
"You must give a filename")
|
||||
|
||||
def report(self, exc_data):
|
||||
text = self.format_text(
|
||||
exc_data, show_hidden_frames=self.show_hidden_frames)
|
||||
f = open(self.filename, 'a')
|
||||
try:
|
||||
f.write(text + '\n' + '-'*60 + '\n')
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
class FileReporter(Reporter):
|
||||
|
||||
file = None
|
||||
show_hidden_frames = True
|
||||
|
||||
def check_params(self):
|
||||
assert self.file is not None, (
|
||||
"You must give a file object")
|
||||
|
||||
def report(self, exc_data):
|
||||
text = self.format_text(
|
||||
exc_data, show_hidden_frames=self.show_hidden_frames)
|
||||
self.file.write(text + '\n' + '-'*60 + '\n')
|
||||
|
||||
class WSGIAppReporter(Reporter):
|
||||
|
||||
def __init__(self, exc_data):
|
||||
self.exc_data = exc_data
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
start_response('500 Server Error', [('Content-type', 'text/html')])
|
||||
return [formatter.format_html(self.exc_data)]
|
||||
123
Paste-1.7.5.1-py2.6.egg/paste/exceptions/serial_number_generator.py
Executable file
123
Paste-1.7.5.1-py2.6.egg/paste/exceptions/serial_number_generator.py
Executable file
@@ -0,0 +1,123 @@
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
Creates a human-readable identifier, using numbers and digits,
|
||||
avoiding ambiguous numbers and letters. hash_identifier can be used
|
||||
to create compact representations that are unique for a certain string
|
||||
(or concatenation of strings)
|
||||
"""
|
||||
|
||||
try:
|
||||
from hashlib import md5
|
||||
except ImportError:
|
||||
from md5 import md5
|
||||
|
||||
good_characters = "23456789abcdefghjkmnpqrtuvwxyz"
|
||||
|
||||
base = len(good_characters)
|
||||
|
||||
def make_identifier(number):
|
||||
"""
|
||||
Encodes a number as an identifier.
|
||||
"""
|
||||
if not isinstance(number, (int, long)):
|
||||
raise ValueError(
|
||||
"You can only make identifiers out of integers (not %r)"
|
||||
% number)
|
||||
if number < 0:
|
||||
raise ValueError(
|
||||
"You cannot make identifiers out of negative numbers: %r"
|
||||
% number)
|
||||
result = []
|
||||
while number:
|
||||
next = number % base
|
||||
result.append(good_characters[next])
|
||||
# Note, this depends on integer rounding of results:
|
||||
number = number / base
|
||||
return ''.join(result)
|
||||
|
||||
def hash_identifier(s, length, pad=True, hasher=md5, prefix='',
|
||||
group=None, upper=False):
|
||||
"""
|
||||
Hashes the string (with the given hashing module), then turns that
|
||||
hash into an identifier of the given length (using modulo to
|
||||
reduce the length of the identifier). If ``pad`` is False, then
|
||||
the minimum-length identifier will be used; otherwise the
|
||||
identifier will be padded with 0's as necessary.
|
||||
|
||||
``prefix`` will be added last, and does not count towards the
|
||||
target length. ``group`` will group the characters with ``-`` in
|
||||
the given lengths, and also does not count towards the target
|
||||
length. E.g., ``group=4`` will cause a identifier like
|
||||
``a5f3-hgk3-asdf``. Grouping occurs before the prefix.
|
||||
"""
|
||||
if not callable(hasher):
|
||||
# Accept sha/md5 modules as well as callables
|
||||
hasher = hasher.new
|
||||
if length > 26 and hasher is md5:
|
||||
raise ValueError, (
|
||||
"md5 cannot create hashes longer than 26 characters in "
|
||||
"length (you gave %s)" % length)
|
||||
if isinstance(s, unicode):
|
||||
s = s.encode('utf-8')
|
||||
h = hasher(str(s))
|
||||
bin_hash = h.digest()
|
||||
modulo = base ** length
|
||||
number = 0
|
||||
for c in list(bin_hash):
|
||||
number = (number * 256 + ord(c)) % modulo
|
||||
ident = make_identifier(number)
|
||||
if pad:
|
||||
ident = good_characters[0]*(length-len(ident)) + ident
|
||||
if group:
|
||||
parts = []
|
||||
while ident:
|
||||
parts.insert(0, ident[-group:])
|
||||
ident = ident[:-group]
|
||||
ident = '-'.join(parts)
|
||||
if upper:
|
||||
ident = ident.upper()
|
||||
return prefix + ident
|
||||
|
||||
# doctest tests:
|
||||
__test__ = {
|
||||
'make_identifier': """
|
||||
>>> make_identifier(0)
|
||||
''
|
||||
>>> make_identifier(1000)
|
||||
'c53'
|
||||
>>> make_identifier(-100)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: You cannot make identifiers out of negative numbers: -100
|
||||
>>> make_identifier('test')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: You can only make identifiers out of integers (not 'test')
|
||||
>>> make_identifier(1000000000000)
|
||||
'c53x9rqh3'
|
||||
""",
|
||||
'hash_identifier': """
|
||||
>>> hash_identifier(0, 5)
|
||||
'cy2dr'
|
||||
>>> hash_identifier(0, 10)
|
||||
'cy2dr6rg46'
|
||||
>>> hash_identifier('this is a test of a long string', 5)
|
||||
'awatu'
|
||||
>>> hash_identifier(0, 26)
|
||||
'cy2dr6rg46cx8t4w2f3nfexzk4'
|
||||
>>> hash_identifier(0, 30)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: md5 cannot create hashes longer than 26 characters in length (you gave 30)
|
||||
>>> hash_identifier(0, 10, group=4)
|
||||
'cy-2dr6-rg46'
|
||||
>>> hash_identifier(0, 10, group=4, upper=True, prefix='M-')
|
||||
'M-CY-2DR6-RG46'
|
||||
"""}
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
Reference in New Issue
Block a user