Back to index

moin  1.9.0~rc2
repr.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     werkzeug.debug.repr
00004     ~~~~~~~~~~~~~~~~~~~
00005 
00006     This module implements object representations for debugging purposes.
00007     Unlike the default repr these reprs expose a lot more information and
00008     produce HTML instead of ASCII.
00009 
00010     Together with the CSS and JavaScript files of the debugger this gives
00011     a colorful and more compact output.
00012 
00013     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00014     :license: BSD.
00015 """
00016 import sys
00017 import re
00018 from traceback import format_exception_only
00019 try:
00020     from collections import deque
00021 except ImportError:
00022     deque = None
00023 from werkzeug.utils import escape
00024 from werkzeug.debug.utils import render_template
00025 
00026 
00027 missing = object()
00028 _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
00029 RegexType = type(_paragraph_re)
00030 
00031 
00032 def debug_repr(obj):
00033     """Creates a debug repr of an object as HTML unicode string."""
00034     return DebugReprGenerator().repr(obj)
00035 
00036 
00037 def dump(obj=missing):
00038     """Print the object details to stdout._write (for the interactive
00039     console of the web debugger.
00040     """
00041     gen = DebugReprGenerator()
00042     if obj is missing:
00043         rv = gen.dump_locals(sys._getframe(1).f_locals)
00044     else:
00045         rv = gen.dump_object(obj)
00046     sys.stdout._write(rv)
00047 
00048 
00049 class _Helper(object):
00050     """Displays an HTML version of the normal help, for the interactive
00051     debugger only because it requires a patched sys.stdout.
00052     """
00053 
00054     def __call__(self, topic=None):
00055         sys.stdout._write(self.get_help(topic))
00056 
00057     def get_help(self, topic):
00058         title = text = None
00059         if topic is not None:
00060             import pydoc
00061             pydoc.help(topic)
00062             rv = sys.stdout.reset().decode('utf-8', 'ignore')
00063             paragraphs = _paragraph_re.split(rv)
00064             if len(paragraphs) > 1:
00065                 title = paragraphs[0]
00066                 text = '\n\n'.join(paragraphs[1:])
00067             else:
00068                 title = 'Help'
00069                 text = paragraphs[0]
00070         return render_template('help_command.html', title=title, text=text)
00071 
00072 helper = _Helper()
00073 
00074 
00075 def _add_subclass_info(inner, obj, base):
00076     if isinstance(base, tuple):
00077         for base in base:
00078             if type(obj) is base:
00079                 return inner
00080     elif type(obj) is base:
00081         return inner
00082     module = ''
00083     if obj.__class__.__module__ not in ('__builtin__', 'exceptions'):
00084         module = '<span class="module">%s.</span>' % obj.__class__.__module__
00085     return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
00086 
00087 
00088 class DebugReprGenerator(object):
00089 
00090     def __init__(self):
00091         self._stack = []
00092 
00093     def _sequence_repr_maker(left, right, base=object(), limit=8):
00094         def proxy(self, obj, recursive):
00095             if recursive:
00096                 return _add_subclass_info(left + '...' + right, obj, base)
00097             buf = [left]
00098             have_extended_section = False
00099             for idx, item in enumerate(obj):
00100                 if idx:
00101                     buf.append(', ')
00102                 if idx == limit:
00103                     buf.append('<span class="extended">')
00104                     have_extended_section = True
00105                 buf.append(self.repr(item))
00106             if have_extended_section:
00107                 buf.append('</span>')
00108             buf.append(right)
00109             return _add_subclass_info(u''.join(buf), obj, base)
00110         return proxy
00111 
00112     list_repr = _sequence_repr_maker('[', ']', list)
00113     tuple_repr = _sequence_repr_maker('(', ')', tuple)
00114     set_repr = _sequence_repr_maker('set([', '])', set)
00115     frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
00116     if deque is not None:
00117         deque_repr = _sequence_repr_maker('<span class="module">collections.'
00118                                           '</span>deque([', '])', deque)
00119     del _sequence_repr_maker
00120 
00121     def regex_repr(self, obj):
00122         pattern = repr(obj.pattern).decode('string-escape', 'ignore')
00123         if pattern[:1] == 'u':
00124             pattern = 'ur' + pattern[1:]
00125         else:
00126             pattern = 'r' + pattern
00127         return u're.compile(<span class="string regex">%s</span>)' % pattern
00128 
00129     def string_repr(self, obj, limit=70):
00130         buf = ['<span class="string">']
00131         escaped = escape(obj)
00132         a = repr(escaped[:limit])
00133         b = repr(escaped[limit:])
00134         if isinstance(obj, unicode):
00135             buf.append('u')
00136             a = a[1:]
00137             b = b[1:]
00138         if b != "''":
00139             buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
00140         else:
00141             buf.append(a)
00142         buf.append('</span>')
00143         return _add_subclass_info(u''.join(buf), obj, (str, unicode))
00144 
00145     def dict_repr(self, d, recursive, limit=5):
00146         if recursive:
00147             return _add_subclass_info(u'{...}', d, dict)
00148         buf = ['{']
00149         have_extended_section = False
00150         for idx, (key, value) in enumerate(d.iteritems()):
00151             if idx:
00152                 buf.append(', ')
00153             if idx == limit - 1:
00154                 buf.append('<span class="extended">')
00155                 have_extended_section = True
00156             buf.append('<span class="pair"><span class="key">%s</span>: '
00157                        '<span class="value">%s</span></span>' %
00158                        (self.repr(key), self.repr(value)))
00159         if have_extended_section:
00160             buf.append('</span>')
00161         buf.append('}')
00162         return _add_subclass_info(u''.join(buf), d, dict)
00163 
00164     def object_repr(self, obj):
00165         return u'<span class="object">%s</span>' % \
00166                escape(repr(obj).decode('utf-8', 'replace'))
00167 
00168     def dispatch_repr(self, obj, recursive):
00169         if obj is helper:
00170             return helper.get_help(None)
00171         if isinstance(obj, (int, long, float, complex)):
00172             return u'<span class="number">%r</span>' % obj
00173         if isinstance(obj, basestring):
00174             return self.string_repr(obj)
00175         if isinstance(obj, RegexType):
00176             return self.regex_repr(obj)
00177         if isinstance(obj, list):
00178             return self.list_repr(obj, recursive)
00179         if isinstance(obj, tuple):
00180             return self.tuple_repr(obj, recursive)
00181         if isinstance(obj, set):
00182             return self.set_repr(obj, recursive)
00183         if isinstance(obj, frozenset):
00184             return self.frozenset_repr(obj, recursive)
00185         if isinstance(obj, dict):
00186             return self.dict_repr(obj, recursive)
00187         if deque is not None and isinstance(obj, deque):
00188             return self.deque_repr(obj, recursive)
00189         return self.object_repr(obj)
00190 
00191     def fallback_repr(self):
00192         try:
00193             info = ''.join(format_exception_only(*sys.exc_info()[:2]))
00194         except:
00195             info = '?'
00196         return u'<span class="brokenrepr">&lt;broken repr (%s)&gt;' \
00197                u'</span>' % escape(info.decode('utf-8', 'ignore').strip())
00198 
00199     def repr(self, obj):
00200         recursive = False
00201         for item in self._stack:
00202             if item is obj:
00203                 recursive = True
00204                 break
00205         self._stack.append(obj)
00206         try:
00207             try:
00208                 return self.dispatch_repr(obj, recursive)
00209             except:
00210                 return self.fallback_repr()
00211         finally:
00212             self._stack.pop()
00213 
00214     def dump_object(self, obj):
00215         repr = items = None
00216         if isinstance(obj, dict):
00217             title = 'Contents of'
00218             items = []
00219             for key, value in obj.iteritems():
00220                 if not isinstance(key, basestring):
00221                     items = None
00222                     break
00223                 items.append((key, self.repr(value)))
00224         if items is None:
00225             items = []
00226             repr = self.repr(obj)
00227             for key in dir(obj):
00228                 try:
00229                     items.append((key, self.repr(getattr(obj, key))))
00230                 except:
00231                     pass
00232             title = 'Details for'
00233         title += ' ' + object.__repr__(obj)[1:-1]
00234         return render_template('dump_object.html', items=items,
00235                                title=title, repr=repr)
00236 
00237     def dump_locals(self, d):
00238         items = [(key, self.repr(value)) for key, value in d.items()]
00239         return render_template('dump_object.html', items=items,
00240                                title='Local variables in frame', repr=None)