Ticket #5516: user_commands.patch

File user_commands.patch, 8.1 KB (added by Todd O'Bryan, 12 years ago)
  • django/core/management/base.py

    Property changes on: .
    ___________________________________________________________________
    Name: svn:ignore
       - build
    dist
    *.egg-info
    MANIFEST
    
       + build
    dist
    *.egg-info
    MANIFEST
    .settings
    .project
    .pydevproject
    
    
     
    99class CommandError(Exception):
    1010    pass
    1111
     12def handle_default_options(options):
     13    """
     14    Include any default options that all commands should accept
     15    here so that ManagementUtility can handle them before searching
     16    for user commands.
     17    """
     18    if options.settings:
     19        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
     20    if options.pythonpath:
     21        sys.path.insert(0, options.pythonpath)
     22               
    1223class BaseCommand(object):
    1324    # Metadata about this command.
    1425    option_list = (
     
    5162    def print_help(self, prog_name, subcommand):
    5263        parser = self.create_parser(prog_name, subcommand)
    5364        parser.print_help()
    54 
     65       
    5566    def run_from_argv(self, argv):
    5667        parser = self.create_parser(argv[0], argv[1])
    5768        options, args = parser.parse_args(argv[2:])
    58         if options.settings:
    59             os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    60         if options.pythonpath:
    61             sys.path.insert(0, options.pythonpath)
     69        handle_default_options(options)
    6270        self.execute(*args, **options.__dict__)
    6371
    6472    def execute(self, *args, **options):
  • django/core/management/__init__.py

     
    2828    klass = load_command_class(name)
    2929    return klass.execute(*args, **options)
    3030
     31def find_commands(command_dir):
     32    return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
     33
     34class LaxOptionParser(OptionParser):
     35    def error(self, msg):
     36        pass
     37
    3138class ManagementUtility(object):
    3239    """
    3340    Encapsulates the logic of the django-admin.py and manage.py utilities.
     
    3946        self.argv = argv or sys.argv[:]
    4047        self.prog_name = os.path.basename(self.argv[0])
    4148        self.commands = self.default_commands()
    42 
     49       
    4350    def default_commands(self):
    4451        """
    4552        Returns a dictionary of instances of all available Command classes.
     
    5057        The dictionary is in the format {name: command_instance}.
    5158        """
    5259        command_dir = os.path.join(__path__[0], 'commands')
    53         names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
     60        names = find_commands(command_dir)
    5461        return dict([(name, load_command_class(name)) for name in names])
    5562
    5663    def main_help_text(self):
     
    6572        commands.sort()
    6673        for cmd in commands:
    6774            usage.append('  %s' % cmd)
     75        usage += self.user_commands_usage()
    6876        return '\n'.join(usage)
     77       
     78       
     79    def user_commands_usage(self):
     80        """
     81        Returns a list of lines that list each app and the commands it provides.
     82        """
     83        usage = []
     84        try:
     85            apps = django.conf.settings.INSTALLED_APPS
     86        except (KeyError, EnvironmentError):
     87            apps = ()
     88        for app in apps:
     89            mod = __import__(app)
     90            components = app.split('.')
     91            for comp in components[1:]:
     92                mod = getattr(mod, comp)
     93            command_dir = os.path.join(os.path.split(os.path.abspath(mod.__file__))[0],
     94                                       'management', 'commands')
     95            if os.path.exists(command_dir):
     96                commands = find_commands(command_dir)
     97                if commands:
     98                    commands = ['%s.%s' % (app, cmd) for cmd in commands]
     99                    commands.sort()
     100                    usage.append('')
     101                    usage.append('From app %s:' % app)
     102                    for cmd in commands:
     103                        usage.append('  %s' % cmd)
     104        return usage
     105   
    69106
     107    def fetch_user_command(self, subcommand):
     108        """
     109        Attempts to fetch a user defined command. Commands are written in the
     110        form  path.to.app.command, so that the last word should correspond to a
     111        module in the path.to.app.management.commands module and should
     112        contain a Command class that is a subclass of BaseCommand.
     113        """
     114        subcommand = subcommand.split('.')
     115        app_path = '.'.join(subcommand[:-1])
     116        from django.conf import settings
     117        command_name = subcommand[-1]
     118        return getattr(__import__('%s.management.commands.%s' % (app_path, command_name), {}, {}, ['Command']), 'Command')()
     119   
    70120    def fetch_command(self, subcommand):
    71121        """
    72122        Tries to fetch the given subcommand, printing a message with the
     
    76126        try:
    77127            return self.commands[subcommand]
    78128        except KeyError:
    79             sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (subcommand, self.prog_name))
    80             sys.exit(1)
     129            # subcommand not provided by django, may be provided by an app
     130            return self.fetch_user_command(subcommand)
    81131
    82132    def execute(self):
    83133        """
    84134        Given the command-line arguments, this figures out which subcommand is
    85135        being run, creates a parser appropriate to that command, and runs it.
    86136        """
     137        from django.core.management.base import BaseCommand, handle_default_options
     138        parser = LaxOptionParser(version=get_version(),
     139                                 option_list=BaseCommand.option_list)
     140        options, args = parser.parse_args(self.argv)
     141        handle_default_options(options)
    87142        try:
    88             subcommand = self.argv[1]
     143            subcommand = args[1]
    89144        except IndexError:
    90145            sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
    91146            sys.exit(1)
    92147
    93148        if subcommand == 'help':
    94             if len(self.argv) > 2:
    95                 self.fetch_command(self.argv[2]).print_help(self.prog_name, self.argv[2])
     149            if len(args) > 2:
     150                try:
     151                    self.fetch_command(args[2]).print_help(self.prog_name, self.argv[2])
     152                except (ValueError, ImportError):
     153                    sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (args[2], self.prog_name))
     154                    sys.exit(1)
    96155            else:
    97156                sys.stderr.write(self.main_help_text() + '\n')
    98157                sys.exit(1)
  • docs/django-admin.txt

     
    735735    * Press [TAB] to see all available options.
    736736    * Type ``sql``, then [TAB], to see all available options whose names start
    737737      with ``sql``.
     738
     739Customized actions
     740==================
     741
     742**New in Django development version**
     743
     744If you want to add an action of your own to ``manage.py``, you can.
     745Simply add a ``management/commands`` directory to your application.
     746Each python module in that directory will be discovered and registered as
     747a command of the form ``app_path.command`` that can be executed as an action
     748when you run ``manage.py``::
     749
     750    /fancy_blog
     751        __init__.py
     752        models.py
     753        /management
     754            __init__.py
     755            /commands
     756                __init__.py
     757                explode.py
     758        views.py
     759       
     760In this example, the ``fancy_blog.explode`` command will be made available to
     761any project that includes the ``fancy_blog`` application in
     762``settings.INSTALLED_APPS``.
     763
     764The ``explode.py`` module has only one requirement -- it must define a class
     765called ``Command`` that extends ``django.core.management.base.BaseCommand``.
     766
     767For more details on how to define your own commands, look at the code for the
     768existing ``django-admin.py`` commands, in ``/django/core/management/commands``.
Back to Top