Browse Source

Fixed #25264 -- Allowed suppressing base command options in --help output.

This also suppresses -verbosity and --trackback options in the
runserver's help.
Jan Szoja 3 years ago
parent
commit
b667ac24ea

+ 31 - 12
django/core/management/base.py

@@ -2,6 +2,7 @@
 Base classes for writing management commands (named commands which can
 be executed through ``django-admin`` or ``manage.py``).
 """
+import argparse
 import os
 import sys
 import warnings
@@ -239,6 +240,7 @@ class BaseCommand:
     base_stealth_options = ('stderr', 'stdout')
     # Command-specific options not defined by the argument parser.
     stealth_options = ()
+    suppressed_base_arguments = set()
 
     def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):
         self.stdout = OutputWrapper(stdout or sys.stdout)
@@ -285,31 +287,37 @@ class BaseCommand:
             called_from_command_line=getattr(self, '_called_from_command_line', None),
             **kwargs
         )
-        parser.add_argument('--version', action='version', version=self.get_version())
-        parser.add_argument(
-            '-v', '--verbosity', default=1,
+        self.add_base_argument(
+            parser, '--version', action='version', version=self.get_version(),
+            help="Show program's version number and exit.",
+        )
+        self.add_base_argument(
+            parser, '-v', '--verbosity', default=1,
             type=int, choices=[0, 1, 2, 3],
             help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output',
         )
-        parser.add_argument(
-            '--settings',
+        self.add_base_argument(
+            parser, '--settings',
             help=(
                 'The Python path to a settings module, e.g. '
                 '"myproject.settings.main". If this isn\'t provided, the '
                 'DJANGO_SETTINGS_MODULE environment variable will be used.'
             ),
         )
-        parser.add_argument(
-            '--pythonpath',
+        self.add_base_argument(
+            parser, '--pythonpath',
             help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".',
         )
-        parser.add_argument('--traceback', action='store_true', help='Raise on CommandError exceptions')
-        parser.add_argument(
-            '--no-color', action='store_true',
+        self.add_base_argument(
+            parser, '--traceback', action='store_true',
+            help='Raise on CommandError exceptions.',
+        )
+        self.add_base_argument(
+            parser, '--no-color', action='store_true',
             help="Don't colorize the command output.",
         )
-        parser.add_argument(
-            '--force-color', action='store_true',
+        self.add_base_argument(
+            parser, '--force-color', action='store_true',
             help='Force colorization of the command output.',
         )
         if self.requires_system_checks:
@@ -326,6 +334,17 @@ class BaseCommand:
         """
         pass
 
+    def add_base_argument(self, parser, *args, **kwargs):
+        """
+        Call the parser's add_argument() method, suppressing the help text
+        according to BaseCommand.suppressed_base_arguments.
+        """
+        for arg in args:
+            if arg in self.suppressed_base_arguments:
+                kwargs['help'] = argparse.SUPPRESS
+                break
+        parser.add_argument(*args, **kwargs)
+
     def print_help(self, prog_name, subcommand):
         """
         Print the help message for this command, derived from

+ 1 - 0
django/core/management/commands/runserver.py

@@ -27,6 +27,7 @@ class Command(BaseCommand):
     # Validation is called explicitly each time the server is reloaded.
     requires_system_checks = []
     stealth_options = ('shutdown_message',)
+    suppressed_base_arguments = {'--verbosity', '--traceback'}
 
     default_addr = '127.0.0.1'
     default_addr_ipv6 = '::1'

+ 8 - 0
docs/howto/custom-management-commands.txt

@@ -242,6 +242,14 @@ All attributes can be set in your derived class and can be used in
     If you pass the :option:`--no-color` option when running your command, all
     ``self.style()`` calls will return the original string uncolored.
 
+.. attribute:: BaseCommand.suppressed_base_arguments
+
+    .. versionadded:: 4.0
+
+    The default command options to suppress in the help output. This should be
+    a set of option names (e.g. ``'--verbosity'``). The default values for the
+    suppressed options are still passed.
+
 Methods
 -------
 

+ 4 - 0
docs/releases/4.0.txt

@@ -274,6 +274,10 @@ Management Commands
   As a consequence, ``readline`` is no longer loaded if running in *isolated*
   mode.
 
+* The new :attr:`BaseCommand.suppressed_base_arguments
+  <django.core.management.BaseCommand.suppressed_base_arguments>` attribute
+  allows suppressing unsupported default command options in the help output.
+
 Migrations
 ~~~~~~~~~~
 

+ 24 - 0
tests/admin_scripts/management/commands/suppress_base_options_command.py

@@ -0,0 +1,24 @@
+from django.core.management import BaseCommand
+
+
+class Command(BaseCommand):
+
+    help = 'Test suppress base options command.'
+    requires_system_checks = []
+    suppressed_base_arguments = {
+        '-v',
+        '--traceback',
+        '--settings',
+        '--pythonpath',
+        '--no-color',
+        '--force-color',
+        '--version',
+        'file',
+    }
+
+    def add_arguments(self, parser):
+        super().add_arguments(parser)
+        self.add_base_argument(parser, 'file', nargs='?', help='input file')
+
+    def handle(self, *labels, **options):
+        print('EXECUTE:SuppressBaseOptionsCommand options=%s' % sorted(options.items()))

+ 37 - 0
tests/admin_scripts/tests.py

@@ -1381,6 +1381,15 @@ class ManageRunserverEmptyAllowedHosts(AdminScriptTestCase):
         self.assertOutput(err, 'CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.')
 
 
+class ManageRunserverHelpOutput(AdminScriptTestCase):
+    def test_suppressed_options(self):
+        """runserver doesn't support --verbosity and --trackback options."""
+        out, err = self.run_manage(['runserver', '--help'])
+        self.assertNotInOutput(out, '--verbosity')
+        self.assertNotInOutput(out, '--trackback')
+        self.assertOutput(out, '--settings')
+
+
 class ManageTestserver(SimpleTestCase):
 
     @mock.patch.object(TestserverCommand, 'handle', return_value='')
@@ -1847,6 +1856,34 @@ class CommandTypes(AdminScriptTestCase):
             "('settings', None), ('traceback', False), ('verbosity', 1)]"
         )
 
+    def test_suppress_base_options_command_help(self):
+        args = ['suppress_base_options_command', '--help']
+        out, err = self.run_manage(args)
+        self.assertNoOutput(err)
+        self.assertOutput(out, 'Test suppress base options command.')
+        self.assertNotInOutput(out, 'input file')
+        self.assertOutput(out, '-h, --help')
+        self.assertNotInOutput(out, '--version')
+        self.assertNotInOutput(out, '--verbosity')
+        self.assertNotInOutput(out, '-v {0,1,2,3}')
+        self.assertNotInOutput(out, '--settings')
+        self.assertNotInOutput(out, '--pythonpath')
+        self.assertNotInOutput(out, '--traceback')
+        self.assertNotInOutput(out, '--no-color')
+        self.assertNotInOutput(out, '--force-color')
+
+    def test_suppress_base_options_command_defaults(self):
+        args = ['suppress_base_options_command']
+        out, err = self.run_manage(args)
+        self.assertNoOutput(err)
+        self.assertOutput(
+            out,
+            "EXECUTE:SuppressBaseOptionsCommand options=[('file', None), "
+            "('force_color', False), ('no_color', False), "
+            "('pythonpath', None), ('settings', None), "
+            "('traceback', False), ('verbosity', 1)]"
+        )
+
 
 class Discovery(SimpleTestCase):