mirror of
https://github.com/django/django.git
synced 2025-03-31 19:46:42 +00:00
Fixed #33701 -- Added fine-grained error locations to the technical 500 debug page.
This commit is contained in:
parent
9d726c7902
commit
85b52d22fd
1
AUTHORS
1
AUTHORS
@ -802,6 +802,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Rachel Tobin <rmtobin@me.com>
|
Rachel Tobin <rmtobin@me.com>
|
||||||
Rachel Willmer <http://www.willmer.com/kb/>
|
Rachel Willmer <http://www.willmer.com/kb/>
|
||||||
Radek Švarz <https://www.svarz.cz/translate/>
|
Radek Švarz <https://www.svarz.cz/translate/>
|
||||||
|
Rafael Giebisch <rafael@giebisch-mail.de>
|
||||||
Raffaele Salmaso <raffaele@salmaso.org>
|
Raffaele Salmaso <raffaele@salmaso.org>
|
||||||
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
||||||
Ramez Ashraf <ramezashraf@gmail.com>
|
Ramez Ashraf <ramezashraf@gmail.com>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import functools
|
import functools
|
||||||
|
import itertools
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
@ -15,7 +16,7 @@ from django.utils.datastructures import MultiValueDict
|
|||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.regex_helper import _lazy_re_compile
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
from django.utils.version import get_docs_version
|
from django.utils.version import PY311, get_docs_version
|
||||||
|
|
||||||
# Minimal Django templates engine to render the error templates
|
# Minimal Django templates engine to render the error templates
|
||||||
# regardless of the project's TEMPLATES setting. Templates are
|
# regardless of the project's TEMPLATES setting. Templates are
|
||||||
@ -546,6 +547,24 @@ class ExceptionReporter:
|
|||||||
pre_context = []
|
pre_context = []
|
||||||
context_line = "<source code not available>"
|
context_line = "<source code not available>"
|
||||||
post_context = []
|
post_context = []
|
||||||
|
|
||||||
|
colno = tb_area_colno = ""
|
||||||
|
if PY311:
|
||||||
|
_, _, start_column, end_column = next(
|
||||||
|
itertools.islice(
|
||||||
|
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if start_column and end_column:
|
||||||
|
underline = "^" * (end_column - start_column)
|
||||||
|
spaces = " " * (start_column + len(str(lineno + 1)) + 2)
|
||||||
|
colno = f"\n{spaces}{underline}"
|
||||||
|
tb_area_spaces = " " * (
|
||||||
|
4
|
||||||
|
+ start_column
|
||||||
|
- (len(context_line) - len(context_line.lstrip()))
|
||||||
|
)
|
||||||
|
tb_area_colno = f"\n{tb_area_spaces}{underline}"
|
||||||
yield {
|
yield {
|
||||||
"exc_cause": exc_cause,
|
"exc_cause": exc_cause,
|
||||||
"exc_cause_explicit": exc_cause_explicit,
|
"exc_cause_explicit": exc_cause_explicit,
|
||||||
@ -562,6 +581,8 @@ class ExceptionReporter:
|
|||||||
"context_line": context_line,
|
"context_line": context_line,
|
||||||
"post_context": post_context,
|
"post_context": post_context,
|
||||||
"pre_context_lineno": pre_context_lineno + 1,
|
"pre_context_lineno": pre_context_lineno + 1,
|
||||||
|
"colno": colno,
|
||||||
|
"tb_area_colno": tb_area_colno,
|
||||||
}
|
}
|
||||||
tb = tb.tb_next
|
tb = tb.tb_next
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@
|
|||||||
</ol>
|
</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ol start="{{ frame.lineno }}" class="context-line">
|
<ol start="{{ frame.lineno }}" class="context-line">
|
||||||
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line }}</pre>{% if not is_email %} <span>…</span>{% endif %}</li>
|
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line }}{{ frame.colno }}</pre>{% if not is_email %} <span>…</span>{% endif %}</li>
|
||||||
</ol>
|
</ol>
|
||||||
{% if frame.post_context and not is_email %}
|
{% if frame.post_context and not is_email %}
|
||||||
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">
|
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">
|
||||||
@ -327,7 +327,7 @@ The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of
|
|||||||
{% else %}
|
{% else %}
|
||||||
During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
|
During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
|
||||||
{% endif %}{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
{% endif %}{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
||||||
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}{% endfor %}
|
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}{% endfor %}
|
||||||
|
|
||||||
Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
||||||
Exception Value: {{ exception_value|force_escape }}{% if exception_notes %}{{ exception_notes }}{% endif %}
|
Exception Value: {{ exception_value|force_escape }}{% if exception_notes %}{{ exception_notes }}{% endif %}
|
||||||
|
@ -31,7 +31,7 @@ Traceback (most recent call last):
|
|||||||
{% for frame in frames %}{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
|
{% for frame in frames %}{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
|
||||||
{% if frame.exc_cause_explicit %}The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:{% else %}During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:{% endif %}
|
{% if frame.exc_cause_explicit %}The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:{% else %}During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:{% endif %}
|
||||||
{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
||||||
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}
|
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
{% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
||||||
{% if exception_value %}Exception Value: {{ exception_value }}{% endif %}{% if exception_notes %}{{ exception_notes }}{% endif %}{% endif %}{% endif %}
|
{% if exception_value %}Exception Value: {{ exception_value }}{% endif %}{% if exception_notes %}{{ exception_notes }}{% endif %}{% endif %}{% endif %}
|
||||||
|
@ -161,7 +161,8 @@ Email
|
|||||||
Error Reporting
|
Error Reporting
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* The debug page now shows :pep:`exception notes <678>` on Python 3.11+.
|
* The debug page now shows :pep:`exception notes <678>` and
|
||||||
|
:pep:`fine-grained error locations <657>` on Python 3.11+.
|
||||||
|
|
||||||
File Storage
|
File Storage
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
@ -766,6 +766,79 @@ class ExceptionReporterTests(SimpleTestCase):
|
|||||||
self.assertIn(implicit_exc.format("<p>Second exception</p>"), text)
|
self.assertIn(implicit_exc.format("<p>Second exception</p>"), text)
|
||||||
self.assertEqual(3, text.count("<p>Final exception</p>"))
|
self.assertEqual(3, text.count("<p>Final exception</p>"))
|
||||||
|
|
||||||
|
@skipIf(
|
||||||
|
sys._xoptions.get("no_debug_ranges", False)
|
||||||
|
or os.environ.get("PYTHONNODEBUGRANGES", False),
|
||||||
|
"Fine-grained error locations are disabled.",
|
||||||
|
)
|
||||||
|
@skipUnless(PY311, "Fine-grained error locations were added in Python 3.11.")
|
||||||
|
def test_highlight_error_position(self):
|
||||||
|
request = self.rf.get("/test_view/")
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise AttributeError("Top level")
|
||||||
|
except AttributeError as explicit:
|
||||||
|
try:
|
||||||
|
raise ValueError(mark_safe("<p>2nd exception</p>")) from explicit
|
||||||
|
except ValueError:
|
||||||
|
raise IndexError("Final exception")
|
||||||
|
except Exception:
|
||||||
|
exc_type, exc_value, tb = sys.exc_info()
|
||||||
|
|
||||||
|
reporter = ExceptionReporter(request, exc_type, exc_value, tb)
|
||||||
|
html = reporter.get_traceback_html()
|
||||||
|
self.assertIn(
|
||||||
|
"<pre> raise AttributeError("Top level")\n"
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"<pre> raise ValueError(mark_safe("
|
||||||
|
""<p>2nd exception</p>")) from explicit\n"
|
||||||
|
" "
|
||||||
|
"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"<pre> raise IndexError("Final exception")\n"
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
# Pastebin.
|
||||||
|
self.assertIn(
|
||||||
|
" raise AttributeError("Top level")\n"
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
" raise ValueError(mark_safe("
|
||||||
|
""<p>2nd exception</p>")) from explicit\n"
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
" raise IndexError("Final exception")\n"
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
# Text traceback.
|
||||||
|
text = reporter.get_traceback_text()
|
||||||
|
self.assertIn(
|
||||||
|
' raise AttributeError("Top level")\n'
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
' raise ValueError(mark_safe("<p>2nd exception</p>")) from explicit\n'
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
' raise IndexError("Final exception")\n'
|
||||||
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
|
||||||
def test_reporting_frames_without_source(self):
|
def test_reporting_frames_without_source(self):
|
||||||
try:
|
try:
|
||||||
source = "def funcName():\n raise Error('Whoops')\nfuncName()"
|
source = "def funcName():\n raise Error('Whoops')\nfuncName()"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user