Django

Code

Ticket #5516: user_commands.patch

File user_commands.patch, 8.1 kB (added by toddobryan, 1 year ago)
  • django/core/management/base.py

    old new  
    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

    old new  
    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

    old new  
    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``.