web/lib/django/core/management/__init__.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/core/management/__init__.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/core/management/__init__.py	Tue May 25 02:43:45 2010 +0200
@@ -152,7 +152,7 @@
         else:
             klass = load_command_class(app_name, name)
     except KeyError:
-        raise CommandError, "Unknown command: %r" % name
+        raise CommandError("Unknown command: %r" % name)
 
     # Grab out a list of defaults from the options. optparse does this for us
     # when the script runs from the command line, but since call_command can
@@ -261,6 +261,82 @@
             sys.exit(1)
         return klass
 
+    def autocomplete(self):
+        """
+        Output completion suggestions for BASH.
+
+        The output of this function is passed to BASH's `COMREPLY` variable and
+        treated as completion suggestions. `COMREPLY` expects a space
+        separated string as the result.
+
+        The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
+        to get information about the cli input. Please refer to the BASH
+        man-page for more information about this variables.
+
+        Subcommand options are saved as pairs. A pair consists of
+        the long option string (e.g. '--exclude') and a boolean
+        value indicating if the option requires arguments. When printing to
+        stdout, a equal sign is appended to options which require arguments.
+
+        Note: If debugging this function, it is recommended to write the debug
+        output in a separate file. Otherwise the debug output will be treated
+        and formatted as potential completion suggestions.
+        """
+        # Don't complete if user hasn't sourced bash_completion file.
+        if not os.environ.has_key('DJANGO_AUTO_COMPLETE'):
+            return
+
+        cwords = os.environ['COMP_WORDS'].split()[1:]
+        cword = int(os.environ['COMP_CWORD'])
+
+        try:
+            curr = cwords[cword-1]
+        except IndexError:
+            curr = ''
+
+        subcommands = get_commands().keys() + ['help']
+        options = [('--help', None)]
+
+        # subcommand
+        if cword == 1:
+            print ' '.join(sorted(filter(lambda x: x.startswith(curr), subcommands)))
+        # subcommand options
+        # special case: the 'help' subcommand has no options
+        elif cwords[0] in subcommands and cwords[0] != 'help':
+            subcommand_cls = self.fetch_command(cwords[0])
+            # special case: 'runfcgi' stores additional options as
+            # 'key=value' pairs
+            if cwords[0] == 'runfcgi':
+                from django.core.servers.fastcgi import FASTCGI_OPTIONS
+                options += [(k, 1) for k in FASTCGI_OPTIONS]
+            # special case: add the names of installed apps to options
+            elif cwords[0] in ('dumpdata', 'reset', 'sql', 'sqlall',
+                               'sqlclear', 'sqlcustom', 'sqlindexes',
+                               'sqlreset', 'sqlsequencereset', 'test'):
+                try:
+                    from django.conf import settings
+                    # Get the last part of the dotted path as the app name.
+                    options += [(a.split('.')[-1], 0) for a in settings.INSTALLED_APPS]
+                except ImportError:
+                    # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
+                    # user will find out once they execute the command.
+                    pass
+            options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
+                        subcommand_cls.option_list]
+            # filter out previously specified options from available options
+            prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]]
+            options = filter(lambda (x, v): x not in prev_opts, options)
+
+            # filter options by current input
+            options = sorted([(k, v) for k, v in options if k.startswith(curr)])
+            for option in options:
+                opt_label = option[0]
+                # append '=' to options which require args
+                if option[1]:
+                    opt_label += '='
+                print opt_label
+        sys.exit(1)
+
     def execute(self):
         """
         Given the command-line arguments, this figures out which subcommand is
@@ -272,6 +348,7 @@
         parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
                                  version=get_version(),
                                  option_list=BaseCommand.option_list)
+        self.autocomplete()
         try:
             options, args = parser.parse_args(self.argv)
             handle_default_options(options)
@@ -281,8 +358,7 @@
         try:
             subcommand = self.argv[1]
         except IndexError:
-            sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
-            sys.exit(1)
+            subcommand = 'help' # Display help if no arguments were given.
 
         if subcommand == 'help':
             if len(args) > 2: