Fixed #31675 -- Added warning to ExceptionReporter when exception chain has a cycle.

This commit is contained in:
Hasan Ramezani 2020-06-09 21:55:49 +02:00 committed by Mariusz Felisiak
parent 69a78a4a63
commit a59de6e89e
2 changed files with 18 additions and 3 deletions

View File

@ -2,6 +2,7 @@ import functools
import re import re
import sys import sys
import types import types
import warnings
from pathlib import Path from pathlib import Path
from django.conf import settings from django.conf import settings
@ -28,6 +29,10 @@ DEBUG_ENGINE = Engine(
CURRENT_DIR = Path(__file__).parent CURRENT_DIR = Path(__file__).parent
class ExceptionCycleWarning(UserWarning):
pass
class CallableSettingWrapper: class CallableSettingWrapper:
""" """
Object to wrap callable appearing in settings. Object to wrap callable appearing in settings.
@ -401,6 +406,11 @@ class ExceptionReporter:
exceptions.append(exc_value) exceptions.append(exc_value)
exc_value = explicit_or_implicit_cause(exc_value) exc_value = explicit_or_implicit_cause(exc_value)
if exc_value in exceptions: if exc_value in exceptions:
warnings.warn(
"Cycle in the exception chain detected: exception '%s' "
"encountered again." % exc_value,
ExceptionCycleWarning,
)
# Avoid infinite loop if there's a cyclic reference (#29393). # Avoid infinite loop if there's a cyclic reference (#29393).
break break

View File

@ -23,8 +23,8 @@ from django.utils.functional import SimpleLazyObject
from django.utils.regex_helper import _lazy_re_compile from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.views.debug import ( from django.views.debug import (
CallableSettingWrapper, ExceptionReporter, Path as DebugPath, CallableSettingWrapper, ExceptionCycleWarning, ExceptionReporter,
SafeExceptionReporterFilter, default_urlconf, Path as DebugPath, SafeExceptionReporterFilter, default_urlconf,
get_default_exception_reporter_filter, technical_404_response, get_default_exception_reporter_filter, technical_404_response,
technical_500_response, technical_500_response,
) )
@ -518,7 +518,12 @@ class ExceptionReporterTests(SimpleTestCase):
tb_frames = None tb_frames = None
tb_generator = threading.Thread(target=generate_traceback_frames, daemon=True) tb_generator = threading.Thread(target=generate_traceback_frames, daemon=True)
tb_generator.start() msg = (
"Cycle in the exception chain detected: exception 'inner' "
"encountered again."
)
with self.assertWarnsMessage(ExceptionCycleWarning, msg):
tb_generator.start()
tb_generator.join(timeout=5) tb_generator.join(timeout=5)
if tb_generator.is_alive(): if tb_generator.is_alive():
# tb_generator is a daemon that runs until the main thread/process # tb_generator is a daemon that runs until the main thread/process