root / snf-webproject / synnefo / webproject / manage.py @ 18a544f5
History | View | Annotate | Download (9.7 kB)
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 |
"""
|
17 |
|
18 |
from django.core.management import ManagementUtility, setup_environ, \ |
19 |
BaseCommand, LaxOptionParser, handle_default_options, find_commands, \ |
20 |
load_command_class
|
21 |
|
22 |
from django.core import management |
23 |
from django.utils.importlib import import_module |
24 |
from optparse import Option, make_option |
25 |
from synnefo.util.version import get_component_version |
26 |
|
27 |
import sys |
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
|
156 |
|
157 |
class SynnefoManagementUtility(ManagementUtility): |
158 |
"""
|
159 |
Override django ManagementUtility to allow us provide a custom
|
160 |
--settings-dir option for synnefo application.
|
161 |
|
162 |
Most of the following code is a copy from django.core.management module
|
163 |
"""
|
164 |
|
165 |
def execute(self): |
166 |
"""
|
167 |
Given the command-line arguments, this figures out which subcommand is
|
168 |
being run, creates a parser appropriate to that command, and runs it.
|
169 |
"""
|
170 |
|
171 |
# --settings-dir option
|
172 |
# will remove it later to avoid django commands from raising errors
|
173 |
option_list = BaseCommand.option_list + ( |
174 |
make_option('--settings-dir',
|
175 |
action='store',
|
176 |
dest='settings_dir',
|
177 |
default=None,
|
178 |
help='Load *.conf files from directory as settings'),)
|
179 |
|
180 |
# Preprocess options to extract --settings and --pythonpath.
|
181 |
# These options could affect the commands that are available, so they
|
182 |
# must be processed early.
|
183 |
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
|
184 |
version=get_component_version('webproject'),
|
185 |
option_list=option_list) |
186 |
self.autocomplete()
|
187 |
try:
|
188 |
options, args = parser.parse_args(self.argv)
|
189 |
handle_default_options(options) |
190 |
except:
|
191 |
pass # Ignore any option errors at this point. |
192 |
|
193 |
# user provides custom settings dir
|
194 |
# set it as environmental variable and remove it from self.argv
|
195 |
if options.settings_dir:
|
196 |
os.environ['SYNNEFO_SETTINGS_DIR'] = options.settings_dir
|
197 |
for arg in self.argv: |
198 |
if arg.startswith('--settings-dir'): |
199 |
self.argv.remove(arg)
|
200 |
|
201 |
try:
|
202 |
subcommand = self.argv[1] |
203 |
except IndexError: |
204 |
subcommand = 'help' # Display help if no arguments were given. |
205 |
|
206 |
if subcommand == 'help': |
207 |
if len(args) > 2: |
208 |
self.fetch_command(args[2]).print_help(self.prog_name, args[2]) |
209 |
else:
|
210 |
parser.print_lax_help() |
211 |
sys.stdout.write(self.main_help_text() + '\n') |
212 |
sys.exit(1)
|
213 |
# Special-cases: We want 'django-admin.py --version' and
|
214 |
# 'django-admin.py --help' to work, for backwards compatibility.
|
215 |
elif self.argv[1:] == ['--version']: |
216 |
# LaxOptionParser already takes care of printing the version.
|
217 |
pass
|
218 |
elif self.argv[1:] in (['--help'], ['-h']): |
219 |
parser.print_lax_help() |
220 |
sys.stdout.write(self.main_help_text() + '\n') |
221 |
else:
|
222 |
self.fetch_command(subcommand).run_from_argv(self.argv) |
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 |
|
255 |
def main(): |
256 |
# no need to run setup_environ
|
257 |
# we already know our project
|
258 |
os.environ['DJANGO_SETTINGS_MODULE'] = os.environ.get('DJANGO_SETTINGS_MODULE', |
259 |
'synnefo.settings')
|
260 |
mu = SynnefoManagementUtility(sys.argv) |
261 |
mu.execute() |
262 |
|
263 |
if __name__ == "__main__": |
264 |
main() |