Changeset 6400
- Timestamp:
- 09/21/07 11:19:20 (10 months ago)
- Files:
-
- django/trunk/django/core/management/base.py (modified) (2 diffs)
- django/trunk/django/core/management/__init__.py (modified) (8 diffs)
- django/trunk/docs/django-admin.txt (modified) (1 diff)
- django/trunk/tests/modeltests/user_commands (added)
- django/trunk/tests/modeltests/user_commands/__init__.py (added)
- django/trunk/tests/modeltests/user_commands/management (added)
- django/trunk/tests/modeltests/user_commands/management/commands (added)
- django/trunk/tests/modeltests/user_commands/management/commands/dance.py (added)
- django/trunk/tests/modeltests/user_commands/management/commands/__init__.py (added)
- django/trunk/tests/modeltests/user_commands/management/__init__.py (added)
- django/trunk/tests/modeltests/user_commands/models.py (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/core/management/base.py
r6281 r6400 10 10 pass 11 11 12 def 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 12 23 class BaseCommand(object): 13 24 # Metadata about this command. … … 56 67 parser = self.create_parser(argv[0], argv[1]) 57 68 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) 62 70 self.execute(*args, **options.__dict__) 63 71 django/trunk/django/core/management/__init__.py
r6093 r6400 1 1 import django 2 from django.core.management.base import BaseCommand, CommandError, handle_default_options 2 3 from optparse import OptionParser 3 4 import os 4 5 import sys 6 from imp import find_module 5 7 6 8 # For backwards compatibility: get_version() used to be in this module. 7 9 get_version = django.get_version 8 10 9 def load_command_class(name): 10 """ 11 Given a command name, returns the Command class instance. Raises 12 ImportError if it doesn't exist. 13 """ 14 # Let the ImportError propogate. 15 return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')() 11 # A cache of loaded commands, so that call_command 12 # doesn't have to reload every time it is called 13 _commands = None 14 15 def find_commands(management_dir): 16 """ 17 Given a path to a management directory, return a list of all the command names 18 that are available. Returns an empty list if no commands are defined. 19 """ 20 command_dir = os.path.join(management_dir,'commands') 21 try: 22 return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] 23 except OSError: 24 return [] 25 26 def find_management_module(app_name): 27 """ 28 Determine the path to the management module for the application named, 29 without acutally importing the application or the management module. 30 31 Raises ImportError if the management module cannot be found for any reason. 32 """ 33 parts = app_name.split('.') 34 parts.append('management') 35 parts.reverse() 36 path = None 37 while parts: 38 part = parts.pop() 39 f,path,descr = find_module(part, path and [path] or None) 40 return path 41 42 def load_command_class(app_name, name): 43 """ 44 Given a command name and an application name, returns the Command 45 class instance. All errors raised by the importation process 46 (ImportError, AttributeError) are allowed to propagate. 47 """ 48 return getattr(__import__('%s.management.commands.%s' % (app_name, name), 49 {}, {}, ['Command']), 'Command')() 50 51 def get_commands(load_user_commands=True, project_directory=None): 52 """ 53 Returns a dictionary of commands against the application in which 54 those commands can be found. This works by looking for a 55 management.commands package in django.core, and in each installed 56 application -- if a commands package exists, all commands in that 57 package are registered. 58 59 Core commands are always included; user-defined commands will also 60 be included if ``load_user_commands`` is True. If a project directory 61 is provided, the startproject command will be disabled, and the 62 startapp command will be modified to use that directory. 63 64 The dictionary is in the format {command_name: app_name}. Key-value 65 pairs from this dictionary can then be used in calls to 66 load_command_class(app_name, command_name) 67 68 The dictionary is cached on the first call, and reused on subsequent 69 calls. 70 """ 71 global _commands 72 if _commands is None: 73 _commands = dict([(name, 'django.core') 74 for name in find_commands(__path__[0])]) 75 if load_user_commands: 76 # Get commands from all installed apps 77 from django.conf import settings 78 for app_name in settings.INSTALLED_APPS: 79 try: 80 path = find_management_module(app_name) 81 _commands.update(dict([(name, app_name) 82 for name in find_commands(path)])) 83 except ImportError: 84 pass # No management module - ignore this app 85 86 if project_directory: 87 # Remove the "startproject" command from self.commands, because 88 # that's a django-admin.py command, not a manage.py command. 89 del _commands['startproject'] 90 91 # Override the startapp command so that it always uses the 92 # project_directory, not the current working directory 93 # (which is default). 94 from django.core.management.commands.startapp import ProjectCommand 95 _commands['startapp'] = ProjectCommand(project_directory) 96 97 return _commands 16 98 17 99 def call_command(name, *args, **options): … … 26 108 call_command('sqlall', 'myapp') 27 109 """ 28 klass = load_command_class(name) 110 try: 111 app_name = get_commands()[name] 112 klass = load_command_class(app_name, name) 113 except KeyError: 114 raise CommandError, "Unknown command: %r" % name 29 115 return klass.execute(*args, **options) 116 117 class LaxOptionParser(OptionParser): 118 """ 119 An option parser that doesn't raise any errors on unknown options. 120 121 This is needed because the --settings and --pythonpath options affect 122 the commands (and thus the options) that are available to the user. 123 """ 124 def error(self, msg): 125 pass 30 126 31 127 class ManagementUtility(object): … … 39 135 self.argv = argv or sys.argv[:] 40 136 self.prog_name = os.path.basename(self.argv[0]) 41 self.commands = self.default_commands() 42 43 def default_commands(self): 44 """ 45 Returns a dictionary of instances of all available Command classes. 46 47 This works by looking for and loading all Python modules in the 48 django.core.management.commands package. 49 50 The dictionary is in the format {name: command_instance}. 51 """ 52 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')] 54 return dict([(name, load_command_class(name)) for name in names]) 55 137 self.project_directory = None 138 self.user_commands = False 139 56 140 def main_help_text(self): 57 141 """ … … 62 146 usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name) 63 147 usage.append('Available subcommands:') 64 commands = self.commands.keys()148 commands = get_commands(self.user_commands, self.project_directory).keys() 65 149 commands.sort() 66 150 for cmd in commands: … … 75 159 """ 76 160 try: 77 return self.commands[subcommand] 161 app_name = get_commands(self.user_commands, self.project_directory)[subcommand] 162 klass = load_command_class(app_name, subcommand) 78 163 except KeyError: 79 164 sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (subcommand, self.prog_name)) 80 165 sys.exit(1) 81 166 return klass 167 82 168 def execute(self): 83 169 """ … … 85 171 being run, creates a parser appropriate to that command, and runs it. 86 172 """ 173 # Preprocess options to extract --settings and --pythonpath. These options 174 # could affect the commands that are available, so they must be processed 175 # early 176 parser = LaxOptionParser(version=get_version(), 177 option_list=BaseCommand.option_list) 178 options, args = parser.parse_args(self.argv) 179 handle_default_options(options) 180 87 181 try: 88 182 subcommand = self.argv[1] … … 92 186 93 187 if subcommand == 'help': 94 if len( self.argv) > 2:95 self.fetch_command( self.argv[2]).print_help(self.prog_name, self.argv[2])188 if len(args) > 2: 189 self.fetch_command(args[2]).print_help(self.prog_name, args[2]) 96 190 else: 97 191 sys.stderr.write(self.main_help_text() + '\n') … … 117 211 def __init__(self, argv, project_directory): 118 212 super(ProjectManagementUtility, self).__init__(argv) 119 120 # Remove the "startproject" command from self.commands, because 121 # that's a django-admin.py command, not a manage.py command. 122 del self.commands['startproject'] 123 124 # Override the startapp command so that it always uses the 125 # project_directory, not the current working directory (which is default). 126 from django.core.management.commands.startapp import ProjectCommand 127 self.commands['startapp'] = ProjectCommand(project_directory) 128 213 self.project_directory = project_directory 214 self.user_commands = True 215 129 216 def setup_environ(settings_mod): 130 217 """ django/trunk/docs/django-admin.txt
r6363 r6400 736 736 * Type ``sql``, then [TAB], to see all available options whose names start 737 737 with ``sql``. 738 739 Customized actions 740 ================== 741 742 **New in Django development version** 743 744 If you want to add an action of your own to ``manage.py``, you can. 745 Simply add a ``management/commands`` directory to your application. 746 Each python module in that directory will be discovered and registered as 747 a command that can be executed as an action when you run ``manage.py``:: 748 749 /fancy_blog 750 __init__.py 751 models.py 752 /management 753 __init__.py 754 /commands 755 __init__.py 756 explode.py 757 views.py 758 759 In this example, ``explode`` command will be made available to any project 760 that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``. 761 762 The ``explode.py`` module has only one requirement -- it must define a class 763 called ``Command`` that extends ``django.core.management.base.BaseCommand``. 764 765 For more details on how to define your own commands, look at the code for the 766 existing ``django-admin.py`` commands, in ``/django/core/management/commands``.
