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

View File

@ -1,7 +1,7 @@
import os import os
import sys import sys
from argparse import ArgumentDefaultsHelpFormatter from argparse import ArgumentDefaultsHelpFormatter
from io import StringIO from io import BytesIO, StringIO, TextIOWrapper
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
@ -11,6 +11,7 @@ from django.apps import apps
from django.core import management from django.core import management
from django.core.checks import Tags from django.core.checks import Tags
from django.core.management import BaseCommand, CommandError, find_commands from django.core.management import BaseCommand, CommandError, find_commands
from django.core.management.base import OutputWrapper
from django.core.management.utils import ( from django.core.management.utils import (
find_command, find_command,
get_random_secret_key, get_random_secret_key,
@ -28,6 +29,29 @@ from .management.commands import dance
from .utils import AssertFormatterFailureCaughtContext 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. # A minimal set of apps to avoid system checks running on all apps.
@override_settings( @override_settings(
INSTALLED_APPS=[ INSTALLED_APPS=[