Revision 18a544f5 snf-webproject/synnefo/webproject/manage.py

b/snf-webproject/synnefo/webproject/manage.py
1
#!/usr/bin/env python
1
"""
2
Extented django management module
3

  
4
Most of the code is shared from django.core.management module
5
to allow us extend the default django ManagementUtility object
6
used to provide command line interface of the django project
7
included in snf-webproject package.
8

  
9
The extended class provides the following:
10

  
11
- additional argument for the configuration of the SYNNEFO_SETTINGS_DIR
12
  environmental variable (--settings-dir).
13
- a fix for management utility to handle custom commands defined in
14
  applications living in namespaced packages (django ticket #14087)
15
- override of --version command to display the snf-webproject version
16
"""
2 17

  
3 18
from django.core.management import ManagementUtility, setup_environ, \
4
BaseCommand, LaxOptionParser, handle_default_options
19
BaseCommand, LaxOptionParser, handle_default_options, find_commands, \
20
load_command_class
5 21

  
22
from django.core import management
23
from django.utils.importlib import import_module
6 24
from optparse import Option, make_option
7 25
from synnefo.util.version import get_component_version
8 26

  
9 27
import sys
10 28
import os
29
import imp
30

  
31
_commands = None
32

  
33
def find_modules(name, path=None):
34
    """Find all modules with name 'name'
35

  
36
    Unlike find_module in the imp package this returns a list of all
37
    matched modules.
38
    """
39
    results = []
40
    if path is None: path = sys.path
41
    for p in path:
42
        importer = sys.path_importer_cache.get(p, None)
43
        if importer is None:
44
            find_module = imp.find_module
45
        else:
46
            find_module = importer.find_module
47

  
48
        try:
49
            result = find_module(name, [p])
50
            if result is not None:
51
                results.append(result)
52
        except ImportError:
53
            pass
54
    if not results:
55
        raise ImportError("No module named %.200s" % name)
56
    return results
57

  
58
def find_management_module(app_name):
59
    """
60
    Determines the path to the management module for the given app_name,
61
    without actually importing the application or the management module.
62

  
63
    Raises ImportError if the management module cannot be found for any reason.
64
    """
65
    parts = app_name.split('.')
66
    parts.append('management')
67
    parts.reverse()
68
    part = parts.pop()
69
    paths = None
70

  
71
    # When using manage.py, the project module is added to the path,
72
    # loaded, then removed from the path. This means that
73
    # testproject.testapp.models can be loaded in future, even if
74
    # testproject isn't in the path. When looking for the management
75
    # module, we need look for the case where the project name is part
76
    # of the app_name but the project directory itself isn't on the path.
77
    try:
78
        modules = find_modules(part, paths)
79
        paths = [m[1] for m in modules]
80
    except ImportError,e:
81
        if os.path.basename(os.getcwd()) != part:
82
            raise e
83

  
84
    while parts:
85
        part = parts.pop()
86
        modules = find_modules(part, paths)
87
        paths = [m[1] for m in modules]
88
    return paths[0]
89

  
90

  
91
def get_commands():
92
    """
93
    Returns a dictionary mapping command names to their callback applications.
94

  
95
    This works by looking for a management.commands package in django.core, and
96
    in each installed application -- if a commands package exists, all commands
97
    in that package are registered.
98

  
99
    Core commands are always included. If a settings module has been
100
    specified, user-defined commands will also be included, the
101
    startproject command will be disabled, and the startapp command
102
    will be modified to use the directory in which the settings module appears.
103

  
104
    The dictionary is in the format {command_name: app_name}. Key-value
105
    pairs from this dictionary can then be used in calls to
106
    load_command_class(app_name, command_name)
107

  
108
    If a specific version of a command must be loaded (e.g., with the
109
    startapp command), the instantiated module can be placed in the
110
    dictionary in place of the application name.
111

  
112
    The dictionary is cached on the first call and reused on subsequent
113
    calls.
114
    """
115
    global _commands
116
    if _commands is None:
117
        _commands = dict([(name, 'django.core') for name in \
118
            find_commands(management.__path__[0])])
119

  
120
        # Find the installed apps
121
        try:
122
            from django.conf import settings
123
            apps = settings.INSTALLED_APPS
124
        except (AttributeError, EnvironmentError, ImportError):
125
            apps = []
126

  
127
        # Find the project directory
128
        try:
129
            from django.conf import settings
130
            module = import_module(settings.SETTINGS_MODULE)
131
            project_directory = setup_environ(module, settings.SETTINGS_MODULE)
132
        except (AttributeError, EnvironmentError, ImportError, KeyError):
133
            project_directory = None
134

  
135
        # Find and load the management module for each installed app.
136
        for app_name in apps:
137
            try:
138
                path = find_management_module(app_name)
139
                _commands.update(dict([(name, app_name)
140
                                       for name in find_commands(path)]))
141
            except ImportError:
142
                pass # No management module - ignore this app
143

  
144
        if project_directory:
145
            # Remove the "startproject" command from self.commands, because
146
            # that's a django-admin.py command, not a manage.py command.
147
            del _commands['startproject']
148

  
149
            # Override the startapp command so that it always uses the
150
            # project_directory, not the current working directory
151
            # (which is default).
152
            from django.core.management.commands.startapp import ProjectCommand
153
            _commands['startapp'] = ProjectCommand(project_directory)
154

  
155
    return _commands
11 156

  
12 157
class SynnefoManagementUtility(ManagementUtility):
13 158
    """
......
76 221
        else:
77 222
            self.fetch_command(subcommand).run_from_argv(self.argv)
78 223

  
224
    def main_help_text(self):
225
        """
226
        Returns the script's main help text, as a string.
227
        """
228
        usage = ['',"Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name,'']
229
        usage.append('Available subcommands:')
230
        commands = get_commands().keys()
231
        commands.sort()
232
        for cmd in commands:
233
            usage.append('  %s' % cmd)
234
        return '\n'.join(usage)
235

  
236
    def fetch_command(self, subcommand):
237
        """
238
        Tries to fetch the given subcommand, printing a message with the
239
        appropriate command called from the command line (usually
240
        "django-admin.py" or "manage.py") if it can't be found.
241
        """
242
        try:
243
            app_name = get_commands()[subcommand]
244
        except KeyError:
245
            sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
246
                (subcommand, self.prog_name))
247
            sys.exit(1)
248
        if isinstance(app_name, BaseCommand):
249
            # If the command is already loaded, use it directly.
250
            klass = app_name
251
        else:
252
            klass = load_command_class(app_name, subcommand)
253
        return klass
254

  
79 255
def main():
80 256
    # no need to run setup_environ
81 257
    # we already know our project

Also available in: Unified diff