Skip to content

Commit

Permalink
Fixed #11745 -- Grouped commands by application in the output of `man…
Browse files Browse the repository at this point in the history
…age.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
  • Loading branch information
aaugustin committed Feb 7, 2012
1 parent 09ad6d1 commit 175e6d7
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 26 deletions.
43 changes: 32 additions & 11 deletions django/core/management/__init__.py
Original file line number Original file line Diff line number Diff line change
@@ -1,10 +1,12 @@
import collections
import os import os
import sys import sys
from optparse import OptionParser, NO_DEFAULT from optparse import OptionParser, NO_DEFAULT
import imp 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.
Expand Down Expand Up @@ -209,16 +211,32 @@ def __init__(self, argv=None):
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):
Expand Down Expand Up @@ -340,12 +358,15 @@ def execute(self):
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']:
Expand Down
17 changes: 11 additions & 6 deletions docs/ref/django-admin.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -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
--------- ---------
Expand All @@ -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::


Expand Down
8 changes: 8 additions & 0 deletions docs/releases/1.4.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -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
========================== ==========================


Expand Down
44 changes: 35 additions & 9 deletions tests/regressiontests/admin_scripts/tests.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ def assertOutput(self, stream, msg):
"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
Expand Down Expand Up @@ -1173,25 +1177,47 @@ def tearDown(self):
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_version_alternative(self):
"--version is equivalent to version"
args1, args2 = ['version'], ['--version']
self.assertEqual(self.run_manage(args1), self.run_manage(args2))

def test_help(self): def test_help(self):
"--help is handled as a special case" "help is handled as a special case"
args = ['--help'] 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_short_help(self): def test_help_commands(self):
"-h is handled as a short form of --help" "help --commands shows the list of all available commands"
args = ['-h'] args = ['help', '--commands']
out, err = self.run_manage(args) out, err = self.run_manage(args)
self.assertOutput(out, "Usage: manage.py subcommand [options] [args]") self.assertNotInOutput(out, 'Usage:')
self.assertOutput(out, "Type 'manage.py help <subcommand>' for help on a specific subcommand.") 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"
Expand Down

0 comments on commit 175e6d7

Please sign in to comment.