Package: djexcept
djexcept is a module that brings flexible exception handling to the
Django web framework.A weakness of the great Django web framework are its poor builtin error
handling capabilities. In plain Django, you can define custom error
handlers for 4 different exceptions (SuspiciousOperation,PermissionDenied,Http404and a fallback handler for runtime
errors which doesn’t even get the exception passed to decide what to do
best).That is where djexcept hooks in. For every type of exception you like -
even custom ones - it lets you decide how to handle it. It provides a
default exception handler that makes additional information about the
exception available in a template context and then renders a template of
your choice. You can define the template to use, the HTTP status code to
send and even change the exception handler on a per exception basis.
However, sensible defaults are provided for you to get up and running as
quick as possible.Note:djexcept is still a young pice of software that I use in my
Django projects. There may be bugs I haven’t found yet. However, the
codebase is reasonably small without lots of magic in it, hence things
shouldn’t go terribly wrong after all. Any bug reports and code review
are highly appreciated.InstallationIt’s as easy as this:pip install djexceptCompatibility with Django versionsdjexcept is tested to work with Django 1.10+, but it should also work
with earlier versions without problems. The contained middleware is
compatible with both the newMIDDLEWAREas well as the pre 1.10MIDDLEWARE_CLASSESmechanism.Getting startedAdddjexcept.middleware.ExceptionHandlingMiddlewareto yourMIDDLEWAREinsettings.py. No modification ofINSTALLED_APPSis needed.Add the@djexcept.register()decorator to every custom exception
you’d like to be handled by djexcept.import djexcept
@djexcept.register()
class MyCustomException(Exception):
    ...For exceptions you haven’t defined yourself, you can call theregister()function e.g. from inside yoururls.pyfile like
so:import djexcept
djexcept.register(ValueError)Create a template calledexception.htmlwhich might contain the
following:<h1>{{ exc_modname }}</h1>
<p>An exception of type {{ exc_name }} from {{ exc_module }} occured.</p>
<p>Exception message: {{ exc }}</p>
<p>The HTTP status code sent with this page is {{ status }}.</p>Raise an exception you have just registered from within your view and
watch what happens.Customization of exception handlingRegistration parametersFor every type of exception you register, you may specify several
parameters that influence how exceptions of that type are handled. The
following parameters may be passed todjexcept.register()as keyword
arguments:handler: exception handler (callable or string of typepath.to.module.function)handle_subtypes: boolean that controls whetherunregisteredsubclasses of the exception class being registered should be handled
in the same way as their ancestorAll other keyword arguments are passed through straight to the exception
handler. The following are those that the built-in exception handler
understands:template_name: name of the template to use for rendering the
error pagestatus: HTTP status code for the error responseinclude_requestwhether to include the request in template
contextcontextdictionary of which a copy is used as starting point for
the template context; values in that context won’t be overwrittenIf some of these keyword arguments are not specified, default values for
them will automatically be inserted according to theDJEXCEPT_*settings (see below) before the handler is called.It is at the sole discretion of the chosen exception handler to
interpret these keyword arguments as desired.The built-in default exception handlerdjexcept’s built-in exception handler understands all of the keyword
arguments listed above and will create either adjango.template.response.SimpleTemplateResponseor adjango.template.response.TemplateResponse, depending on the setting
forDJEXCEPT_INCLUDE_REQUEST.It usesdjexcept.util.populate_context()to populate the context
with some handy values regarding the exception that you can use in your
template. Please see the API reference for details about these values.Custom exception handlersThere is nothing stopping you from writing your own exception handler,
as long as it follows some guidelines.An exception handler has to be a callable that accepts as positional
arguments the request, the exception object and at least the keyword
arguments listed in the previous section, because these, if unspecified
at time of registration, will be filled with default values. It must
return either adjango.http.response.HttpResponseobject orNone, in which case the exception isn’t handled by djexcept and
Django’s regular exception handling sets in.If your custom handler doesn’t care about some of the mandatory keyword
arguments, you could insert a**kwargsat the end of its argument
list to catch any extra keyword arguments and have it working even when
new ones are added to djexcept in the future.Note:Please keep in mind that exceptions raised from inside
exception handlers are not handled by djexcept. to prevent creating an
infinite loop.Here is a simple example that populates the context with some value and
then calls djexcept’s built-in handler to construct the response. Please
don’t forget to create a copy of the context object before altering it,
because dictionaries are mutable and you might otherwise change the
context of subsequent exceptions.import time
from djexcept.handlers import handle_exception

def my_exception_handler(request, exc, context=None, **kwargs):
    context = dict(context or {})
    context.setdefault("time", time.ctime())
    return handle_exception(request, exc, context=context, **kwargs)Handling child exception typesdjexcept has the ability to automatically handle any sub-type of a
registered exception type in the same way as their registered ancestor.
This behaviour is controlled by theDJEXCEPT_HANDLE_SUBTYPESsetting
and is enabled by default.In practice, that allows you to write your own hirarchy of meaningful
exception types that you can use within your view logic. Consider the
following example:import djexcept

@djexcept.register(template_name="exceptions/business_logic_violation.html")
class BusinessRuleViolation(Exception):
    pass

class NegativeAccountBalance(BusinessRuleViolation):
    pass

class OfferExpired(BusinessRuleViolation):
    passUsing this example, raising eitherNegativeAccountBalanceorOfferExpiredwill be handled as it was aBusinessRuleViolation,
what it in fact is.In your template, you could now introduce some conditional content:{% if exc_name == "NegativeAccountBalance" %}
    ...
{% elif exc_name == "OfferExpired" %}
    ...
{% endif %}In theory, you could even catch all possible sub-types ofException,
however doing so is not recommended because it will hide potential bugs
that might occur at runtime:import djexcept
djexcept.register(Exception)Sub-type handling can also be disabled per type by passinghandle_subtypes = Falsetodjexcept.register().Configurationdjexcept introduces some new settings that may be used insettings.pyto customize its behaviour. Neither of them are required
for djexcept to work, because all have sensible default values that
should be just fine for most users.DJEXCEPT_TEMPLATE_NAME(default:exception.html)Name of the default template to use.DJEXCEPT_STATUS(default:400)Default HTTP status code for exception pages.DJEXCEPT_EXCEPTION_HANDLER(default:djexcept.handlers.handle_exception)Default exception handler. Please specify it as a string of the formpath.to.module.function, as known from Django’sMIDDLEWARElist.DJEXCEPT_HANDLE_SUBTYPES(default:True)Whether to treatunregisteredsubclasses of registered exception
types in the same way as their ancestor.DJEXCEPT_INCLUDE_REQUEST(default:True)Whether to include therequestobject into the template context.DJEXCEPT_DISABLE_ON_DEBUG(default:False)Whether to disable djexcept’s exception handling when Django’s debug
mode is enabled. You might find this useful to see full tracebacks
instead of your custom exception pages while developing your project.API referenceRegistrationThe public API methods of thedjexcept.registrationsubmodule are
also directly available indjexceptfor convenience.djexcept.register(exception_class, **attrs)Registers the given Exception subclass for error handling with djexcept.The additional keyword arguments are treated as follows: *handler:
an exception handler to overwrite the default one *handle_subtypes: may be used to overwrite theDJEXCEPT_HANDLE_SUBTYPESsetting on a per exception basisAll other keyword arguments are passed directly to the handler function
when there is an exception to handle.This function may also be used as a class decorator when defining custom
exceptions.djexcept.exceptions.RegistrationErroris raised if the class was
already registered.djexcept.unregister(exception_class)Unregisters the given exception class from djexcept.djexcept.exceptions.RegistrationErroris raised if the class wasn’t
registered.djexcept.is_registered(exception_class)Checks whether the given Exception subclass is registered for use with
djexcept.djexcept.is_handled(exception_class)Checks whether the given exception class is handled by djexcept. IfDJEXCEPT_HANDLE_SUBTYPESsetting is disabled and not overwritten at
registration stage, this function returns the same result asdjexcept.is_registered().Handlersdjexcept.handler.handle_exception(request, exc, template_name=None, status=None, include_request=None, context=None)This is djexcept’s default exception handler.It usesdjexcept.util.populate_context()to populate the context
with some handy values regarding the exception that you can use in your
template.Adjango.template.response.SimpleTemplateResponseordjango.template.response.TemplateResponseis returned.Utilpopulate_context(context, exc, status=None)Populates the given context dictionary with djexcept’s handy default
values. The dictionary is altered in-place, but values that are already
present won’t be overwritten.The following values are added to the context: *exc: the exception
object *exc_name: the name of the exception type (e.g.PermissionDeniedorValueError) *exc_module: the module
name of the exception’s type (e.g.django.core.exceptionsorbuiltins) *exc_modname: both concatenated, separated by a
period (e.g.django.core.exceptions.PermissionDeniedorbuiltins.ValueError) *status: the HTTP status code used (only
added if notNone)Exceptionsdjexcept.exceptions.ImproperlyConfiguredIs raised when something went wrong at settings parsing.djexcept.exceptions.RegistrationErrorIs raised when an illegal call todjexcept.register()ordjexcept.unregister()is made.ContributingContributions are always welcome. Please use issues and pull requests on
GitHub.