Fixed #11745 -- Grouped commands by application in the output of `manage.py help`. Made 'version' consistent with 'help' while I was in the area, and added tests. Thanks Jannis for the feedback and review.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17462 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Aymeric Augustin 2012-02-07 18:46:29 +00:00
parent 09ad6d1b88
commit 175e6d77df
4 changed files with 88 additions and 28 deletions

View File

@ -1,3 +1,4 @@
import collections
import os import os
import sys import sys
from optparse import OptionParser, NO_DEFAULT from optparse import OptionParser, NO_DEFAULT
@ -5,6 +6,7 @@ import imp
import warnings import warnings
from django.core.management.base import BaseCommand, CommandError, handle_default_options from django.core.management.base import BaseCommand, CommandError, handle_default_options
from django.core.management.color import color_style
from django.utils.importlib import import_module from django.utils.importlib import import_module
# For backwards compatibility: get_version() used to be in this module. # For backwards compatibility: get_version() used to be in this module.
@ -209,16 +211,32 @@ class ManagementUtility(object):
self.argv = argv or sys.argv[:] self.argv = argv or sys.argv[:]
self.prog_name = os.path.basename(self.argv[0]) self.prog_name = os.path.basename(self.argv[0])
def main_help_text(self): def main_help_text(self, commands_only=False):
""" """
Returns the script's main help text, as a string. Returns the script's main help text, as a string.
""" """
usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,''] if commands_only:
usage.append('Available subcommands:') usage = sorted(get_commands().keys())
commands = get_commands().keys() else:
commands.sort() usage = [
for cmd in commands: "",
usage.append(' %s' % cmd) "Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,
"",
"Available subcommands:",
]
commands_dict = collections.defaultdict(lambda: [])
for name, app in get_commands().iteritems():
if app == 'django.core':
app = 'django'
else:
app = app.rpartition('.')[-1]
commands_dict[app].append(name)
style = color_style()
for app in sorted(commands_dict.keys()):
usage.append("")
usage.append(style.NOTICE("[%s]" % app))
for name in sorted(commands_dict[app]):
usage.append(" %s" % name)
return '\n'.join(usage) return '\n'.join(usage)
def fetch_command(self, subcommand): def fetch_command(self, subcommand):
@ -340,12 +358,15 @@ class ManagementUtility(object):
subcommand = 'help' # Display help if no arguments were given. subcommand = 'help' # Display help if no arguments were given.
if subcommand == 'help': if subcommand == 'help':
if len(args) > 2: if len(args) <= 2:
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
else:
parser.print_lax_help() parser.print_lax_help()
sys.stdout.write(self.main_help_text() + '\n') sys.stdout.write(self.main_help_text() + '\n')
sys.exit(1) elif args[2] == '--commands':
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
else:
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
elif subcommand == 'version':
sys.stdout.write(parser.get_version() + '\n')
# Special-cases: We want 'django-admin.py --version' and # Special-cases: We want 'django-admin.py --version' and
# 'django-admin.py --help' to work, for backwards compatibility. # 'django-admin.py --help' to work, for backwards compatibility.
elif self.argv[1:] == ['--version']: elif self.argv[1:] == ['--version']:

View File

@ -47,11 +47,16 @@ for the given command.
Getting runtime help Getting runtime help
-------------------- --------------------
.. django-admin-option:: --help .. django-admin:: help
Run ``django-admin.py help`` to display a list of all available commands. Run ``django-admin.py help`` to display usage information and a list of the
Run ``django-admin.py help <command>`` to display a description of the commands provided by each application.
given command and a list of its available options.
Run ``django-admin.py help --commands`` to display a list of all available
commands.
Run ``django-admin.py help <command>`` to display a description of the given
command and a list of its available options.
App names App names
--------- ---------
@ -63,9 +68,9 @@ contains the string ``'mysite.blog'``, the app name is ``blog``.
Determining the version Determining the version
----------------------- -----------------------
.. django-admin-option:: --version .. django-admin:: version
Run ``django-admin.py --version`` to display the current Django version. Run ``django-admin.py version`` to display the current Django version.
Examples of output:: Examples of output::

View File

@ -989,6 +989,14 @@ after tests' execution, then you can restore the previous behavior by
subclassing ``DjangoTestRunner`` and overriding its ``teardown_databases()`` subclassing ``DjangoTestRunner`` and overriding its ``teardown_databases()``
method. method.
Output of :djadmin:`manage.py help <help>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:djadmin:`manage.py help <help>` now groups available commands by application.
If you depended on its output, for instance if you parsed it, you must update
your scripts. To obtain the list of all available management commands in a
script, you can use :djadmin:`manage.py help --commands <help>` instead.
Features deprecated in 1.4 Features deprecated in 1.4
========================== ==========================

View File

@ -173,6 +173,10 @@ class AdminScriptTestCase(unittest.TestCase):
"Utility assertion: assert that the given message exists in the output" "Utility assertion: assert that the given message exists in the output"
self.assertTrue(msg in stream, "'%s' does not match actual output text '%s'" % (msg, stream)) self.assertTrue(msg in stream, "'%s' does not match actual output text '%s'" % (msg, stream))
def assertNotInOutput(self, stream, msg):
"Utility assertion: assert that the given message doesn't exist in the output"
self.assertFalse(msg in stream, "'%s' matches actual output text '%s'" % (msg, stream))
########################################################################## ##########################################################################
# DJANGO ADMIN TESTS # DJANGO ADMIN TESTS
# This first series of test classes checks the environment processing # This first series of test classes checks the environment processing
@ -1173,25 +1177,47 @@ class CommandTypes(AdminScriptTestCase):
self.remove_settings('settings.py') self.remove_settings('settings.py')
def test_version(self): def test_version(self):
"--version is handled as a special case" "version is handled as a special case"
args = ['--version'] args = ['version']
out, err = self.run_manage(args) out, err = self.run_manage(args)
self.assertNoOutput(err) self.assertNoOutput(err)
self.assertOutput(out, get_version()) self.assertOutput(out, get_version())
def test_help(self): def test_version_alternative(self):
"--help is handled as a special case" "--version is equivalent to version"
args = ['--help'] args1, args2 = ['version'], ['--version']
out, err = self.run_manage(args) self.assertEqual(self.run_manage(args1), self.run_manage(args2))
self.assertOutput(out, "Usage: manage.py subcommand [options] [args]")
self.assertOutput(out, "Type 'manage.py help <subcommand>' for help on a specific subcommand.")
def test_short_help(self): def test_help(self):
"-h is handled as a short form of --help" "help is handled as a special case"
args = ['-h'] args = ['help']
out, err = self.run_manage(args) out, err = self.run_manage(args)
self.assertOutput(out, "Usage: manage.py subcommand [options] [args]") self.assertOutput(out, "Usage: manage.py subcommand [options] [args]")
self.assertOutput(out, "Type 'manage.py help <subcommand>' for help on a specific subcommand.") self.assertOutput(out, "Type 'manage.py help <subcommand>' for help on a specific subcommand.")
self.assertOutput(out, '[django]')
self.assertOutput(out, 'startapp')
self.assertOutput(out, 'startproject')
def test_help_commands(self):
"help --commands shows the list of all available commands"
args = ['help', '--commands']
out, err = self.run_manage(args)
self.assertNotInOutput(out, 'Usage:')
self.assertNotInOutput(out, 'Options:')
self.assertNotInOutput(out, '[django]')
self.assertOutput(out, 'startapp')
self.assertOutput(out, 'startproject')
self.assertNotInOutput(out, '\n\n')
def test_help_alternative(self):
"--help is equivalent to help"
args1, args2 = ['help'], ['--help']
self.assertEqual(self.run_manage(args1), self.run_manage(args2))
def test_help_short_altert(self):
"-h is handled as a short form of --help"
args1, args2 = ['--help'], ['-h']
self.assertEqual(self.run_manage(args1), self.run_manage(args2))
def test_specific_help(self): def test_specific_help(self):
"--help can be used on a specific command" "--help can be used on a specific command"