PageRenderTime 83ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/pyramid_debugtoolbar/repr.py

https://bitbucket.org/biideal/pyramid_debugtoolbar
Python | 300 lines | 280 code | 5 blank | 15 comment | 3 complexity | 70805078ba843f77da746fff249ed3f3 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. """
  3. werkzeug.debug.repr
  4. ~~~~~~~~~~~~~~~~~~~
  5. This module implements object representations for debugging purposes.
  6. Unlike the default repr these reprs expose a lot more information and
  7. produce HTML instead of ASCII.
  8. Together with the CSS and JavaScript files of the debugger this gives
  9. a colorful and more compact output.
  10. :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
  11. :license: BSD.
  12. """
  13. import sys
  14. import re
  15. from traceback import format_exception_only
  16. try:
  17. from collections import deque
  18. except ImportError: # pragma: no cover
  19. deque = None
  20. from pyramid_debugtoolbar.compat import text_
  21. from pyramid_debugtoolbar.compat import text_type
  22. from pyramid_debugtoolbar.compat import binary_type
  23. from pyramid_debugtoolbar.compat import iteritems_
  24. from pyramid_debugtoolbar.compat import string_types
  25. from pyramid_debugtoolbar.compat import long
  26. from pyramid_debugtoolbar.compat import PY3
  27. from pyramid_debugtoolbar.utils import escape
  28. missing = object()
  29. _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
  30. RegexType = type(_paragraph_re)
  31. HELP_HTML = '''\
  32. <div class=box>
  33. <h3>%(title)s</h3>
  34. <pre class=help>%(text)s</pre>
  35. </div>\
  36. '''
  37. OBJECT_DUMP_HTML = '''\
  38. <div class=box>
  39. <h3>%(title)s</h3>
  40. %(repr)s
  41. <table>%(items)s</table>
  42. </div>\
  43. '''
  44. def debug_repr(obj):
  45. """Creates a debug repr of an object as HTML unicode string."""
  46. return DebugReprGenerator().repr(obj)
  47. def dump(obj=missing):
  48. """Print the object details to stdout._write (for the interactive
  49. console of the web debugger.
  50. """
  51. gen = DebugReprGenerator()
  52. if obj is missing:
  53. rv = gen.dump_locals(sys._getframe(1).f_locals)
  54. else:
  55. rv = gen.dump_object(obj)
  56. sys.stdout._write(rv)
  57. class _Helper(object):
  58. """Displays an HTML version of the normal help, for the interactive
  59. debugger only because it requires a patched sys.stdout.
  60. """
  61. def __repr__(self):
  62. return 'Type help(object) for help about object.'
  63. def __call__(self, topic=None):
  64. if topic is None:
  65. sys.stdout._write('<span class=help>%s</span>' % repr(self))
  66. return
  67. import pydoc
  68. pydoc.help(topic)
  69. rv = text_(sys.stdout.reset(), 'utf-8', 'ignore')
  70. paragraphs = _paragraph_re.split(rv)
  71. if len(paragraphs) > 1:
  72. title = paragraphs[0]
  73. text = '\n\n'.join(paragraphs[1:])
  74. else: # pragma: no cover
  75. title = 'Help'
  76. text = paragraphs[0]
  77. sys.stdout._write(HELP_HTML % {'title': title, 'text': text})
  78. helper = _Helper()
  79. def _add_subclass_info(inner, obj, bases):
  80. if isinstance(bases, tuple):
  81. for base in bases:
  82. if type(obj) is base:
  83. return inner
  84. elif type(obj) is bases:
  85. return inner
  86. module = ''
  87. if obj.__class__.__module__ not in ('builtins', '__builtin__','exceptions'):
  88. module = '<span class="module">%s.</span>' % obj.__class__.__module__
  89. return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
  90. class DebugReprGenerator(object):
  91. def __init__(self):
  92. self._stack = []
  93. def _sequence_repr_maker(left, right, base=object()):
  94. def proxy(self, obj, recursive):
  95. if recursive:
  96. return _add_subclass_info(left + '...' + right, obj, base)
  97. buf = [left]
  98. for idx, item in enumerate(obj):
  99. if idx:
  100. buf.append(', ')
  101. buf.append(self.repr(item))
  102. buf.append(right)
  103. return _add_subclass_info(text_(''.join(buf)), obj, base)
  104. return proxy
  105. list_repr = _sequence_repr_maker('[', ']', list)
  106. tuple_repr = _sequence_repr_maker('(', ')', tuple)
  107. set_repr = _sequence_repr_maker('set([', '])', set)
  108. frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
  109. if deque is not None:
  110. deque_repr = _sequence_repr_maker('<span class="module">collections.'
  111. '</span>deque([', '])', deque)
  112. del _sequence_repr_maker
  113. def regex_repr(self, obj):
  114. if PY3:
  115. pattern = text_("'%s'" % str(obj.pattern), 'string-escape', 'ignore')
  116. pattern = 'r' + pattern
  117. else:
  118. pattern = text_(repr(obj.pattern), 'string-escape', 'ignore')
  119. if pattern[:1] == 'u':
  120. pattern = 'ur' + pattern[1:]
  121. else:
  122. pattern = 'r' + pattern
  123. return text_(
  124. 're.compile(<span class="string regex">%s</span>)' % pattern)
  125. def py2_string_repr(self, obj, limit=70):
  126. buf = ['<span class="string">']
  127. escaped = escape(obj)
  128. a = repr(escaped[:limit])
  129. b = repr(escaped[limit:])
  130. if isinstance(obj, text_type):
  131. buf.append('u')
  132. a = a[1:]
  133. b = b[1:]
  134. if b != "''":
  135. buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
  136. else:
  137. buf.append(a)
  138. buf.append('</span>')
  139. return _add_subclass_info(text_('').join(buf), obj, (str, unicode))
  140. def py3_text_repr(self, obj, limit=70):
  141. buf = ['<span class="string">']
  142. escaped = escape(obj)
  143. a = repr(escaped[:limit])
  144. b = repr(escaped[limit:])
  145. if b != "''":
  146. buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
  147. else:
  148. buf.append(a)
  149. buf.append('</span>')
  150. return _add_subclass_info(text_(''.join(buf)), obj, text_type)
  151. def py3_binary_repr(self, obj, limit=70):
  152. buf = ['<span class="string">']
  153. escaped = escape(text_(obj, 'utf-8', 'replace'))
  154. a = repr(escaped[:limit])
  155. b = repr(escaped[limit:])
  156. buf.append('b')
  157. if b != "''":
  158. buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
  159. else:
  160. buf.append(a)
  161. buf.append('</span>')
  162. return _add_subclass_info(text_(''.join(buf)), obj, binary_type)
  163. def dict_repr(self, d, recursive):
  164. if recursive:
  165. return _add_subclass_info(text_('{...}'), d, dict)
  166. buf = ['{']
  167. for idx, (key, value) in enumerate(iteritems_(d)):
  168. if idx:
  169. buf.append(', ')
  170. buf.append('<span class="pair"><span class="key">%s</span>: '
  171. '<span class="value">%s</span></span>' %
  172. (self.repr(key), self.repr(value)))
  173. buf.append('}')
  174. return _add_subclass_info(text_(''.join(buf)), d, dict)
  175. def object_repr(self, obj):
  176. return text_('<span class="object">%s</span>' %
  177. escape(text_(repr(obj), 'utf-8', 'replace')))
  178. def dispatch_repr(self, obj, recursive):
  179. if obj is helper:
  180. return text_('<span class="help">%r</span>' % helper)
  181. if isinstance(obj, (int, long, float, complex)):
  182. return text_('<span class="number">%r</span>' % obj)
  183. if PY3:
  184. if isinstance(obj, text_type):
  185. return self.py3_text_repr(obj)
  186. if isinstance(obj, binary_type):
  187. return self.py3_binary_repr(obj)
  188. else:
  189. if isinstance(obj, basestring):
  190. return self.py2_string_repr(obj)
  191. if isinstance(obj, RegexType):
  192. return self.regex_repr(obj)
  193. if isinstance(obj, list):
  194. return self.list_repr(obj, recursive)
  195. if isinstance(obj, tuple):
  196. return self.tuple_repr(obj, recursive)
  197. if isinstance(obj, set):
  198. return self.set_repr(obj, recursive)
  199. if isinstance(obj, frozenset):
  200. return self.frozenset_repr(obj, recursive)
  201. if isinstance(obj, dict):
  202. return self.dict_repr(obj, recursive)
  203. if deque is not None and isinstance(obj, deque):
  204. return self.deque_repr(obj, recursive)
  205. return self.object_repr(obj)
  206. def fallback_repr(self):
  207. try:
  208. info = ''.join(format_exception_only(*sys.exc_info()[:2]))
  209. except Exception: # pragma: no cover
  210. info = '?'
  211. return text_(
  212. '<span class="brokenrepr">&lt;broken repr (%s)&gt;'
  213. '</span>' % escape(text_(info, 'utf-8', 'ignore').strip())
  214. )
  215. def repr(self, obj):
  216. recursive = False
  217. for item in self._stack:
  218. if item is obj:
  219. recursive = True
  220. break
  221. self._stack.append(obj)
  222. try:
  223. try:
  224. return self.dispatch_repr(obj, recursive)
  225. except Exception:
  226. return self.fallback_repr()
  227. finally:
  228. self._stack.pop()
  229. def dump_object(self, obj):
  230. repr = items = None
  231. if isinstance(obj, dict):
  232. title = 'Contents of'
  233. items = []
  234. for key, value in iteritems_(obj):
  235. if not isinstance(key, string_types):
  236. items = None
  237. break
  238. items.append((key, self.repr(value)))
  239. if items is None:
  240. items = []
  241. repr = self.repr(obj)
  242. for key in dir(obj):
  243. try:
  244. items.append((key, self.repr(getattr(obj, key))))
  245. except Exception:
  246. pass
  247. title = 'Details for'
  248. title += ' ' + object.__repr__(obj)[1:-1]
  249. return self.render_object_dump(items, title, repr)
  250. def dump_locals(self, d):
  251. items = [(key, self.repr(value)) for key, value in d.items()]
  252. return self.render_object_dump(items, 'Local variables in frame')
  253. def render_object_dump(self, items, title, repr=None):
  254. html_items = []
  255. for key, value in items:
  256. html_items.append('<tr><th>%s<td><pre class=repr>%s</pre>' %
  257. (escape(key), value))
  258. if not html_items:
  259. html_items.append('<tr><td><em>Nothing</em>')
  260. return OBJECT_DUMP_HTML % {
  261. 'title': escape(title),
  262. 'repr': repr and '<pre class=repr>%s</pre>' % repr or '',
  263. 'items': '\n'.join(html_items)
  264. }