Commit 2d8b0315 authored by Claude Paroz's avatar Claude Paroz
Browse files

[1.5.x] Fixed #19665 -- Ensured proper stderr output for Command.run_from_argv

Thanks Stefan Koegl for the report and Simon Charette for the review.
Backport of b9c8bbf3 from master.
parent 42fcfcaa
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -221,9 +221,12 @@ class BaseCommand(object):
        try:
            self.execute(*args, **options.__dict__)
        except Exception as e:
            # self.stderr is not guaranteed to be set here
            stderr = getattr(self, 'stderr', OutputWrapper(sys.stderr, self.style.ERROR))
            if options.traceback:
                self.stderr.write(traceback.format_exc())
            self.stderr.write('%s: %s' % (e.__class__.__name__, e))
                stderr.write(traceback.format_exc())
            else:
                stderr.write('%s: %s' % (e.__class__.__name__, e))
            sys.exit(1)

    def execute(self, *args, **options):
+27 −0
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@ import codecs

from django import conf, bin, get_version
from django.conf import settings
from django.core.management import BaseCommand
from django.db import connection
from django.test.simple import DjangoTestSuiteRunner
from django.utils import unittest
from django.utils.encoding import force_str, force_text
from django.utils._os import upath
from django.utils.six import StringIO
from django.test import LiveServerTestCase

test_dir = os.path.dirname(os.path.dirname(upath(__file__)))
@@ -1279,6 +1281,31 @@ class CommandTypes(AdminScriptTestCase):
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")

    def test_base_run_from_argv(self):
        """
        Test run_from_argv properly terminates even with custom execute() (#19665)
        Also test proper traceback display.
        """
        command = BaseCommand()
        command.execute = lambda args: args  # This will trigger TypeError

        old_stderr = sys.stderr
        sys.stderr = err = StringIO()
        try:
            with self.assertRaises(SystemExit):
                command.run_from_argv(['', ''])
            err_message = err.getvalue()
            self.assertNotIn("Traceback", err_message)
            self.assertIn("TypeError", err_message)

            with self.assertRaises(SystemExit):
                command.run_from_argv(['', '', '--traceback'])
            err_message = err.getvalue()
            self.assertIn("Traceback (most recent call last)", err_message)
            self.assertIn("TypeError", err_message)
        finally:
            sys.stderr = old_stderr

    def test_noargs(self):
        "NoArg Commands can be executed"
        args = ['noargs_command']