1
# -*- coding: utf-8 -*-
6
This module implements object representations for debugging purposes.
7
Unlike the default repr these reprs expose a lot more information and
8
produce HTML instead of ASCII.
10
Together with the CSS and JavaScript files of the debugger this gives
11
a colorful and more compact output.
13
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
18
from traceback import format_exception_only
20
from collections import deque
23
from werkzeug.utils import escape
24
from werkzeug.debug.utils import render_template
28
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
29
RegexType = type(_paragraph_re)
33
"""Creates a debug repr of an object as HTML unicode string."""
34
return DebugReprGenerator().repr(obj)
37
def dump(obj=missing):
38
"""Print the object details to stdout._write (for the interactive
39
console of the web debugger.
41
gen = DebugReprGenerator()
43
rv = gen.dump_locals(sys._getframe(1).f_locals)
45
rv = gen.dump_object(obj)
49
class _Helper(object):
50
"""Displays an HTML version of the normal help, for the interactive
51
debugger only because it requires a patched sys.stdout.
54
def __call__(self, topic=None):
55
sys.stdout._write(self.get_help(topic))
57
def get_help(self, topic):
62
rv = sys.stdout.reset().decode('utf-8', 'ignore')
63
paragraphs = _paragraph_re.split(rv)
64
if len(paragraphs) > 1:
66
text = '\n\n'.join(paragraphs[1:])
70
return render_template('help_command.html', title=title, text=text)
75
def _add_subclass_info(inner, obj, base):
76
if isinstance(base, tuple):
80
elif type(obj) is base:
83
if obj.__class__.__module__ not in ('__builtin__', 'exceptions'):
84
module = '<span class="module">%s.</span>' % obj.__class__.__module__
85
return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
88
class DebugReprGenerator(object):
93
def _sequence_repr_maker(left, right, base=object(), limit=8):
94
def proxy(self, obj, recursive):
96
return _add_subclass_info(left + '...' + right, obj, base)
98
have_extended_section = False
99
for idx, item in enumerate(obj):
103
buf.append('<span class="extended">')
104
have_extended_section = True
105
buf.append(self.repr(item))
106
if have_extended_section:
107
buf.append('</span>')
109
return _add_subclass_info(u''.join(buf), obj, base)
112
list_repr = _sequence_repr_maker('[', ']', list)
113
tuple_repr = _sequence_repr_maker('(', ')', tuple)
114
set_repr = _sequence_repr_maker('set([', '])', set)
115
frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
116
if deque is not None:
117
deque_repr = _sequence_repr_maker('<span class="module">collections.'
118
'</span>deque([', '])', deque)
119
del _sequence_repr_maker
121
def regex_repr(self, obj):
122
pattern = repr(obj.pattern).decode('string-escape', 'ignore')
123
if pattern[:1] == 'u':
124
pattern = 'ur' + pattern[1:]
126
pattern = 'r' + pattern
127
return u're.compile(<span class="string regex">%s</span>)' % pattern
129
def string_repr(self, obj, limit=70):
130
buf = ['<span class="string">']
131
escaped = escape(obj)
132
a = repr(escaped[:limit])
133
b = repr(escaped[limit:])
134
if isinstance(obj, unicode):
139
buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
142
buf.append('</span>')
143
return _add_subclass_info(u''.join(buf), obj, (str, unicode))
145
def dict_repr(self, d, recursive, limit=5):
147
return _add_subclass_info(u'{...}', d, dict)
149
have_extended_section = False
150
for idx, (key, value) in enumerate(d.iteritems()):
154
buf.append('<span class="extended">')
155
have_extended_section = True
156
buf.append('<span class="pair"><span class="key">%s</span>: '
157
'<span class="value">%s</span></span>' %
158
(self.repr(key), self.repr(value)))
159
if have_extended_section:
160
buf.append('</span>')
162
return _add_subclass_info(u''.join(buf), d, dict)
164
def object_repr(self, obj):
165
return u'<span class="object">%s</span>' % \
166
escape(repr(obj).decode('utf-8', 'replace'))
168
def dispatch_repr(self, obj, recursive):
170
return helper.get_help(None)
171
if isinstance(obj, (int, long, float, complex)):
172
return u'<span class="number">%r</span>' % obj
173
if isinstance(obj, basestring):
174
return self.string_repr(obj)
175
if isinstance(obj, RegexType):
176
return self.regex_repr(obj)
177
if isinstance(obj, list):
178
return self.list_repr(obj, recursive)
179
if isinstance(obj, tuple):
180
return self.tuple_repr(obj, recursive)
181
if isinstance(obj, set):
182
return self.set_repr(obj, recursive)
183
if isinstance(obj, frozenset):
184
return self.frozenset_repr(obj, recursive)
185
if isinstance(obj, dict):
186
return self.dict_repr(obj, recursive)
187
if deque is not None and isinstance(obj, deque):
188
return self.deque_repr(obj, recursive)
189
return self.object_repr(obj)
191
def fallback_repr(self):
193
info = ''.join(format_exception_only(*sys.exc_info()[:2]))
196
return u'<span class="brokenrepr"><broken repr (%s)>' \
197
u'</span>' % escape(info.decode('utf-8', 'ignore').strip())
201
for item in self._stack:
205
self._stack.append(obj)
208
return self.dispatch_repr(obj, recursive)
210
return self.fallback_repr()
214
def dump_object(self, obj):
216
if isinstance(obj, dict):
217
title = 'Contents of'
219
for key, value in obj.iteritems():
220
if not isinstance(key, basestring):
223
items.append((key, self.repr(value)))
226
repr = self.repr(obj)
229
items.append((key, self.repr(getattr(obj, key))))
232
title = 'Details for'
233
title += ' ' + object.__repr__(obj)[1:-1]
234
return render_template('dump_object.html', items=items,
235
title=title, repr=repr)
237
def dump_locals(self, d):
238
items = [(key, self.repr(value)) for key, value in d.items()]
239
return render_template('dump_object.html', items=items,
240
title='Local variables in frame', repr=None)