debugtools/formatter.py
def _format_object(object):
    """
    # Instead of just printing <SomeType at 0xfoobar>, expand the fields.
    """

    attrs = iter(object.__dict__.items())
    if object.__class__:
        # Add class members too.
        attrs = chain(attrs, iter(object.__class__.__dict__.items()))

    # Remove private and protected variables
    # Filter needless exception classes which are added to each model.
    # Filter unremoved form.Meta (unline model.Meta) which makes no sense either
    is_model = isinstance(object, Model)
    is_form = isinstance(object, BaseForm)
    attrs = dict(
        (k, v)
        for k, v in attrs
            if not k.startswith('_')
            and not getattr(v, 'alters_data', False)
            and not (is_model and k in ('DoesNotExist', 'MultipleObjectsReturned'))
            and not (is_form and k in ('Meta',))
    )

    # Add members which are not found in __dict__.
    # This includes values such as auto_id, c, errors in a form.
    for member in dir(object):
        try:
            if member.startswith('_') or not hasattr(object, member):
                continue
        except HANDLED_EXCEPTIONS as e:
            attrs[member] = _format_exception(e)
            continue

        value = getattr(object, member)
        if callable(value) or member in attrs or getattr(value, 'alters_data', False):
            continue

        attrs[member] = value

    # Format property objects
    for name, value in list(attrs.items()):  # not iteritems(), so can delete.
        if isinstance(value, property):
            attrs[name] = _try_call(lambda: getattr(object, name))
        elif isinstance(value, types.FunctionType):
            if PY3:
                spec = inspect.getfullargspec(value)
            else:
                spec = inspect.getargspec(value)
            if len(spec.args) == 1 or len(spec.args) == len(spec.defaults or ()) + 1:
                if _is_unsafe_name(name):
                    # The delete and save methods should have an alters_data = True set.
                    # however, when delete or save methods are overridden, this is often missed.
                    attrs[name] = LiteralStr('<Skipped for safety reasons (could alter the database)>')
                else:
                    # should be simple method(self) signature to be callable in the template
                    # function may have args (e.g. BoundField.as_textarea) as long as they have defaults.
                    attrs[name] = _try_call(lambda: value(object))
            else:
                del attrs[name]
        elif hasattr(value, '__get__'):
            # fetched the descriptor, e.g. django.db.models.fields.related.ForeignRelatedObjectsDescriptor
            attrs[name] = value = _try_call(lambda: getattr(object, name), return_exceptions=True)
            if isinstance(value, Manager):
                attrs[name] = LiteralStr('<{0} manager>'.format(value.__class__.__name__))
            elif isinstance(value, AttributeError):
                del attrs[name]  # e.g. Manager isn't accessible via Model instances.
            elif isinstance(value, HANDLED_EXCEPTIONS):
                attrs[name] = _format_exception(value)

    # Include representations which are relevant in template context.
    if getattr(object, '__str__', None) is not object.__str__:
        attrs['__str__'] = _try_call(lambda: smart_str(object))
    elif getattr(object, '__unicode__', None) is not object.__unicode__:
        attrs['__unicode__'] = _try_call(lambda: smart_str(object))

    if hasattr(object, '__getattr__'):
        attrs['__getattr__'] = LiteralStr('<dynamic attribute>')
    if hasattr(object, '__getitem__'):
        attrs['__getitem__'] = LiteralStr('<dynamic item>')
    if hasattr(object, '__iter__'):
        attrs['__iter__'] = LiteralStr('<iterator object>')
    if hasattr(object, '__len__'):
        attrs['__len__'] = len(object)

    # Add known __getattr__ members which are useful for template designers.
    if isinstance(object, BaseForm):
        for field_name in list(object.fields.keys()):
            attrs[field_name] = object[field_name]
        del attrs['__getitem__']

    return _format_dict(attrs)
def _format_object(object):
    """
    # Instead of just printing <SomeType at 0xfoobar>, expand the fields.
    """

    attrs = iter(object.__dict__.items())
    if object.__class__:
        # Add class members too.
        attrs = chain(attrs, iter(object.__class__.__dict__.items()))

    # Remove private and protected variables
    # Filter needless exception classes which are added to each model.
    # Filter unremoved form.Meta (unline model.Meta) which makes no sense either
    is_model = isinstance(object, Model)
    is_form = isinstance(object, BaseForm)
    attrs = dict(
        (k, v)
        for k, v in attrs
            if not k.startswith('_')
            and not getattr(v, 'alters_data', False)
            and not (is_model and k in ('DoesNotExist', 'MultipleObjectsReturned'))
            and not (is_form and k in ('Meta',))
    )

    # Add members which are not found in __dict__.
    # This includes values such as auto_id, c, errors in a form.
    for member in dir(object):
        try:
            if member.startswith('_') or not hasattr(object, member):
                continue
        except HANDLED_EXCEPTIONS as e:
            attrs[member] = _format_exception(e)
            continue

        value = getattr(object, member)
        if callable(value) or member in attrs or getattr(value, 'alters_data', False):
            continue

        attrs[member] = value

    # Format property objects
    for name, value in list(attrs.items()):  # not iteritems(), so can delete.
        if isinstance(value, property):
            attrs[name] = _try_call(lambda: getattr(object, name))
        elif isinstance(value, types.FunctionType):
            if PY3:
                spec = inspect.getfullargspec(value)
            else:
                spec = inspect.getargspec(value)
            if len(spec.args) == 1 or len(spec.args) == len(spec.defaults or ()) + 1:
                if _is_unsafe_name(name):
                    # The delete and save methods should have an alters_data = True set.
                    # however, when delete or save methods are overridden, this is often missed.
                    attrs[name] = LiteralStr('<Skipped for safety reasons (could alter the database)>')
                else:
                    # should be simple method(self) signature to be callable in the template
                    # function may have args (e.g. BoundField.as_textarea) as long as they have defaults.
                    attrs[name] = _try_call(lambda: value(object))
            else:
                del attrs[name]
        elif hasattr(value, '__get__'):
            # fetched the descriptor, e.g. django.db.models.fields.related.ForeignRelatedObjectsDescriptor
            attrs[name] = value = _try_call(lambda: getattr(object, name), return_exceptions=True)
            if isinstance(value, Manager):
                attrs[name] = LiteralStr('<{0} manager>'.format(value.__class__.__name__))
            elif isinstance(value, AttributeError):
                del attrs[name]  # e.g. Manager isn't accessible via Model instances.
            elif isinstance(value, HANDLED_EXCEPTIONS):
                attrs[name] = _format_exception(value)

    # Include representations which are relevant in template context.
    if getattr(object, '__str__', None) is not object.__str__:
        attrs['__str__'] = _try_call(lambda: smart_str(object))
    elif getattr(object, '__unicode__', None) is not object.__unicode__:
        attrs['__unicode__'] = _try_call(lambda: smart_str(object))

    if hasattr(object, '__getattr__'):
        attrs['__getattr__'] = LiteralStr('<dynamic attribute>')
    if hasattr(object, '__getitem__'):
        attrs['__getitem__'] = LiteralStr('<dynamic item>')
    if hasattr(object, '__iter__'):
        attrs['__iter__'] = LiteralStr('<iterator object>')
    if hasattr(object, '__len__'):
        attrs['__len__'] = len(object)

    # Add known __getattr__ members which are useful for template designers.
    if isinstance(object, BaseForm):
        for field_name in list(object.fields.keys()):
            attrs[field_name] = object[field_name]
        del attrs['__getitem__']

    return _format_dict(attrs)
# Instead of just printing <SomeType at 0xfoobar>, expand the fields.