Code

Ticket #5369: command.patch

File command.patch, 21.0 KB (added by toddobryan@…, 7 years ago)

patch implementing the refactoring

  • home/tobryan1/workspace/django/django/core/management/__init__.py

    Property changes on: /home/tobryan1/workspace/django
    ___________________________________________________________________
    Name: svn:ignore
       - build
    dist
    *.egg-info
    MANIFEST
    
       + build
    dist
    *.egg-info
    MANIFEST
    .settings
    .project
    .pydevproject
    
    
     
    5252        names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] 
    5353        return dict([(name, load_command_class(name)) for name in names]) 
    5454 
    55     def usage(self): 
    56         """ 
    57         Returns a usage string, for use with optparse. 
    58  
    59         The string doesn't include the options (e.g., "--verbose"), because 
    60         optparse puts those in automatically. 
    61         """ 
    62         usage = ["%prog command [options]\nactions:"] 
     55    def print_help(self, argv): 
     56        prog_name = os.path.basename(argv[0]) 
     57        usage = ['%s <subcommand> [options] [args]' % prog_name] 
     58        usage.append('Django command line tool, version %s' % django.get_version()) 
     59        usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % prog_name) 
     60        usage.append('Available subcommands:') 
    6361        commands = self.commands.items() 
    6462        commands.sort() 
    6563        for name, cmd in commands: 
    66             usage.append('  %s %s' % (name, cmd.args)) 
    67             usage.extend(textwrap.wrap(cmd.help, initial_indent='    ', subsequent_indent='    ')) 
    68             usage.append('') 
    69         return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space. 
     64            usage.append('  %s' % name) 
     65        print '\n'.join(usage) 
     66         
     67    def fetch_command(self, subcommand, command_name): 
     68        """ 
     69        Tries to fetch the given subcommand, printing a message with the 
     70        appropriate command called from the command line (usually 
     71        django-admin.py or manage.py) if it can't be found 
     72        """ 
     73        try: 
     74            return self.commands[subcommand] 
     75        except KeyError: 
     76            sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (subcommand, command_name)) 
     77            sys.exit(1) 
    7078 
    7179    def execute(self, argv=None): 
    7280        """ 
    73         Parses the given argv from the command line, determines which command 
    74         to run and runs the command. 
     81        Figures out which command is being run (the first arg), creates a parser 
     82        appropriate to that command, and runs it 
    7583        """ 
    7684        if argv is None: 
    7785            argv = sys.argv 
    78  
    79         # Create the parser object and parse the command-line args. 
    80         # TODO: Ideally each Command class would register its own options for 
    81         # add_option(), but we'd need to figure out how to allow for multiple 
    82         # Commands using the same options. The optparse library gets in the way 
    83         # by checking for conflicts: 
    84         # http://docs.python.org/lib/optparse-conflicts-between-options.html 
    85         parser = OptionParser(usage=self.usage(), version=get_version()) 
    86         parser.add_option('--settings', 
    87             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.') 
    88         parser.add_option('--pythonpath', 
    89             help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".') 
    90         parser.add_option('--plain', action='store_true', dest='plain', 
    91             help='When using "shell": Tells Django to use plain Python, not IPython.') 
    92         parser.add_option('--noinput', action='store_false', dest='interactive', default=True, 
    93             help='Tells Django to NOT prompt the user for input of any kind.') 
    94         parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True, 
    95             help='When using "runserver": Tells Django to NOT use the auto-reloader.') 
    96         parser.add_option('--format', default='json', dest='format', 
    97             help='Specifies the output serialization format for fixtures') 
    98         parser.add_option('--indent', default=None, dest='indent', 
    99             type='int', help='Specifies the indent level to use when pretty-printing output') 
    100         parser.add_option('--verbosity', action='store', dest='verbosity', default='1', 
    101             type='choice', choices=['0', '1', '2'], 
    102             help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') 
    103         parser.add_option('--adminmedia', dest='admin_media_path', default='', 
    104             help='When using "runserver": Specifies the directory from which to serve admin media.') 
    105         options, args = parser.parse_args(argv[1:]) 
    106  
    107         # If the 'settings' or 'pythonpath' options were submitted, activate those. 
    108         if options.settings: 
    109             os.environ['DJANGO_SETTINGS_MODULE'] = options.settings 
    110         if options.pythonpath: 
    111             sys.path.insert(0, options.pythonpath) 
    112  
    113         # Run the appropriate command. 
    11486        try: 
    115             command_name = args[0] 
     87            command_name = argv[1] 
    11688        except IndexError: 
    117             sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0])) 
     89            sys.stderr.write("Type '%s help' for usage.\n" % os.path.basename(argv[0])) 
    11890            sys.exit(1) 
    119         try: 
    120             command = self.commands[command_name] 
    121         except KeyError: 
    122             sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0]))) 
    123             sys.exit(1) 
    124         command.execute(*args[1:], **options.__dict__) 
     91        if command_name == 'help': 
     92            if len(argv) > 2: 
     93                self.fetch_command(argv[2], argv[0]).print_help(argv[2:]) 
     94            else: 
     95                self.print_help(argv) 
     96        else: 
     97            self.fetch_command(command_name, argv[0]).run(argv[1:]) 
     98         
    12599 
    126100class ProjectManagementUtility(ManagementUtility): 
    127101    """ 
  • home/tobryan1/workspace/django/django/core/management/base.py

     
     1import django 
    12from django.core.exceptions import ImproperlyConfigured 
    23from django.core.management.color import color_style 
     4 
     5import itertools 
     6from optparse import make_option, OptionParser 
    37import sys 
    48import os 
     9from traceback import print_exc 
    510 
    611class CommandError(Exception): 
    712    pass 
     
    813 
    914class BaseCommand(object): 
    1015    # Metadata about this command. 
     16    option_list = ( 
     17        make_option('--settings',  
     18                    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.'), 
     19        make_option('--pythonpath', 
     20                    help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'), 
     21    ) 
    1122    help = '' 
    1223    args = '' 
    1324 
     
    1829 
    1930    def __init__(self): 
    2031        self.style = color_style() 
     32     
     33    def get_version(self): 
     34        """ 
     35        returns the Django version, which should be correct for all built-in 
     36        Django commands. User-supplied commands should override this method. 
     37        """ 
     38        return django.get_version() 
     39     
     40    def usage(self): 
     41        usage = '%prog [options] ' + self.args 
     42        if self.help: 
     43            return '%s\n%s' % (self.help, usage) 
     44        else: 
     45            return usage 
     46     
     47    @classmethod 
     48    def get_option_list(cls): 
     49        """ 
     50        Builds up a list of parameters to the OptionParser.add_option command 
     51        by recursively adding option parameters up the class hierarchy. Requires 
     52        BaseCommand subclasses to only extend a single superclass. 
     53        """ 
     54        if cls is BaseCommand: 
     55            option_list = BaseCommand.option_list 
     56        else: 
     57            option_list = cls.__base__.get_option_list() 
     58        if cls.option_list is option_list: 
     59            return option_list 
     60        else: 
     61            # this class adds new option_params not in superclasses 
     62            # get a list of all options so far 
     63            opt_strs = list(itertools.chain(*[op._short_opts + op._long_opts for op in option_list])) 
     64            # check for duplicates 
     65            for option in cls.option_list: 
     66                strs = option._short_opts + option._long_opts 
     67                for opt_str in strs: 
     68                    if opt_str in opt_strs: 
     69                        raise CommandError("%s attempts to redefine the option '%s', which appears in a superclass." % 
     70                                       (cls, opt_str)) 
     71            return option_list + cls.option_list            
     72         
     73    def create_parser(self, prog_name): 
     74        return OptionParser(prog=prog_name, 
     75                            usage=self.usage(),  
     76                            version=self.get_version(), 
     77                            option_list=self.__class__.get_option_list()) 
     78         
     79    def print_help(self, args): 
     80        parser = self.create_parser(args[0]) 
     81        parser.print_help() 
     82             
     83    def run(self, args): 
     84        parser = self.create_parser(args[0]) 
     85        (options, args) = parser.parse_args(args[1:]) 
     86        if options.settings: 
     87            os.environ['DJANGO_SETTINGS_MODULE'] = options.settings 
     88        if options.pythonpath: 
     89            sys.path.insert(0, options.pythonpath) 
     90        try: 
     91            self.execute(*args, **options.__dict__) 
     92        except Exception, e: 
     93            print_exc() 
     94            parser.print_usage() 
    2195 
    2296    def execute(self, *args, **options): 
    2397        # Switch to English, because django-admin.py creates database content 
     
    119193 
    120194    def handle_noargs(self, **options): 
    121195        raise NotImplementedError() 
    122  
     196     
    123197def copy_helper(style, app_or_project, name, directory, other_name=''): 
    124198    import django 
    125199    import re 
  • home/tobryan1/workspace/django/django/core/management/commands/dumpdata.py

     
    11from django.core.management.base import BaseCommand, CommandError 
    22 
     3from optparse import make_option 
     4 
    35class Command(BaseCommand): 
     6    option_list = ( 
     7        make_option('--format', default='json', dest='format', 
     8            help='Specifies the output serialization format for fixtures'), 
     9        make_option('--indent', default=None, dest='indent', type='int', 
     10            help='Specifies the indent level to use when pretty-printing output'), 
     11    ) 
    412    help = 'Output the contents of the database as a fixture of the given format.' 
    5     args = '[--format] [--indent] [appname ...]' 
     13    args = '[appname ...]' 
    614 
    715    def handle(self, *app_labels, **options): 
    816        from django.db.models import get_app, get_apps, get_models 
  • home/tobryan1/workspace/django/django/core/management/commands/flush.py

     
    11from django.core.management.base import NoArgsCommand, CommandError 
    22from django.core.management.color import no_style 
    33 
     4from optparse import make_option 
     5 
    46class Command(NoArgsCommand): 
     7    option_list = ( 
     8        make_option('--verbosity', action='store', dest='verbosity', default='1', 
     9            type='choice', choices=['0', '1', '2'], 
     10            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), 
     11        make_option('--noinput', action='store_false', dest='interactive', default=True, 
     12            help='Tells Django to NOT prompt the user for input of any kind.'), 
     13    ) 
    514    help = "Executes ``sqlflush`` on the current database." 
    6     args = '[--verbosity] [--noinput]' 
    715 
    816    def handle_noargs(self, **options): 
    917        from django.conf import settings 
  • home/tobryan1/workspace/django/django/core/management/commands/loaddata.py

     
    11from django.core.management.base import BaseCommand 
    22from django.core.management.color import no_style 
     3 
     4from optparse import make_option 
    35import sys 
    46import os 
    57 
     
    911    from sets import Set as set   # Python 2.3 fallback 
    1012 
    1113class Command(BaseCommand): 
     14    option_list = ( 
     15        make_option('--verbosity', action='store', dest='verbosity', default='1', 
     16            type='choice', choices=['0', '1', '2'], 
     17            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), 
     18        ) 
    1219    help = 'Installs the named fixture(s) in the database.' 
    13     args = "[--verbosity] fixture, fixture, ..." 
     20    args = "fixture [fixture ...]" 
    1421 
    1522    def handle(self, *fixture_labels, **options): 
    1623        from django.db.models import get_apps 
  • home/tobryan1/workspace/django/django/core/management/commands/reset.py

     
    11from django.core.management.base import AppCommand, CommandError 
    22from django.core.management.color import no_style 
    33 
     4from optparse import make_option 
     5 
    46class Command(AppCommand): 
     7    option_list = ( 
     8        make_option('--noinput', action='store_false', dest='interactive', default=True, 
     9            help='Tells Django to NOT prompt the user for input of any kind.'), 
     10    ) 
    511    help = "Executes ``sqlreset`` for the given app(s) in the current database." 
    6     args = '[--noinput] [appname ...]' 
     12    args = '[appname ...]' 
    713 
    814    output_transaction = True 
    915 
  • home/tobryan1/workspace/django/django/core/management/commands/runfcgi.py

     
    1414            pass 
    1515        from django.core.servers.fastcgi import runfastcgi 
    1616        runfastcgi(args) 
     17         
     18    def usage(self): 
     19        from django.core.servers.fastcgi import FASTCGI_HELP 
     20        return FASTCGI_HELP 
  • home/tobryan1/workspace/django/django/core/management/commands/runserver.py

     
    11from django.core.management.base import BaseCommand, CommandError 
     2 
     3from optparse import make_option 
    24import os 
    35import sys 
    46 
     
    35import sys 
    46 
    57class Command(BaseCommand): 
     8    option_list = ( 
     9        make_option('--noreload', action='store_false', dest='use_reloader', default=True, 
     10            help='When using "runserver": Tells Django to NOT use the auto-reloader.'), 
     11        make_option('--adminmedia', dest='admin_media_path', default='', 
     12            help='When using "runserver": Specifies the directory from which to serve admin media.'), 
     13    ) 
    614    help = "Starts a lightweight Web server for development." 
    7     args = '[--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port]' 
     15    args = '[optional port number, or ipaddr:port]' 
    816 
    917    # Validation is called explicitly each time the server is reloaded. 
    1018    requires_model_validation = False 
  • home/tobryan1/workspace/django/django/core/management/commands/shell.py

     
    11from django.core.management.base import NoArgsCommand 
    22 
     3from optparse import make_option 
     4 
    35class Command(NoArgsCommand): 
     6    option_list = ( 
     7        make_option('--plain', action='store_true', dest='plain', 
     8            help='When using "shell": Tells Django to use plain Python, not IPython.'), 
     9    ) 
    410    help = "Runs a Python interactive interpreter. Tries to use IPython, if it's available." 
    5     args = '[--plain]' 
    611 
    712    requires_model_validation = False 
    813 
  • home/tobryan1/workspace/django/django/core/management/commands/syncdb.py

     
    11from django.core.management.base import NoArgsCommand 
    22from django.core.management.color import no_style 
     3 
     4from optparse import make_option 
    35import sys 
    46 
    57try: 
     
    810    from sets import Set as set   # Python 2.3 fallback 
    911 
    1012class Command(NoArgsCommand): 
     13    option_list = ( 
     14        make_option('--verbosity', action='store', dest='verbosity', default='1', 
     15            type='choice', choices=['0', '1', '2'], 
     16            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), 
     17        make_option('--noinput', action='store_false', dest='interactive', default=True, 
     18            help='Tells Django to NOT prompt the user for input of any kind.'), 
     19    ) 
    1120    help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    12     args = '[--verbosity] [--noinput]' 
    1321 
    1422    def handle_noargs(self, **options): 
    1523        from django.db import connection, transaction, models 
  • home/tobryan1/workspace/django/django/core/management/commands/test.py

     
    11from django.core.management.base import BaseCommand 
     2 
     3from optparse import make_option 
    24import sys 
    35 
    46class Command(BaseCommand): 
     7    option_list = ( 
     8        make_option('--verbosity', action='store', dest='verbosity', default='1', 
     9            type='choice', choices=['0', '1', '2'], 
     10            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), 
     11        make_option('--noinput', action='store_false', dest='interactive', default=True, 
     12            help='Tells Django to NOT prompt the user for input of any kind.'), 
     13    ) 
    514    help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.' 
    6     args = '[--verbosity] [--noinput] [appname ...]' 
     15    args = '[appname ...]' 
    716 
    817    requires_model_validation = False 
    918 
  • home/tobryan1/workspace/django/django/core/management/commands/testserver.py

     
    11from django.core.management.base import BaseCommand 
    22 
     3from optparse import make_option 
     4 
    35class Command(BaseCommand): 
     6    option_list = ( 
     7        make_option('--verbosity', action='store', dest='verbosity', default='1', 
     8            type='choice', choices=['0', '1', '2'], 
     9            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), 
     10    ) 
    411    help = 'Runs a development server with data from the given fixture(s).' 
    512    args = '[fixture ...]' 
    613 
  • home/tobryan1/workspace/django/django/core/servers/fastcgi.py

     
    1717__version__ = "0.1" 
    1818__all__ = ["runfastcgi"] 
    1919 
    20 FASTCGI_HELP = r"""runfcgi: 
     20FASTCGI_HELP = r""" 
    2121  Run this project as a fastcgi (or some other protocol supported 
    2222  by flup) application. To do this, the flup package from 
    2323  http://www.saddi.com/software/flup/ is required. 
     
    2222  by flup) application. To do this, the flup package from 
    2323  http://www.saddi.com/software/flup/ is required. 
    2424 
    25 Usage: 
    26    django-admin.py runfcgi --settings=yourproject.settings [fcgi settings] 
    27    manage.py runfcgi [fcgi settings] 
     25   runfcgi [options] [fcgi settings] 
    2826 
    2927Optional Fcgi settings: (setting=value) 
    3028  protocol=PROTOCOL    fcgi, scgi, ajp, ... (default fcgi)