Source code for django_utils.templatetags.debug
from django import template
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
from django.db import models
import six
import copy
import pprint
import datetime
register = template.Library()
class _Formatter(object):
formatters_type = {}
formatters_instance = []
[docs]class Formatter(_Formatter):
MAX_LENGTH = 100
MAX_LENGTH_DOTS = 3
def __init__(self, max_depth=3):
'''Initialize the formatter with a given maximum default depth
:param max_depth: The maximum depth to print
'''
self.max_depth = max_depth
def _register(*types):
'''Register a handler for the given type(s)
:param types: The type(s) to handle
:return: The unmodified decorated function
'''
def _register(func):
for type_ in types:
_Formatter.formatters_type[type_] = func
_Formatter.formatters_instance.append((type_, func))
return func
return _register
[docs] @_register(*six.integer_types)
def format_int(self, value, depth, show_protected, show_special):
'''Format an integer/long
:param value: an int/long to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> str(formatter(1, 0))
'1'
>>> formatter(1, 1)
'1'
'''
return value
[docs] @_register(six.binary_type)
def format_str(self, value, depth, show_protected, show_special):
'''Format a string
:param value: a str value to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> str(formatter('test'))
'test'
>>> str(formatter(six.b('test')))
'test'
'''
return self.format_unicode(value.decode('utf-8', 'replace'), depth,
show_protected, show_special)
[docs] @_register(six.text_type)
def format_unicode(self, value, depth, show_protected, show_special):
'''Format a string
:param value: a unicode value to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> original_max_length = formatter.MAX_LENGTH
>>> formatter.MAX_LENGTH = 10
>>> str(formatter('x' * 11))
'xxxxxxx...'
>>> formatter.MAX_LENGTH = original_max_length
'''
if value[self.MAX_LENGTH:]:
value = value[:self.MAX_LENGTH - self.MAX_LENGTH_DOTS]
value += self.MAX_LENGTH_DOTS * '.'
return value
[docs] @_register(list)
def format_list(self, value, depth, show_protected, show_special):
'''Format a string
:param value: a list to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> formatter(list(range(5)))
'[0, 1, 2, 3, 4]'
'''
values = []
for i, v in enumerate(value):
values.append(self.format(v, depth - 1, show_protected,
show_special))
return values
[docs] @_register(datetime.datetime, datetime.date)
def format_datetime(self, value, depth, show_protected, show_special):
'''Format a date
:param value: a date to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> formatter(datetime.date(2000, 1, 2))
'<date:2000-01-02>'
>>> formatter(datetime.datetime(2000, 1, 2, 3, 4, 5, 6))
'<datetime:2000-01-02 03:04:05.000006>'
'''
return '<%s:%s>' % (
value.__class__.__name__,
value,
)
[docs] @_register(dict)
def format_dict(self, value, depth, show_protected, show_special):
'''Format a string
:param value: a str value to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> formatter({'a': 1, 'b': 2}, 5)
'{a: 1, b: 2}'
'''
def key(key):
'''Make sure that hidden/protected variables end up at the end'''
key = key[0]
if 'a' <= key[0].lower() <= 'z' or '0' <= key[0] <= '9':
return 0, key
else:
return 1, key
output = []
for k, v in sorted(value.items(), key=key):
output.append('%s: %s' % (
k, self(v, depth - 1, show_protected, show_special)))
return '{%s}' % self.format_unicode(
', '.join(output), depth - 1, show_protected, show_special)
[docs] @_register(models.Model)
def format_model(self, value, depth, show_protected, show_special):
'''Format a string
:param value: a str value to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> from django.contrib.auth.models import User
>>> user = User()
>>> del user.date_joined
>>> str(formatter(user, 5, show_protected=False)[:30])
'<User {email: , first_name: , '
'''
return self.format_object(value, depth, False, False)
[docs] def format_object(self, value, depth, show_protected, show_special):
'''Format an object
:param value: an object to format
:param depth: the current depth
:return: a formatted string
>>> formatter = Formatter()
>>> original_max_length = formatter.MAX_LENGTH
>>> formatter.MAX_LENGTH = 50
>>> class Spam(object):
... x = 1
... _y = 2
... __z = 3
... __hidden_ = 4
>>> spam = Spam()
>>> str(formatter(spam, show_protected=True, show_special=True))
'<Spam {x: 1, _Spam__hidden_: 4, _Spam__z: 3, __dict__:...}>'
>>> str(formatter(spam, show_protected=False, show_special=False))
'<Spam {x: 1}>'
>>> formatter.MAX_LENGTH = original_max_length
'''
dict_ = getattr(value, '__dict__', None)
if dict_:
dict_ = dict(dict_)
else:
dict_ = {}
for k in dir(value):
v = getattr(value, k, None)
if v is not None and not hasattr(v, '__call__'):
dict_[k] = v
for k in list(dict_.keys()):
if k.startswith('__'):
if not show_special:
dict_.pop(k)
elif k.startswith('_') and not show_protected:
dict_.pop(k)
# Difference between Python 2 and 3, the results are covered by tests
# regardless so the no cover is not important :)
if hasattr(value, '__name__'): # pragma: no cover
name = value.__name__
elif(hasattr(value, '__class__') and
hasattr(value.__class__, '__name__')): # pragma: no cover
name = value.__class__.__name__
else: # pragma: no cover
module = __name__
name = str(value).replace(module + '.', '', 1)
return '<%s %s>' % (
name,
self.format(dict_, depth - 1, show_protected, show_special),
)
[docs] def format(self, value, depth, show_protected, show_special):
'''Call the formatter with the given value to format and optional depth
>>> formatter = Formatter()
>>> class Eggs: pass
>>> formatter(Eggs)
'<Eggs {}>'
'''
# Specific "is None" check since we don't want to replace 0
if depth is None:
depth = self.max_depth
elif depth <= 0:
return self.format_unicode(six.text_type(value), depth - 1,
show_protected, show_special)
formatter = self.formatters_type.get(type(value))
if not formatter:
for k, v in self.formatters_instance:
if isinstance(value, k):
formatter = v
break
if not formatter:
formatter = Formatter.format_object
return formatter(self, value, depth, show_protected, show_special)
def __call__(self, value, depth=None, show_protected=True,
show_special=False):
formatted = self.format(value, depth, show_protected, show_special)
if not isinstance(formatted, six.string_types):
formatted = pprint.pformat(formatted)
return formatted
[docs]@register.filter
def debug(value, max_depth=3):
'''Debug template filter to print variables in a pretty way
>>> str(debug(123).strip())
'<pre style="border: 1px solid #fcc; background-color: #ccc;">123</pre>'
'''
value = copy.deepcopy(value)
formatter = Formatter(max_depth=max_depth)
formatted_safe = mark_safe('''
<pre style="border: 1px solid #fcc; background-color: #ccc;">%s</pre>
''' % conditional_escape(formatter(value)))
return formatted_safe