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