diff --git a/django/core/management/base.py b/django/core/management/base.py index abc6f79a157..631c761c004 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -6,6 +6,7 @@ import argparse import os import sys from argparse import ArgumentParser, HelpFormatter +from functools import partial from io import TextIOBase import django @@ -71,6 +72,15 @@ class CommandParser(ArgumentParser): else: raise CommandError("Error: %s" % message) + def add_subparsers(self, **kwargs): + parser_class = kwargs.get("parser_class", type(self)) + if issubclass(parser_class, CommandParser): + kwargs["parser_class"] = partial( + parser_class, + called_from_command_line=self.called_from_command_line, + ) + return super().add_subparsers(**kwargs) + def handle_default_options(options): """ diff --git a/tests/user_commands/management/commands/subparser_vanilla.py b/tests/user_commands/management/commands/subparser_vanilla.py new file mode 100644 index 00000000000..1605f21cadc --- /dev/null +++ b/tests/user_commands/management/commands/subparser_vanilla.py @@ -0,0 +1,13 @@ +import argparse + +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + def add_arguments(self, parser): + subparsers = parser.add_subparsers(parser_class=argparse.ArgumentParser) + parser_foo = subparsers.add_parser("foo") + parser_foo.add_argument("bar", type=int) + + def handle(self, *args, **options): + pass diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py index e40632385cb..408108b5525 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -468,6 +468,30 @@ class CommandRunTests(AdminScriptTestCase): self.assertNoOutput(err) self.assertEqual(out.strip(), "Set foo") + def test_subparser_error_formatting(self): + self.write_settings("settings.py", apps=["user_commands"]) + out, err = self.run_manage(["subparser", "foo", "twelve"]) + self.maxDiff = None + self.assertNoOutput(out) + err_lines = err.splitlines() + self.assertEqual(len(err_lines), 2) + self.assertEqual( + err_lines[1], + "manage.py subparser foo: error: argument bar: invalid int value: 'twelve'", + ) + + def test_subparser_non_django_error_formatting(self): + self.write_settings("settings.py", apps=["user_commands"]) + out, err = self.run_manage(["subparser_vanilla", "foo", "seven"]) + self.assertNoOutput(out) + err_lines = err.splitlines() + self.assertEqual(len(err_lines), 2) + self.assertEqual( + err_lines[1], + "manage.py subparser_vanilla foo: error: argument bar: invalid int value: " + "'seven'", + ) + class UtilsTests(SimpleTestCase): def test_no_existent_external_program(self):