Commit 8de23e53 authored by Kostas Papadimitriou's avatar Kostas Papadimitriou
Browse files

Management utility fix

Handle command discovery of namespaced application modules.
parent bbd0f38f
#!/usr/bin/env python
"""
Extented django management module
Most of the code is shared from django.core.management module
to allow us extend the default django ManagementUtility object
used to provide command line interface of the django project
included in snf-webproject package.
The extended class provides the following:
- additional argument for the configuration of the SYNNEFO_SETTINGS_DIR
environmental variable (--settings-dir).
- a fix for management utility to handle custom commands defined in
applications living in namespaced packages (django ticket #14087)
- override of --version command to display the snf-webproject version
"""
from django.core.management import ManagementUtility, setup_environ, \
BaseCommand, LaxOptionParser, handle_default_options
BaseCommand, LaxOptionParser, handle_default_options, find_commands, \
load_command_class
from django.core import management
from django.utils.importlib import import_module
from optparse import Option, make_option
from synnefo.util.version import get_component_version
import sys
import os
import imp
_commands = None
def find_modules(name, path=None):
"""Find all modules with name 'name'
Unlike find_module in the imp package this returns a list of all
matched modules.
"""
results = []
if path is None: path = sys.path
for p in path:
importer = sys.path_importer_cache.get(p, None)
if importer is None:
find_module = imp.find_module
else:
find_module = importer.find_module
try:
result = find_module(name, [p])
if result is not None:
results.append(result)
except ImportError:
pass
if not results:
raise ImportError("No module named %.200s" % name)
return results
def find_management_module(app_name):
"""
Determines the path to the management module for the given app_name,
without actually importing the application or the management module.
Raises ImportError if the management module cannot be found for any reason.
"""
parts = app_name.split('.')
parts.append('management')
parts.reverse()
part = parts.pop()
paths = None
# When using manage.py, the project module is added to the path,
# loaded, then removed from the path. This means that
# testproject.testapp.models can be loaded in future, even if
# testproject isn't in the path. When looking for the management
# module, we need look for the case where the project name is part
# of the app_name but the project directory itself isn't on the path.
try:
modules = find_modules(part, paths)
paths = [m[1] for m in modules]
except ImportError,e:
if os.path.basename(os.getcwd()) != part:
raise e
while parts:
part = parts.pop()
modules = find_modules(part, paths)
paths = [m[1] for m in modules]
return paths[0]
def get_commands():
"""
Returns a dictionary mapping command names to their callback applications.
This works by looking for a management.commands package in django.core, and
in each installed application -- if a commands package exists, all commands
in that package are registered.
Core commands are always included. If a settings module has been
specified, user-defined commands will also be included, the
startproject command will be disabled, and the startapp command
will be modified to use the directory in which the settings module appears.
The dictionary is in the format {command_name: app_name}. Key-value
pairs from this dictionary can then be used in calls to
load_command_class(app_name, command_name)
If a specific version of a command must be loaded (e.g., with the
startapp command), the instantiated module can be placed in the
dictionary in place of the application name.
The dictionary is cached on the first call and reused on subsequent
calls.
"""
global _commands
if _commands is None:
_commands = dict([(name, 'django.core') for name in \
find_commands(management.__path__[0])])
# Find the installed apps
try:
from django.conf import settings
apps = settings.INSTALLED_APPS
except (AttributeError, EnvironmentError, ImportError):
apps = []
# Find the project directory
try:
from django.conf import settings
module = import_module(settings.SETTINGS_MODULE)
project_directory = setup_environ(module, settings.SETTINGS_MODULE)
except (AttributeError, EnvironmentError, ImportError, KeyError):
project_directory = None
# Find and load the management module for each installed app.
for app_name in apps:
try:
path = find_management_module(app_name)
_commands.update(dict([(name, app_name)
for name in find_commands(path)]))
except ImportError:
pass # No management module - ignore this app
if project_directory:
# Remove the "startproject" command from self.commands, because
# that's a django-admin.py command, not a manage.py command.
del _commands['startproject']
# Override the startapp command so that it always uses the
# project_directory, not the current working directory
# (which is default).
from django.core.management.commands.startapp import ProjectCommand
_commands['startapp'] = ProjectCommand(project_directory)
return _commands
class SynnefoManagementUtility(ManagementUtility):
"""
......@@ -76,6 +221,37 @@ class SynnefoManagementUtility(ManagementUtility):
else:
self.fetch_command(subcommand).run_from_argv(self.argv)
def main_help_text(self):
"""
Returns the script's main help text, as a string.
"""
usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,'']
usage.append('Available subcommands:')
commands = get_commands().keys()
commands.sort()
for cmd in commands:
usage.append(' %s' % cmd)
return '\n'.join(usage)
def fetch_command(self, subcommand):
"""
Tries to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually
"django-admin.py" or "manage.py") if it can't be found.
"""
try:
app_name = get_commands()[subcommand]
except KeyError:
sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
(subcommand, self.prog_name))
sys.exit(1)
if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly.
klass = app_name
else:
klass = load_command_class(app_name, subcommand)
return klass
def main():
# no need to run setup_environ
# we already know our project
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment