diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 4201cfe..e0bee53 100644
|
a
|
b
|
def force_escape(value):
|
| 414 | 414 | """ |
| 415 | 415 | return escape(value) |
| 416 | 416 | |
| | 417 | @register.filter(is_safe=True) |
| | 418 | @stringfilter |
| | 419 | def nonraising_force_escape(value): |
| | 420 | """ |
| | 421 | Escapes a string's HTML. This returns a new string containing the escaped |
| | 422 | characters, or in case of exception in force_escape() |
| | 423 | (in particular MemoryError), a unicode string notifying |
| | 424 | about the exception. Never raises any exception derived from Exception. |
| | 425 | """ |
| | 426 | try: |
| | 427 | return force_escape(value) |
| | 428 | except Exception, e: |
| | 429 | return u"cannot render value due to exception in force_escape(): %s" % e.__class__.__name__ |
| | 430 | |
| 417 | 431 | @register.filter("linebreaks", is_safe=True, needs_autoescape=True) |
| 418 | 432 | @stringfilter |
| 419 | 433 | def linebreaks_filter(value, autoescape=None): |
diff --git a/django/views/debug.py b/django/views/debug.py
index 10c07e8..a738f4f 100644
|
a
|
b
|
from django.core.exceptions import ImproperlyConfigured
|
| 11 | 11 | from django.http import (HttpResponse, HttpResponseServerError, |
| 12 | 12 | HttpResponseNotFound, HttpRequest, build_request_repr) |
| 13 | 13 | from django.template import Template, Context, TemplateDoesNotExist |
| 14 | | from django.template.defaultfilters import force_escape, pprint |
| | 14 | from django.template.defaultfilters import nonraising_force_escape, pprint |
| 15 | 15 | from django.utils.html import escape |
| 16 | 16 | from django.utils.encoding import force_bytes, smart_text |
| 17 | 17 | from django.utils.module_loading import import_by_path |
| … |
… |
class ExceptionReporter(object):
|
| 246 | 246 | frames = self.get_traceback_frames() |
| 247 | 247 | for i, frame in enumerate(frames): |
| 248 | 248 | if 'vars' in frame: |
| 249 | | frame['vars'] = [(k, force_escape(pprint(v))) for k, v in frame['vars']] |
| | 249 | frame['vars'] = [(k, nonraising_force_escape(pprint(v))) |
| | 250 | for k, v in frame['vars']] |
| 250 | 251 | frames[i] = frame |
| 251 | 252 | |
| 252 | 253 | unicode_hint = '' |
| … |
… |
TECHNICAL_500_TEMPLATE = """
|
| 584 | 585 | <body> |
| 585 | 586 | <div id="summary"> |
| 586 | 587 | <h1>{% if exception_type %}{{ exception_type }}{% else %}Report{% endif %}{% if request %} at {{ request.path_info|escape }}{% endif %}</h1> |
| 587 | | <pre class="exception_value">{% if exception_value %}{{ exception_value|force_escape }}{% else %}No exception supplied{% endif %}</pre> |
| | 588 | <pre class="exception_value">{% if exception_value %}{{ exception_value|nonraising_force_escape }}{% else %}No exception supplied{% endif %}</pre> |
| 588 | 589 | <table class="meta"> |
| 589 | 590 | {% if request %} |
| 590 | 591 | <tr> |
| … |
… |
TECHNICAL_500_TEMPLATE = """
|
| 609 | 610 | {% if exception_type and exception_value %} |
| 610 | 611 | <tr> |
| 611 | 612 | <th>Exception Value:</th> |
| 612 | | <td><pre>{{ exception_value|force_escape }}</pre></td> |
| | 613 | <td><pre>{{ exception_value|nonraising_force_escape }}</pre></td> |
| 613 | 614 | </tr> |
| 614 | 615 | {% endif %} |
| 615 | 616 | {% if lastframe %} |
| … |
… |
TECHNICAL_500_TEMPLATE = """
|
| 639 | 640 | {% if unicode_hint %} |
| 640 | 641 | <div id="unicode-hint"> |
| 641 | 642 | <h2>Unicode error hint</h2> |
| 642 | | <p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint|force_escape }}</strong></p> |
| | 643 | <p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint|nonraising_force_escape }}</strong></p> |
| 643 | 644 | </div> |
| 644 | 645 | {% endif %} |
| 645 | 646 | {% if template_does_not_exist %} |
| … |
… |
TECHNICAL_500_TEMPLATE = """
|
| 717 | 718 | <tbody> |
| 718 | 719 | {% for var in frame.vars|dictsort:"0" %} |
| 719 | 720 | <tr> |
| 720 | | <td>{{ var.0|force_escape }}</td> |
| | 721 | <td>{{ var.0|nonraising_force_escape }}</td> |
| 721 | 722 | <td class="code"><pre>{{ var.1 }}</pre></td> |
| 722 | 723 | </tr> |
| 723 | 724 | {% endfor %} |
| … |
… |
Traceback:
|
| 770 | 771 | {% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %} |
| 771 | 772 | {% endfor %} |
| 772 | 773 | Exception Type: {{ exception_type|escape }}{% if request %} at {{ request.path_info|escape }}{% endif %} |
| 773 | | Exception Value: {{ exception_value|force_escape }} |
| | 774 | Exception Value: {{ exception_value|nonraising_force_escape }} |
| 774 | 775 | </textarea> |
| 775 | 776 | <br><br> |
| 776 | 777 | <input type="submit" value="Share this traceback on a public Web site"> |
diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py
index a84b419..708d9e3 100644
|
a
|
b
|
from django.test.utils import (override_settings, setup_test_template_loader,
|
| 16 | 16 | restore_template_loaders) |
| 17 | 17 | from django.utils.encoding import force_text, force_bytes |
| 18 | 18 | from django.views.debug import ExceptionReporter |
| | 19 | from django.template import defaultfilters |
| 19 | 20 | |
| 20 | 21 | from .. import BrokenException, except_args |
| 21 | 22 | from ..views import (sensitive_view, non_sensitive_view, paranoid_view, |
| … |
… |
class ExceptionReporterTests(TestCase):
|
| 105 | 106 | self.assertIn('<h2>Request information</h2>', html) |
| 106 | 107 | self.assertNotIn('<p>Request data not supplied</p>', html) |
| 107 | 108 | |
| | 109 | def test_request_and_exception_when_escaping_raises(self): |
| | 110 | "Complete rendering of exception report if escaping raises exception" |
| | 111 | # specifically if it raises MemoryError |
| | 112 | |
| | 113 | try: |
| | 114 | request = self.rf.get('/test_view/') |
| | 115 | raise ValueError("Can't find my keys") |
| | 116 | except ValueError: |
| | 117 | exc_type, exc_value, tb = sys.exc_info() |
| | 118 | |
| | 119 | def mock_escape_that_raises(*args, **kwargs): |
| | 120 | raise MemoryError() |
| | 121 | |
| | 122 | orig_escape = defaultfilters.escape |
| | 123 | try: |
| | 124 | defaultfilters.escape = mock_escape_that_raises |
| | 125 | reporter = ExceptionReporter(request, exc_type, exc_value, tb) |
| | 126 | html = reporter.get_traceback_html() |
| | 127 | self.assertIn('<h1>ValueError at /test_view/</h1>', html) |
| | 128 | self.assertIn('<pre class="exception_value">cannot render value due to exception in force_escape(): MemoryError</pre>', html) |
| | 129 | finally: |
| | 130 | defaultfilters.escape = orig_escape |
| | 131 | |
| 108 | 132 | def test_no_request(self): |
| 109 | 133 | "An exception report can be generated without request" |
| 110 | 134 | try: |