1
0
mirror of https://github.com/django/django.git synced 2025-03-06 15:32:33 +00:00

Fixed #36056 -- Made OutputWrapper a virtual subclass of TextIOBase.

This fixes the ignored exception in self._out.flush() from
django.core.management.base.OutputWrapper:
`ValueError: I/O operation on closed file.`
This commit is contained in:
Adam Johnson 2025-01-04 03:29:53 +00:00 committed by GitHub
parent a4d3f2535e
commit ec0e784f91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 2 deletions

View File

@ -142,7 +142,7 @@ class DjangoHelpFormatter(HelpFormatter):
super().add_arguments(self._reordered_actions(actions))
class OutputWrapper(TextIOBase):
class OutputWrapper:
"""
Wrapper around stdout/stderr
"""
@ -181,6 +181,9 @@ class OutputWrapper(TextIOBase):
self._out.write(style_func(msg))
TextIOBase.register(OutputWrapper)
class BaseCommand:
"""
The base class from which all management commands ultimately

View File

@ -1,7 +1,7 @@
import os
import sys
from argparse import ArgumentDefaultsHelpFormatter
from io import StringIO
from io import BytesIO, StringIO, TextIOWrapper
from pathlib import Path
from unittest import mock
@ -11,6 +11,7 @@ from django.apps import apps
from django.core import management
from django.core.checks import Tags
from django.core.management import BaseCommand, CommandError, find_commands
from django.core.management.base import OutputWrapper
from django.core.management.utils import (
find_command,
get_random_secret_key,
@ -28,6 +29,29 @@ from .management.commands import dance
from .utils import AssertFormatterFailureCaughtContext
class OutputWrapperTests(SimpleTestCase):
def test_unhandled_exceptions(self):
cases = [
StringIO("Hello world"),
TextIOWrapper(BytesIO(b"Hello world")),
]
for out in cases:
with self.subTest(out=out):
wrapper = OutputWrapper(out)
out.close()
unraisable_exceptions = []
def unraisablehook(unraisable):
unraisable_exceptions.append(unraisable)
sys.__unraisablehook__(unraisable)
with mock.patch.object(sys, "unraisablehook", unraisablehook):
del wrapper
self.assertEqual(unraisable_exceptions, [])
# A minimal set of apps to avoid system checks running on all apps.
@override_settings(
INSTALLED_APPS=[