Ticket #5222: __init__.py

File __init__.py, 9.3 KB (added by dnordberg@…, 8 years ago)

The patched version of init.py

Line 
1import django
2from optparse import OptionParser
3import os
4import sys
5import textwrap
6
7# For backwards compatibility: get_version() used to be in this module.
8get_version = django.get_version
9
10def find_commands(path):
11    """
12    Given a path to a management directory, return a list of all the command names
13    that are available. Returns an empty list if no commands are defined.
14    """
15    command_dir = os.path.join(path, 'commands')
16    try:
17        return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
18    except OSError:
19        return []
20
21def load_default_commands():
22    """
23    Returns a dictionary of instances of all available Command classes.
24
25    This works by looking for and loading all Python modules in the
26    django.core.management.commands package.
27
28    The dictionary is in the format {name: command_instance}.
29    """
30    return dict([(name, getattr(__import__('django.core.management.commands.%s' % (name), {}, {}, ['Command']), 'Command')()) for name in find_commands(__path__[0])])
31
32def load_project_commands():
33    """
34    Returns a dictionary of instances of all available Command classes.
35
36    It looks for a management.commands package in each installed application
37    -- if a commands package exists, it loads all commands in that
38    application and raises an AttributeError if a command doesn't contain a
39    Command instance.
40
41    The dictionary is in the format {name: command_instance}.
42    """
43   
44    commands = {}
45   
46    if 'DJANGO_SETTINGS_MODULE' in os.environ:
47        from django.db import models
48       
49        # Get commands from all installed apps
50        for app in models.get_apps():
51            try:
52                app_name = '.'.join(app.__name__.split('.')[:-1])
53                path = os.path.join(os.path.dirname(app.__file__),'management')
54                commands.update(dict([(name, getattr(__import__('%s.management.commands.%s' % (app_name, name), {}, {}, ['Command']), 'Command')()) for name in find_commands(path)]))
55            except AttributeError:
56                sys.stderr.write("Management command '%s' in application '%s' doesn't contain a Command instance.\n" % (name, app_name))
57                sys.exit(1)
58           
59    return commands
60
61def call_command(name, *args, **options):
62    """
63    Calls the given command, with the given options and args/kwargs.
64   
65    This is the primary API you should use for calling specific commands.
66   
67    Some examples:
68        call_command('syncdb')
69        call_command('shell', plain=True)
70        call_command('sqlall', 'myapp')
71    """
72    klass = getattr(__import__(_DJANGO_COMMANDS[name].__module__, {}, {}, ['Command']), 'Command')()
73    return klass.execute(*args, **options)
74
75class ManagementUtility(object):
76    """
77    Encapsulates the logic of the django-admin.py and manage.py utilities.
78
79    A ManagementUtility has a number of commands, which can be manipulated
80    by editing the self.commands dictionary.
81    """
82    def __init__(self):
83        self.commands = _DJANGO_COMMANDS
84       
85    def usage(self):
86        """
87        Returns a usage string, for use with optparse.
88
89        The string doesn't include the options (e.g., "--verbose"), because
90        optparse puts those in automatically.
91        """
92        usage = ["%prog command [options]\nactions:"]
93        commands = self.commands.items()
94        commands.sort()
95        for name, cmd in commands:
96            usage.append(%s %s' % (name, cmd.args))
97            usage.extend(textwrap.wrap(cmd.help, initial_indent='    ', subsequent_indent='    '))
98            usage.append('')
99        return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space.
100
101    def execute(self, argv=None):
102        """
103        Parses the given argv from the command line, determines which command
104        to run and runs the command.
105        """
106        if argv is None:
107            argv = sys.argv
108
109        # Create the parser object and parse the command-line args.
110        # TODO: Ideally each Command class would register its own options for
111        # add_option(), but we'd need to figure out how to allow for multiple
112        # Commands using the same options. The optparse library gets in the way
113        # by checking for conflicts:
114        # http://docs.python.org/lib/optparse-conflicts-between-options.html
115        parser = OptionParser(usage=self.usage(), version=get_version())
116        parser.add_option('--settings',
117            help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
118        parser.add_option('--pythonpath',
119            help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
120        parser.add_option('--plain', action='store_true', dest='plain',
121            help='When using "shell": Tells Django to use plain Python, not IPython.')
122        parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
123            help='Tells Django to NOT prompt the user for input of any kind.')
124        parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
125            help='When using "runserver": Tells Django to NOT use the auto-reloader.')
126        parser.add_option('--format', default='json', dest='format',
127            help='Specifies the output serialization format for fixtures')
128        parser.add_option('--indent', default=None, dest='indent',
129            type='int', help='Specifies the indent level to use when pretty-printing output')
130        parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
131            type='choice', choices=['0', '1', '2'],
132            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
133        parser.add_option('--adminmedia', dest='admin_media_path', default='',
134            help='When using "runserver": Specifies the directory from which to serve admin media.')
135        options, args = parser.parse_args(argv[1:])
136
137        # If the 'settings' or 'pythonpath' options were submitted, activate those.
138        if options.settings:
139            os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
140        if options.pythonpath:
141            sys.path.insert(0, options.pythonpath)
142
143        # Run the appropriate command.
144        try:
145            command_name = args[0]
146        except IndexError:
147            sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0]))
148            sys.exit(1)
149        try:
150            command = self.commands[command_name]
151        except KeyError:
152            sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0])))
153            sys.exit(1)
154        command.execute(*args[1:], **options.__dict__)
155
156class ProjectManagementUtility(ManagementUtility):
157    """
158    A ManagementUtility that is specific to a particular Django project.
159    As such, its commands are slightly different than those of its parent
160    class.
161
162    In practice, this class represents manage.py, whereas ManagementUtility
163    represents django-admin.py.
164    """
165    def __init__(self, project_directory):
166        super(ProjectManagementUtility, self).__init__()
167
168        # Remove the "startproject" command from self.commands, because
169        # that's a django-admin.py command, not a manage.py command.
170        del self.commands['startproject']
171
172        # Override the startapp command so that it always uses the
173        # project_directory, not the current working directory (which is default).
174        from django.core.management.commands.startapp import ProjectCommand
175        self.commands['startapp'] = ProjectCommand(project_directory)
176
177def setup_environ(settings_mod):
178    """
179    Configure the runtime environment. This can also be used by external
180    scripts wanting to set up a similar environment to manage.py.
181    """
182    # Add this project to sys.path so that it's importable in the conventional
183    # way. For example, if this file (manage.py) lives in a directory
184    # "myproject", this code would add "/path/to/myproject" to sys.path.
185    project_directory, settings_filename = os.path.split(settings_mod.__file__)
186    project_name = os.path.basename(project_directory)
187    settings_name = os.path.splitext(settings_filename)[0]
188    sys.path.append(os.path.join(project_directory, '..'))
189    project_module = __import__(project_name, {}, {}, [''])
190    sys.path.pop()
191
192    # Set DJANGO_SETTINGS_MODULE appropriately.
193    os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
194   
195    # Update default commands with project commands for django internal
196    # webservers and the manage.py utility
197    _DJANGO_COMMANDS.update(load_project_commands())
198   
199    return project_directory
200
201def execute_from_command_line(argv=None):
202    """
203    A simple method that runs a ManagementUtility.
204    """
205    utility = ManagementUtility()
206    utility.execute(argv)
207
208def execute_manager(settings_mod, argv=None):
209    """
210    Like execute_from_command_line(), but for use by manage.py, a
211    project-specific django-admin.py utility.
212    """
213    project_directory = setup_environ(settings_mod)
214    utility = ProjectManagementUtility(project_directory)
215    utility.execute(argv)
216   
217# Add default commands to a dict
218_DJANGO_COMMANDS = load_default_commands()
219
220# Update default commands with project commands
221# when DJANGO_SETTINGS_MODULE is already set.
222_DJANGO_COMMANDS.update(load_project_commands())
223
Back to Top