-
commit a6ed2cf0f2f5ba30f6753e6ac38337d570050aad
Author: Claude Paroz <claude@2xlibre.net>
Date: Sat May 5 21:01:12 2012 +0200
Set output of commands through logging methods
diff --git a/django/contrib/auth/management/commands/changepassword.py b/django/contrib/auth/management/commands/changepassword.py
index d125dfe..d46cb4f 100644
a
|
b
|
class Command(BaseCommand):
|
35 | 35 | except User.DoesNotExist: |
36 | 36 | raise CommandError("user '%s' does not exist" % username) |
37 | 37 | |
38 | | self.stdout.write("Changing password for user '%s'\n" % u.username) |
| 38 | self.info("Changing password for user '%s'" % u.username) |
39 | 39 | |
40 | 40 | MAX_TRIES = 3 |
41 | 41 | count = 0 |
… |
… |
class Command(BaseCommand):
|
44 | 44 | p1 = self._get_pass() |
45 | 45 | p2 = self._get_pass("Password (again): ") |
46 | 46 | if p1 != p2: |
47 | | self.stdout.write("Passwords do not match. Please try again.\n") |
| 47 | self.info("Passwords do not match. Please try again.") |
48 | 48 | count = count + 1 |
49 | 49 | |
50 | 50 | if count == MAX_TRIES: |
-
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
index ad65977..99e2e8e 100644
a
|
b
|
class Command(BaseCommand):
|
80 | 80 | if default_username and username == '': |
81 | 81 | username = default_username |
82 | 82 | if not RE_VALID_USERNAME.match(username): |
83 | | sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n") |
| 83 | self.error("Error: That username is invalid. Use only letters, digits and underscores.") |
84 | 84 | username = None |
85 | 85 | continue |
86 | 86 | try: |
… |
… |
class Command(BaseCommand):
|
88 | 88 | except User.DoesNotExist: |
89 | 89 | break |
90 | 90 | else: |
91 | | sys.stderr.write("Error: That username is already taken.\n") |
| 91 | self.error("Error: That username is already taken.") |
92 | 92 | username = None |
93 | 93 | |
94 | 94 | # Get an email |
… |
… |
class Command(BaseCommand):
|
98 | 98 | try: |
99 | 99 | is_valid_email(email) |
100 | 100 | except exceptions.ValidationError: |
101 | | sys.stderr.write("Error: That e-mail address is invalid.\n") |
| 101 | self.error("Error: That e-mail address is invalid.") |
102 | 102 | email = None |
103 | 103 | else: |
104 | 104 | break |
… |
… |
class Command(BaseCommand):
|
109 | 109 | password = getpass.getpass() |
110 | 110 | password2 = getpass.getpass('Password (again): ') |
111 | 111 | if password != password2: |
112 | | sys.stderr.write("Error: Your passwords didn't match.\n") |
| 112 | self.error("Error: Your passwords didn't match.") |
113 | 113 | password = None |
114 | 114 | continue |
115 | 115 | if password.strip() == '': |
116 | | sys.stderr.write("Error: Blank passwords aren't allowed.\n") |
| 116 | self.error("Error: Blank passwords aren't allowed.") |
117 | 117 | password = None |
118 | 118 | continue |
119 | 119 | break |
120 | 120 | except KeyboardInterrupt: |
121 | | sys.stderr.write("\nOperation cancelled.\n") |
| 121 | self.error("\nOperation cancelled.") |
122 | 122 | sys.exit(1) |
123 | 123 | |
124 | 124 | User.objects.db_manager(database).create_superuser(username, email, password) |
125 | 125 | if verbosity >= 1: |
126 | | self.stdout.write("Superuser created successfully.\n") |
127 | | |
| 126 | self.info("Superuser created successfully.") |
-
diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py
index cab96c8..499a57a 100644
a
|
b
|
Type 'yes' to continue, or 'no' to cancel: """
|
165 | 165 | |
166 | 166 | if self.verbosity >= 1: |
167 | 167 | template = ("\n%(modified_count)s %(identifier)s %(action)s" |
168 | | "%(destination)s%(unmodified)s%(post_processed)s.\n") |
| 168 | "%(destination)s%(unmodified)s%(post_processed)s.") |
169 | 169 | summary = template % { |
170 | 170 | 'modified_count': modified_count, |
171 | 171 | 'identifier': 'static file' + (modified_count != 1 and 's' or ''), |
… |
… |
Type 'yes' to continue, or 'no' to cancel: """
|
178 | 178 | ', %s post-processed' |
179 | 179 | % post_processed_count or ''), |
180 | 180 | } |
181 | | self.stdout.write(smart_str(summary)) |
| 181 | self.info(smart_str(summary)) |
182 | 182 | |
183 | 183 | def log(self, msg, level=2): |
184 | 184 | """ |
185 | 185 | Small log helper |
186 | 186 | """ |
187 | | msg = smart_str(msg) |
188 | | if not msg.endswith("\n"): |
189 | | msg += "\n" |
190 | 187 | if self.verbosity >= level: |
191 | | self.stdout.write(msg) |
| 188 | self.info(smart_str(msg)) |
192 | 189 | |
193 | 190 | def clear_dir(self, path): |
194 | 191 | """ |
-
diff --git a/django/contrib/staticfiles/management/commands/findstatic.py b/django/contrib/staticfiles/management/commands/findstatic.py
index bcf0c2f..77d23d7 100644
a
|
b
|
class Command(LabelCommand):
|
23 | 23 | result = [result] |
24 | 24 | output = u'\n '.join( |
25 | 25 | (smart_unicode(os.path.realpath(path)) for path in result)) |
26 | | self.stdout.write( |
27 | | smart_str(u"Found '%s' here:\n %s\n" % (path, output))) |
| 26 | self.info( |
| 27 | smart_str(u"Found '%s' here:\n %s" % (path, output))) |
28 | 28 | else: |
29 | 29 | if verbosity >= 1: |
30 | | self.stderr.write( |
31 | | smart_str("No matching file found for '%s'.\n" % path)) |
| 30 | self.error("No matching file found for '%s'." % path) |
-
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 0464eb2..d831858 100644
a
|
b
|
import warnings
|
8 | 8 | from django.core.management.base import BaseCommand, CommandError, handle_default_options |
9 | 9 | from django.core.management.color import color_style |
10 | 10 | from django.utils.importlib import import_module |
| 11 | from django.utils.log import getLogger |
11 | 12 | |
12 | 13 | # For backwards compatibility: get_version() used to be in this module. |
13 | 14 | from django import get_version |
… |
… |
from django import get_version
|
15 | 16 | # A cache of loaded commands, so that call_command |
16 | 17 | # doesn't have to reload every time it's called. |
17 | 18 | _commands = None |
| 19 | logger = getLogger('django.commands') |
18 | 20 | |
19 | 21 | def find_commands(management_dir): |
20 | 22 | """ |
… |
… |
def call_command(name, *args, **options):
|
139 | 141 | # when the script runs from the command line, but since call_command can |
140 | 142 | # be called programatically, we need to simulate the loading and handling |
141 | 143 | # of defaults (see #10080 for details). |
142 | | defaults = {} |
| 144 | defaults = {'raise_on_error': True} |
143 | 145 | for opt in klass.option_list: |
144 | 146 | if opt.default is NO_DEFAULT: |
145 | 147 | defaults[opt.dest] = None |
… |
… |
def call_command(name, *args, **options):
|
149 | 151 | |
150 | 152 | return klass.execute(*args, **defaults) |
151 | 153 | |
| 154 | |
152 | 155 | class LaxOptionParser(OptionParser): |
153 | 156 | """ |
154 | 157 | An option parser that doesn't raise any errors on unknown options. |
… |
… |
class ManagementUtility(object):
|
251 | 254 | try: |
252 | 255 | app_name = get_commands()[subcommand] |
253 | 256 | except KeyError: |
254 | | sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ |
| 257 | logger.error("Unknown command: %r\nType '%s help' for usage." % \ |
255 | 258 | (subcommand, self.prog_name)) |
256 | 259 | sys.exit(1) |
257 | 260 | if isinstance(app_name, BaseCommand): |
… |
… |
class ManagementUtility(object):
|
362 | 365 | if subcommand == 'help': |
363 | 366 | if len(args) <= 2: |
364 | 367 | parser.print_lax_help() |
365 | | sys.stdout.write(self.main_help_text() + '\n') |
| 368 | logger.info(self.main_help_text()) |
366 | 369 | elif args[2] == '--commands': |
367 | | sys.stdout.write(self.main_help_text(commands_only=True) + '\n') |
| 370 | logger.info(self.main_help_text(commands_only=True)) |
368 | 371 | else: |
369 | 372 | self.fetch_command(args[2]).print_help(self.prog_name, args[2]) |
370 | 373 | elif subcommand == 'version': |
371 | | sys.stdout.write(parser.get_version() + '\n') |
| 374 | logger.info(parser.get_version()) |
372 | 375 | # Special-cases: We want 'django-admin.py --version' and |
373 | 376 | # 'django-admin.py --help' to work, for backwards compatibility. |
374 | 377 | elif self.argv[1:] == ['--version']: |
… |
… |
class ManagementUtility(object):
|
376 | 379 | pass |
377 | 380 | elif self.argv[1:] in (['--help'], ['-h']): |
378 | 381 | parser.print_lax_help() |
379 | | sys.stdout.write(self.main_help_text() + '\n') |
| 382 | logger.info(self.main_help_text()) |
380 | 383 | else: |
381 | 384 | self.fetch_command(subcommand).run_from_argv(self.argv) |
382 | 385 | |
-
diff --git a/django/core/management/base.py b/django/core/management/base.py
index a936877..d91a2d3 100644
a
|
b
|
import sys
|
8 | 8 | |
9 | 9 | from optparse import make_option, OptionParser |
10 | 10 | import traceback |
| 11 | import warnings |
11 | 12 | |
12 | 13 | import django |
13 | 14 | from django.core.exceptions import ImproperlyConfigured |
14 | 15 | from django.core.management.color import color_style |
15 | 16 | from django.utils.encoding import smart_str |
| 17 | from django.utils.log import getLogger, setup_commands_logger |
16 | 18 | |
17 | 19 | |
18 | 20 | class CommandError(Exception): |
… |
… |
def handle_default_options(options):
|
44 | 46 | sys.path.insert(0, options.pythonpath) |
45 | 47 | |
46 | 48 | |
| 49 | class OutputRedirector(object): |
| 50 | """ |
| 51 | Utility redirector class used until self.stderr and self.sdtout are |
| 52 | removed from BaseCommand class (1.7) |
| 53 | """ |
| 54 | def __init__(self, proxy_func): |
| 55 | self.proxy_func = proxy_func |
| 56 | |
| 57 | def write(self, msg): |
| 58 | warnings.warn( |
| 59 | "The 'self.stdout' and 'self.stderr' objects are deprecated, " |
| 60 | "update your management command to use the new info() and error() " |
| 61 | "methods; please see the Django 1.5 release notes " |
| 62 | "(https://docs.djangoproject.com/en/dev/releases/1.5/).", |
| 63 | PendingDeprecationWarning) |
| 64 | self.proxy_func(msg) |
| 65 | |
| 66 | |
47 | 67 | class BaseCommand(object): |
48 | 68 | """ |
49 | 69 | The base class from which all management commands ultimately |
… |
… |
class BaseCommand(object):
|
141 | 161 | |
142 | 162 | def __init__(self): |
143 | 163 | self.style = color_style() |
| 164 | self._logger = getLogger('django.commands') |
144 | 165 | |
145 | 166 | def get_version(self): |
146 | 167 | """ |
… |
… |
class BaseCommand(object):
|
220 | 241 | if show_traceback: |
221 | 242 | traceback.print_exc() |
222 | 243 | else: |
223 | | sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) |
| 244 | self.error('Error: %s' % e) |
224 | 245 | sys.exit(1) |
225 | 246 | |
| 247 | if 'stdout' in options or 'stderr' in options: |
| 248 | # Set up a temporary logger |
| 249 | self._logger = setup_commands_logger( |
| 250 | 'django.commands_temp', |
| 251 | options.get('stdout', sys.stdout), |
| 252 | options.get('stderr', sys.stderr) |
| 253 | ) |
| 254 | # Temporarily redirect, until deprecation path is over (1.6) |
| 255 | self.stdout = OutputRedirector(self.info) |
| 256 | self.stderr = OutputRedirector(self.error) |
226 | 257 | try: |
227 | | self.stdout = options.get('stdout', sys.stdout) |
228 | | self.stderr = options.get('stderr', sys.stderr) |
229 | 258 | if self.requires_model_validation and not options.get('skip_validation'): |
230 | 259 | self.validate() |
231 | 260 | output = self.handle(*args, **options) |
… |
… |
class BaseCommand(object):
|
236 | 265 | from django.db import connections, DEFAULT_DB_ALIAS |
237 | 266 | connection = connections[options.get('database', DEFAULT_DB_ALIAS)] |
238 | 267 | if connection.ops.start_transaction_sql(): |
239 | | self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()) + '\n') |
240 | | self.stdout.write(output) |
241 | | if self.output_transaction: |
242 | | self.stdout.write('\n' + self.style.SQL_KEYWORD("COMMIT;") + '\n') |
| 268 | output = "%s\n%s" % ( |
| 269 | self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()), |
| 270 | output |
| 271 | ) |
| 272 | output = "%s\n%s" % (output, self.style.SQL_KEYWORD("COMMIT;")) |
| 273 | self.info(output) |
243 | 274 | except CommandError as e: |
244 | | if show_traceback: |
245 | | traceback.print_exc() |
| 275 | if options.get('raise_on_error', False): |
| 276 | raise |
246 | 277 | else: |
247 | | self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) |
248 | | sys.exit(1) |
| 278 | if show_traceback: |
| 279 | traceback.print_exc() |
| 280 | else: |
| 281 | self.error('Error: %s' % e) |
| 282 | sys.exit(1) |
249 | 283 | finally: |
| 284 | if self._logger.name == 'django.commands_temp': |
| 285 | for h in self._logger.handlers: |
| 286 | h.close() |
250 | 287 | if saved_lang is not None: |
251 | 288 | translation.activate(saved_lang) |
252 | 289 | |
… |
… |
class BaseCommand(object):
|
269 | 306 | error_text = s.read() |
270 | 307 | raise CommandError("One or more models did not validate:\n%s" % error_text) |
271 | 308 | if display_num_errors: |
272 | | self.stdout.write("%s error%s found\n" % (num_errors, num_errors != 1 and 's' or '')) |
| 309 | self.info("%s error%s found" % (num_errors, num_errors != 1 and 's' or '')) |
| 310 | |
| 311 | def info(self, msg): |
| 312 | """ Output a message on the logger (stdout by default) """ |
| 313 | self._logger.info(msg) |
| 314 | |
| 315 | def error(self, msg): |
| 316 | """ Output an error on the logger (stderr by default) """ |
| 317 | self._logger.error(smart_str(self.style.ERROR(msg))) |
273 | 318 | |
274 | 319 | def handle(self, *args, **options): |
275 | 320 | """ |
-
diff --git a/django/core/management/commands/compilemessages.py b/django/core/management/commands/compilemessages.py
index 354787e..b1e5f48 100644
a
|
b
|
def has_bom(fn):
|
11 | 11 | sample.startswith(codecs.BOM_UTF16_LE) or \ |
12 | 12 | sample.startswith(codecs.BOM_UTF16_BE) |
13 | 13 | |
14 | | def compile_messages(stderr, locale=None): |
| 14 | def compile_messages(logger, locale=None): |
15 | 15 | basedirs = [os.path.join('conf', 'locale'), 'locale'] |
16 | 16 | if os.environ.get('DJANGO_SETTINGS_MODULE'): |
17 | 17 | from django.conf import settings |
… |
… |
def compile_messages(stderr, locale=None):
|
29 | 29 | for dirpath, dirnames, filenames in os.walk(basedir): |
30 | 30 | for f in filenames: |
31 | 31 | if f.endswith('.po'): |
32 | | stderr.write('processing file %s in %s\n' % (f, dirpath)) |
| 32 | logger.info('processing file %s in %s' % (f, dirpath)) |
33 | 33 | fn = os.path.join(dirpath, f) |
34 | 34 | if has_bom(fn): |
35 | 35 | raise CommandError("The %s file has a BOM (Byte Order Mark). Django only supports .po files encoded in UTF-8 and without any BOM." % fn) |
… |
… |
class Command(BaseCommand):
|
60 | 60 | |
61 | 61 | def handle(self, **options): |
62 | 62 | locale = options.get('locale') |
63 | | compile_messages(self.stderr, locale=locale) |
| 63 | compile_messages(self._logger, locale=locale) |
-
diff --git a/django/core/management/commands/createcachetable.py b/django/core/management/commands/createcachetable.py
index 82a126b..403722a 100644
a
|
b
|
|
1 | 1 | from optparse import make_option |
2 | 2 | |
3 | 3 | from django.core.cache.backends.db import BaseDatabaseCache |
4 | | from django.core.management.base import LabelCommand |
| 4 | from django.core.management.base import LabelCommand, CommandError |
5 | 5 | from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS |
6 | 6 | from django.db.utils import DatabaseError |
7 | 7 | |
… |
… |
class Command(LabelCommand):
|
55 | 55 | try: |
56 | 56 | curs.execute("\n".join(full_statement)) |
57 | 57 | except DatabaseError as e: |
58 | | self.stderr.write( |
59 | | self.style.ERROR("Cache table '%s' could not be created.\nThe error was: %s.\n" % |
60 | | (tablename, e))) |
61 | 58 | transaction.rollback_unless_managed(using=db) |
| 59 | raise CommandError("Cache table '%s' could not be created.\nThe error was: %s." % ( |
| 60 | tablename, e)) |
62 | 61 | else: |
63 | 62 | for statement in index_output: |
64 | 63 | curs.execute(statement) |
-
diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py
index ce3c6e8..a6c1a8b 100644
a
|
b
|
class Command(NoArgsCommand):
|
28 | 28 | verbosity = int(options.get('verbosity')) |
29 | 29 | interactive = options.get('interactive') |
30 | 30 | |
31 | | self.style = no_style() |
32 | | |
33 | 31 | # Import the 'management' module within each installed app, to register |
34 | 32 | # dispatcher events. |
35 | 33 | for app_name in settings.INSTALLED_APPS: |
… |
… |
class Command(NoArgsCommand):
|
38 | 36 | except ImportError: |
39 | 37 | pass |
40 | 38 | |
41 | | sql_list = sql_flush(self.style, connection, only_django=True) |
| 39 | sql_list = sql_flush(no_style(), connection, only_django=True) |
42 | 40 | |
43 | 41 | if interactive: |
44 | 42 | confirm = raw_input("""You have requested a flush of the database. |
… |
… |
The full error: %s""" % (connection.settings_dict['NAME'], e))
|
82 | 80 | call_command('loaddata', 'initial_data', **kwargs) |
83 | 81 | |
84 | 82 | else: |
85 | | self.stdout.write("Flush cancelled.\n") |
| 83 | return "Flush cancelled." |
-
diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py
index 10fd9bd..1220186 100644
a
|
b
|
class Command(NoArgsCommand):
|
20 | 20 | def handle_noargs(self, **options): |
21 | 21 | try: |
22 | 22 | for line in self.handle_inspection(options): |
23 | | self.stdout.write("%s\n" % line) |
| 23 | self.info(line) |
24 | 24 | except NotImplementedError: |
25 | 25 | raise CommandError("Database inspection isn't supported for the currently selected database backend.") |
26 | 26 | |
-
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index 848b637..05bc211 100644
a
|
b
|
import traceback
|
7 | 7 | |
8 | 8 | from django.conf import settings |
9 | 9 | from django.core import serializers |
10 | | from django.core.management.base import BaseCommand |
| 10 | from django.core.management.base import BaseCommand, CommandError |
11 | 11 | from django.core.management.color import no_style |
12 | 12 | from django.db import (connections, router, transaction, DEFAULT_DB_ALIAS, |
13 | 13 | IntegrityError, DatabaseError) |
… |
… |
try:
|
20 | 20 | except ImportError: |
21 | 21 | has_bz2 = False |
22 | 22 | |
| 23 | |
23 | 24 | class Command(BaseCommand): |
24 | 25 | help = 'Installs the named fixture(s) in the database.' |
25 | 26 | args = "fixture [fixture ...]" |
… |
… |
class Command(BaseCommand):
|
34 | 35 | using = options.get('database') |
35 | 36 | |
36 | 37 | connection = connections[using] |
37 | | self.style = no_style() |
38 | 38 | |
39 | 39 | if not len(fixture_labels): |
40 | | self.stderr.write( |
41 | | self.style.ERROR("No database fixture specified. Please provide the path of at least one fixture in the command line.\n") |
42 | | ) |
43 | | return |
| 40 | raise CommandError("No database fixture specified. " |
| 41 | "Please provide the path of at least one fixture in the command line.") |
44 | 42 | |
45 | 43 | verbosity = int(options.get('verbosity')) |
46 | 44 | show_traceback = options.get('traceback') |
… |
… |
class Command(BaseCommand):
|
124 | 122 | |
125 | 123 | if formats: |
126 | 124 | if verbosity >= 2: |
127 | | self.stdout.write("Loading '%s' fixtures...\n" % fixture_name) |
| 125 | self.info("Loading '%s' fixtures..." % fixture_name) |
128 | 126 | else: |
129 | | self.stderr.write( |
130 | | self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" % |
131 | | (fixture_name, format))) |
132 | | if commit: |
133 | | transaction.rollback(using=using) |
134 | | transaction.leave_transaction_management(using=using) |
135 | | return |
| 127 | raise CommandError("Problem installing fixture '%s': %s is not a known serialization format." % |
| 128 | (fixture_name, format)) |
136 | 129 | |
137 | 130 | if os.path.isabs(fixture_name): |
138 | 131 | fixture_dirs = [fixture_name] |
… |
… |
class Command(BaseCommand):
|
141 | 134 | |
142 | 135 | for fixture_dir in fixture_dirs: |
143 | 136 | if verbosity >= 2: |
144 | | self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir)) |
| 137 | self.info("Checking %s for fixtures..." % humanize(fixture_dir)) |
145 | 138 | |
146 | 139 | label_found = False |
147 | 140 | for combo in product([using, None], formats, compression_formats): |
… |
… |
class Command(BaseCommand):
|
154 | 147 | ) |
155 | 148 | |
156 | 149 | if verbosity >= 3: |
157 | | self.stdout.write("Trying %s for %s fixture '%s'...\n" % \ |
158 | | (humanize(fixture_dir), file_name, fixture_name)) |
| 150 | self.info("Trying %s for %s fixture '%s'..." % ( |
| 151 | humanize(fixture_dir), file_name, fixture_name)) |
159 | 152 | full_path = os.path.join(fixture_dir, file_name) |
160 | 153 | open_method = compression_types[compression_format] |
161 | 154 | try: |
162 | 155 | fixture = open_method(full_path, 'r') |
163 | 156 | except IOError: |
164 | 157 | if verbosity >= 2: |
165 | | self.stdout.write("No %s fixture '%s' in %s.\n" % \ |
166 | | (format, fixture_name, humanize(fixture_dir))) |
| 158 | self.info("No %s fixture '%s' in %s." % ( |
| 159 | format, fixture_name, humanize(fixture_dir))) |
167 | 160 | else: |
168 | 161 | try: |
169 | 162 | if label_found: |
170 | | self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" % |
171 | | (fixture_name, humanize(fixture_dir)))) |
172 | | if commit: |
173 | | transaction.rollback(using=using) |
174 | | transaction.leave_transaction_management(using=using) |
175 | | return |
| 163 | raise CommandError("Multiple fixtures named '%s' in %s. Aborting." % |
| 164 | (fixture_name, humanize(fixture_dir))) |
176 | 165 | |
177 | 166 | fixture_count += 1 |
178 | 167 | objects_in_fixture = 0 |
179 | 168 | loaded_objects_in_fixture = 0 |
180 | 169 | if verbosity >= 2: |
181 | | self.stdout.write("Installing %s fixture '%s' from %s.\n" % \ |
182 | | (format, fixture_name, humanize(fixture_dir))) |
| 170 | self.info("Installing %s fixture '%s' from %s." % ( |
| 171 | format, fixture_name, humanize(fixture_dir))) |
183 | 172 | |
184 | 173 | objects = serializers.deserialize(format, fixture, using=using) |
185 | 174 | |
… |
… |
class Command(BaseCommand):
|
208 | 197 | # If the fixture we loaded contains 0 objects, assume that an |
209 | 198 | # error was encountered during fixture loading. |
210 | 199 | if objects_in_fixture == 0: |
211 | | self.stderr.write( |
212 | | self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" % |
213 | | (fixture_name))) |
214 | | if commit: |
215 | | transaction.rollback(using=using) |
216 | | transaction.leave_transaction_management(using=using) |
217 | | return |
| 200 | raise CommandError("No fixture data found for '%s'. (File format may be invalid.)" % |
| 201 | (fixture_name)) |
218 | 202 | |
219 | 203 | # Since we disabled constraint checks, we must manually check for |
220 | 204 | # any invalid keys that might have been added |
… |
… |
class Command(BaseCommand):
|
223 | 207 | |
224 | 208 | except (SystemExit, KeyboardInterrupt): |
225 | 209 | raise |
| 210 | except CommandError: |
| 211 | if commit: |
| 212 | transaction.rollback(using=using) |
| 213 | transaction.leave_transaction_management(using=using) |
| 214 | raise |
226 | 215 | except Exception: |
227 | 216 | if commit: |
228 | 217 | transaction.rollback(using=using) |
… |
… |
class Command(BaseCommand):
|
230 | 219 | if show_traceback: |
231 | 220 | traceback.print_exc() |
232 | 221 | else: |
233 | | self.stderr.write( |
234 | | self.style.ERROR("Problem installing fixture '%s': %s\n" % |
235 | | (full_path, ''.join(traceback.format_exception(sys.exc_type, |
236 | | sys.exc_value, sys.exc_traceback))))) |
| 222 | self.error("Problem installing fixture '%s': %s" % ( |
| 223 | full_path, ''.join(traceback.format_exception(sys.exc_type, |
| 224 | sys.exc_value, sys.exc_traceback)))) |
237 | 225 | return |
238 | 226 | |
239 | 227 | |
240 | 228 | # If we found even one object in a fixture, we need to reset the |
241 | 229 | # database sequences. |
242 | 230 | if loaded_object_count > 0: |
243 | | sequence_sql = connection.ops.sequence_reset_sql(self.style, models) |
| 231 | sequence_sql = connection.ops.sequence_reset_sql(no_style(), models) |
244 | 232 | if sequence_sql: |
245 | 233 | if verbosity >= 2: |
246 | | self.stdout.write("Resetting sequences\n") |
| 234 | self.info("Resetting sequences") |
247 | 235 | for line in sequence_sql: |
248 | 236 | cursor.execute(line) |
249 | 237 | |
… |
… |
class Command(BaseCommand):
|
253 | 241 | |
254 | 242 | if verbosity >= 1: |
255 | 243 | if fixture_object_count == loaded_object_count: |
256 | | self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % ( |
| 244 | self.info("Installed %d object(s) from %d fixture(s)" % ( |
257 | 245 | loaded_object_count, fixture_count)) |
258 | 246 | else: |
259 | | self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)\n" % ( |
| 247 | self.info("Installed %d object(s) (of %d) from %d fixture(s)" % ( |
260 | 248 | loaded_object_count, fixture_object_count, fixture_count)) |
261 | 249 | |
262 | 250 | # Close the DB connection. This is required as a workaround for an |
-
diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index 046ffb4..bba43d3 100644
a
|
b
|
def _popen(cmd):
|
46 | 46 | return p.communicate() |
47 | 47 | |
48 | 48 | def walk(root, topdown=True, onerror=None, followlinks=False, |
49 | | ignore_patterns=None, verbosity=0, stdout=sys.stdout): |
| 49 | ignore_patterns=None, verbosity=0, logger=None): |
50 | 50 | """ |
51 | 51 | A version of os.walk that can follow symlinks for Python < 2.6 |
52 | 52 | """ |
… |
… |
def walk(root, topdown=True, onerror=None, followlinks=False,
|
63 | 63 | for dirname in remove_dirs: |
64 | 64 | dirnames.remove(dirname) |
65 | 65 | if verbosity > 1: |
66 | | stdout.write('ignoring directory %s\n' % dirname) |
| 66 | logger.info('ignoring directory %s' % dirname) |
67 | 67 | yield (dirpath, dirnames, filenames) |
68 | 68 | if followlinks: |
69 | 69 | for d in dirnames: |
… |
… |
def is_ignored(path, ignore_patterns):
|
81 | 81 | return True |
82 | 82 | return False |
83 | 83 | |
84 | | def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False): |
| 84 | def find_files(root, ignore_patterns, verbosity, logger, symlinks=False): |
85 | 85 | """ |
86 | 86 | Helper function to get all files in the given root. |
87 | 87 | """ |
88 | 88 | all_files = [] |
89 | 89 | for (dirpath, dirnames, filenames) in walk(root, followlinks=symlinks, |
90 | | ignore_patterns=ignore_patterns, verbosity=verbosity, stdout=stdout): |
| 90 | ignore_patterns=ignore_patterns, verbosity=verbosity, logger=logger): |
91 | 91 | for filename in filenames: |
92 | 92 | norm_filepath = os.path.normpath(os.path.join(dirpath, filename)) |
93 | 93 | if is_ignored(norm_filepath, ignore_patterns): |
94 | 94 | if verbosity > 1: |
95 | | stdout.write('ignoring file %s in %s\n' % (filename, dirpath)) |
| 95 | logger.info('ignoring file %s in %s' % (filename, dirpath)) |
96 | 96 | else: |
97 | 97 | all_files.extend([(dirpath, filename)]) |
98 | 98 | all_files.sort() |
99 | 99 | return all_files |
100 | 100 | |
101 | | def copy_plural_forms(msgs, locale, domain, verbosity, stdout=sys.stdout): |
| 101 | def copy_plural_forms(msgs, locale, domain, verbosity, logger): |
102 | 102 | """ |
103 | 103 | Copies plural forms header contents from a Django catalog of locale to |
104 | 104 | the msgs string, inserting it at the right place. msgs should be the |
… |
… |
def copy_plural_forms(msgs, locale, domain, verbosity, stdout=sys.stdout):
|
116 | 116 | m = plural_forms_re.search(fp.read()) |
117 | 117 | if m: |
118 | 118 | if verbosity > 1: |
119 | | stdout.write("copying plural forms: %s\n" % m.group('value')) |
| 119 | logger.info("copying plural forms: %s" % m.group('value')) |
120 | 120 | lines = [] |
121 | 121 | seen = False |
122 | 122 | for line in msgs.split('\n'): |
… |
… |
def write_pot_file(potfile, msgs, file, work_file, is_templatized):
|
146 | 146 | fp.write(msgs) |
147 | 147 | |
148 | 148 | def process_file(file, dirpath, potfile, domain, verbosity, |
149 | | extensions, wrap, location, stdout=sys.stdout): |
| 149 | extensions, wrap, location, logger): |
150 | 150 | """ |
151 | 151 | Extract translatable literals from :param file: for :param domain: |
152 | 152 | creating or updating the :param potfile: POT file. |
… |
… |
def process_file(file, dirpath, potfile, domain, verbosity,
|
157 | 157 | from django.utils.translation import templatize |
158 | 158 | |
159 | 159 | if verbosity > 1: |
160 | | stdout.write('processing file %s in %s\n' % (file, dirpath)) |
| 160 | logger.info('processing file %s in %s' % (file, dirpath)) |
161 | 161 | _, file_ext = os.path.splitext(file) |
162 | 162 | if domain == 'djangojs' and file_ext in extensions: |
163 | 163 | is_templatized = True |
… |
… |
def process_file(file, dirpath, potfile, domain, verbosity,
|
212 | 212 | if is_templatized: |
213 | 213 | os.unlink(work_file) |
214 | 214 | |
215 | | def write_po_file(pofile, potfile, domain, locale, verbosity, stdout, |
| 215 | def write_po_file(pofile, potfile, domain, locale, verbosity, logger, |
216 | 216 | copy_pforms, wrap, location, no_obsolete): |
217 | 217 | """ |
218 | 218 | Creates of updates the :param pofile: PO file for :param domain: and :param |
… |
… |
def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
|
235 | 235 | raise CommandError( |
236 | 236 | "errors happened while running msgmerge\n%s" % errors) |
237 | 237 | elif copy_pforms: |
238 | | msgs = copy_plural_forms(msgs, locale, domain, verbosity, stdout) |
| 238 | msgs = copy_plural_forms(msgs, locale, domain, verbosity, logger) |
239 | 239 | msgs = msgs.replace( |
240 | 240 | "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "") |
241 | 241 | with open(pofile, 'wb') as fp: |
… |
… |
def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
|
250 | 250 | |
251 | 251 | def make_messages(locale=None, domain='django', verbosity=1, all=False, |
252 | 252 | extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False, |
253 | | no_location=False, no_obsolete=False, stdout=sys.stdout): |
| 253 | no_location=False, no_obsolete=False, logger=None): |
254 | 254 | """ |
255 | 255 | Uses the ``locale/`` directory from the Django Git tree or an |
256 | 256 | application/project to process all files with translatable literals for |
… |
… |
def make_messages(locale=None, domain='django', verbosity=1, all=False,
|
312 | 312 | |
313 | 313 | for locale in locales: |
314 | 314 | if verbosity > 0: |
315 | | stdout.write("processing language %s\n" % locale) |
| 315 | logger.info("processing language %s" % locale) |
316 | 316 | basedir = os.path.join(localedir, locale, 'LC_MESSAGES') |
317 | 317 | if not os.path.isdir(basedir): |
318 | 318 | os.makedirs(basedir) |
… |
… |
def make_messages(locale=None, domain='django', verbosity=1, all=False,
|
324 | 324 | os.unlink(potfile) |
325 | 325 | |
326 | 326 | for dirpath, file in find_files(".", ignore_patterns, verbosity, |
327 | | stdout, symlinks=symlinks): |
| 327 | logger, symlinks=symlinks): |
328 | 328 | process_file(file, dirpath, potfile, domain, verbosity, extensions, |
329 | | wrap, location, stdout) |
| 329 | wrap, location, logger) |
330 | 330 | |
331 | 331 | if os.path.exists(potfile): |
332 | | write_po_file(pofile, potfile, domain, locale, verbosity, stdout, |
| 332 | write_po_file(pofile, potfile, domain, locale, verbosity, logger, |
333 | 333 | not invoked_for_django, wrap, location, no_obsolete) |
334 | 334 | |
335 | 335 | |
… |
… |
class Command(NoArgsCommand):
|
387 | 387 | extensions = handle_extensions(exts) |
388 | 388 | |
389 | 389 | if verbosity > 1: |
390 | | self.stdout.write('examining files with the extensions: %s\n' |
391 | | % get_text_list(list(extensions), 'and')) |
| 390 | self.info('examining files with the extensions: %s' |
| 391 | % get_text_list(list(extensions), 'and')) |
392 | 392 | |
393 | 393 | make_messages(locale, domain, verbosity, process_all, extensions, |
394 | | symlinks, ignore_patterns, no_wrap, no_location, no_obsolete, self.stdout) |
| 394 | symlinks, ignore_patterns, no_wrap, no_location, no_obsolete, self._logger) |
-
diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py
index e618219..f4b2bf1 100644
a
|
b
|
class Command(BaseCommand):
|
87 | 87 | shutdown_message = options.get('shutdown_message', '') |
88 | 88 | quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' |
89 | 89 | |
90 | | self.stdout.write("Validating models...\n\n") |
| 90 | self.info("Validating models...\n") |
91 | 91 | self.validate(display_num_errors=True) |
92 | | self.stdout.write(( |
| 92 | self.info( |
93 | 93 | "Django version %(version)s, using settings %(settings)r\n" |
94 | 94 | "Development server is running at http://%(addr)s:%(port)s/\n" |
95 | | "Quit the server with %(quit_command)s.\n" |
96 | | ) % { |
97 | | "version": self.get_version(), |
98 | | "settings": settings.SETTINGS_MODULE, |
99 | | "addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr, |
100 | | "port": self.port, |
101 | | "quit_command": quit_command, |
102 | | }) |
| 95 | "Quit the server with %(quit_command)s.\n" % { |
| 96 | "version": self.get_version(), |
| 97 | "settings": settings.SETTINGS_MODULE, |
| 98 | "addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr, |
| 99 | "port": self.port, |
| 100 | "quit_command": quit_command, |
| 101 | } |
| 102 | ) |
103 | 103 | # django.core.management.base forces the locale to en-us. We should |
104 | 104 | # set it up correctly for the first request (particularly important |
105 | 105 | # in the "--noreload" case). |
… |
… |
class Command(BaseCommand):
|
120 | 120 | error_text = ERRORS[e.args[0].args[0]] |
121 | 121 | except (AttributeError, KeyError): |
122 | 122 | error_text = str(e) |
123 | | sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n') |
| 123 | self.error("Error: %s" % error_text) |
124 | 124 | # Need to use an OS exit because sys.exit doesn't work in a thread |
125 | 125 | os._exit(1) |
126 | 126 | except KeyboardInterrupt: |
127 | 127 | if shutdown_message: |
128 | | self.stdout.write("%s\n" % shutdown_message) |
| 128 | self.info(shutdown_message) |
129 | 129 | sys.exit(0) |
130 | 130 | |
131 | 131 | |
-
diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py
index 88caea1..c7d172a 100644
a
|
b
|
class Command(NoArgsCommand):
|
81 | 81 | |
82 | 82 | # Create the tables for each model |
83 | 83 | if verbosity >= 1: |
84 | | self.stdout.write("Creating tables ...\n") |
| 84 | self.info("Creating tables ...") |
85 | 85 | for app_name, model_list in manifest.items(): |
86 | 86 | for model in model_list: |
87 | 87 | # Create the model's database table, if it doesn't already exist. |
88 | 88 | if verbosity >= 3: |
89 | | self.stdout.write("Processing %s.%s model\n" % (app_name, model._meta.object_name)) |
| 89 | self.info("Processing %s.%s model" % (app_name, model._meta.object_name)) |
90 | 90 | sql, references = connection.creation.sql_create_model(model, self.style, seen_models) |
91 | 91 | seen_models.add(model) |
92 | 92 | created_models.add(model) |
… |
… |
class Command(NoArgsCommand):
|
96 | 96 | sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references)) |
97 | 97 | sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references)) |
98 | 98 | if verbosity >= 1 and sql: |
99 | | self.stdout.write("Creating table %s\n" % model._meta.db_table) |
| 99 | self.info("Creating table %s" % model._meta.db_table) |
100 | 100 | for statement in sql: |
101 | 101 | cursor.execute(statement) |
102 | 102 | tables.append(connection.introspection.table_name_converter(model._meta.db_table)) |
… |
… |
class Command(NoArgsCommand):
|
114 | 114 | # Install custom SQL for the app (but only if this |
115 | 115 | # is a model we've just created) |
116 | 116 | if verbosity >= 1: |
117 | | self.stdout.write("Installing custom SQL ...\n") |
| 117 | self.info("Installing custom SQL ...") |
118 | 118 | for app_name, model_list in manifest.items(): |
119 | 119 | for model in model_list: |
120 | 120 | if model in created_models: |
121 | 121 | custom_sql = custom_sql_for_model(model, self.style, connection) |
122 | 122 | if custom_sql: |
123 | 123 | if verbosity >= 2: |
124 | | self.stdout.write("Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) |
| 124 | self.info("Installing custom SQL for %s.%s model" % ( |
| 125 | app_name, model._meta.object_name)) |
125 | 126 | try: |
126 | 127 | for sql in custom_sql: |
127 | 128 | cursor.execute(sql) |
128 | 129 | except Exception as e: |
129 | | self.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ |
130 | | (app_name, model._meta.object_name, e)) |
| 130 | self.error("Failed to install custom SQL for %s.%s model: %s" % ( |
| 131 | app_name, model._meta.object_name, e)) |
131 | 132 | if show_traceback: |
132 | 133 | traceback.print_exc() |
133 | 134 | transaction.rollback_unless_managed(using=db) |
… |
… |
class Command(NoArgsCommand):
|
135 | 136 | transaction.commit_unless_managed(using=db) |
136 | 137 | else: |
137 | 138 | if verbosity >= 3: |
138 | | self.stdout.write("No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) |
| 139 | self.info("No custom SQL for %s.%s model" % ( |
| 140 | app_name, model._meta.object_name)) |
139 | 141 | |
140 | 142 | if verbosity >= 1: |
141 | | self.stdout.write("Installing indexes ...\n") |
| 143 | self.info("Installing indexes ...") |
142 | 144 | # Install SQL indices for all newly created models |
143 | 145 | for app_name, model_list in manifest.items(): |
144 | 146 | for model in model_list: |
… |
… |
class Command(NoArgsCommand):
|
146 | 148 | index_sql = connection.creation.sql_indexes_for_model(model, self.style) |
147 | 149 | if index_sql: |
148 | 150 | if verbosity >= 2: |
149 | | self.stdout.write("Installing index for %s.%s model\n" % (app_name, model._meta.object_name)) |
| 151 | self.info("Installing index for %s.%s model" % ( |
| 152 | app_name, model._meta.object_name)) |
150 | 153 | try: |
151 | 154 | for sql in index_sql: |
152 | 155 | cursor.execute(sql) |
153 | 156 | except Exception as e: |
154 | | self.stderr.write("Failed to install index for %s.%s model: %s\n" % \ |
155 | | (app_name, model._meta.object_name, e)) |
| 157 | self.error("Failed to install index for %s.%s model: %s" % ( |
| 158 | app_name, model._meta.object_name, e)) |
156 | 159 | transaction.rollback_unless_managed(using=db) |
157 | 160 | else: |
158 | 161 | transaction.commit_unless_managed(using=db) |
-
diff --git a/django/core/management/templates.py b/django/core/management/templates.py
index 735e29a..6955a7b 100644
a
|
b
|
class TemplateCommand(BaseCommand):
|
99 | 99 | for file in options.get('files'): |
100 | 100 | extra_files.extend(map(lambda x: x.strip(), file.split(','))) |
101 | 101 | if self.verbosity >= 2: |
102 | | self.stdout.write("Rendering %s template files with " |
103 | | "extensions: %s\n" % |
104 | | (app_or_project, ', '.join(extensions))) |
105 | | self.stdout.write("Rendering %s template files with " |
106 | | "filenames: %s\n" % |
107 | | (app_or_project, ', '.join(extra_files))) |
| 102 | self.info("Rendering %s template files with extensions: %s" % |
| 103 | (app_or_project, ', '.join(extensions))) |
| 104 | self.info("Rendering %s template files with filenames: %s" % |
| 105 | (app_or_project, ', '.join(extra_files))) |
108 | 106 | |
109 | 107 | base_name = '%s_name' % app_or_project |
110 | 108 | base_subdir = '%s_template' % app_or_project |
… |
… |
class TemplateCommand(BaseCommand):
|
161 | 159 | new_file.write(content) |
162 | 160 | |
163 | 161 | if self.verbosity >= 2: |
164 | | self.stdout.write("Creating %s\n" % new_path) |
| 162 | self.info("Creating %s" % new_path) |
165 | 163 | try: |
166 | 164 | shutil.copymode(old_path, new_path) |
167 | 165 | self.make_writeable(new_path) |
… |
… |
class TemplateCommand(BaseCommand):
|
169 | 167 | notice = self.style.NOTICE( |
170 | 168 | "Notice: Couldn't set permission bits on %s. You're " |
171 | 169 | "probably using an uncommon filesystem setup. No " |
172 | | "problem.\n" % new_path) |
173 | | sys.stderr.write(smart_str(notice)) |
| 170 | "problem." % new_path) |
| 171 | self.error(notice) |
174 | 172 | |
175 | 173 | if self.paths_to_remove: |
176 | 174 | if self.verbosity >= 2: |
177 | | self.stdout.write("Cleaning up temporary files.\n") |
| 175 | self.info("Cleaning up temporary files.") |
178 | 176 | for path_to_remove in self.paths_to_remove: |
179 | 177 | if path.isfile(path_to_remove): |
180 | 178 | os.remove(path_to_remove) |
… |
… |
class TemplateCommand(BaseCommand):
|
227 | 225 | filename, display_url = cleanup_url(url) |
228 | 226 | |
229 | 227 | if self.verbosity >= 2: |
230 | | self.stdout.write("Downloading %s\n" % display_url) |
| 228 | self.info("Downloading %s" % display_url) |
231 | 229 | try: |
232 | 230 | the_path, info = urllib.urlretrieve(url, |
233 | 231 | path.join(tempdir, filename)) |
… |
… |
class TemplateCommand(BaseCommand):
|
282 | 280 | tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_extract') |
283 | 281 | self.paths_to_remove.append(tempdir) |
284 | 282 | if self.verbosity >= 2: |
285 | | self.stdout.write("Extracting %s\n" % filename) |
| 283 | self.info("Extracting %s" % filename) |
286 | 284 | try: |
287 | 285 | archive.extract(filename, tempdir) |
288 | 286 | return tempdir |
-
diff --git a/django/utils/log.py b/django/utils/log.py
index df2089f..6ffa871 100644
a
|
b
|
|
1 | 1 | import logging |
| 2 | import sys |
2 | 3 | import traceback |
3 | 4 | |
4 | 5 | from django.conf import settings |
… |
… |
try:
|
21 | 22 | except ImportError: |
22 | 23 | from django.utils.dictconfig import dictConfig |
23 | 24 | |
24 | | getLogger = logging.getLogger |
25 | | |
26 | | # Ensure the creation of the Django logger |
27 | | # with a null handler. This ensures we don't get any |
28 | | # 'No handlers could be found for logger "django"' messages |
29 | | logger = getLogger('django') |
30 | | if not logger.handlers: |
31 | | logger.addHandler(NullHandler()) |
32 | | |
33 | 25 | |
34 | 26 | class AdminEmailHandler(logging.Handler): |
35 | 27 | """An exception log handler that emails log entries to site admins. |
… |
… |
class CallbackFilter(logging.Filter):
|
103 | 95 | class RequireDebugFalse(logging.Filter): |
104 | 96 | def filter(self, record): |
105 | 97 | return not settings.DEBUG |
| 98 | |
| 99 | |
| 100 | class SingleLevelFilter(logging.Filter): |
| 101 | def __init__(self, passlevel, reject): |
| 102 | self.passlevel = passlevel |
| 103 | self.reject = reject |
| 104 | |
| 105 | def filter(self, record): |
| 106 | if self.reject: |
| 107 | return (record.levelno != self.passlevel) |
| 108 | else: |
| 109 | return (record.levelno == self.passlevel) |
| 110 | |
| 111 | |
| 112 | def setup_commands_logger(name, stdout, stderr): |
| 113 | logger = getLogger(name) |
| 114 | if not logger.handlers or name.endswith('temp'): |
| 115 | logger.setLevel(logging.DEBUG) |
| 116 | logger.handlers = [] |
| 117 | handler_out = logging.StreamHandler(stdout) |
| 118 | handler_out.addFilter(SingleLevelFilter(logging.INFO, False)) |
| 119 | handler_err = logging.StreamHandler(stderr) |
| 120 | handler_err.addFilter(SingleLevelFilter(logging.INFO, True)) |
| 121 | logger.addHandler(handler_out) |
| 122 | logger.addHandler(handler_err) |
| 123 | return logger |
| 124 | |
| 125 | getLogger = logging.getLogger |
| 126 | |
| 127 | # Ensure the creation of the Django logger |
| 128 | # with a null handler. This ensures we don't get any |
| 129 | # 'No handlers could be found for logger "django"' messages |
| 130 | logger = getLogger('django') |
| 131 | if not logger.handlers: |
| 132 | logger.addHandler(NullHandler()) |
| 133 | |
| 134 | setup_commands_logger('django.commands', sys.stdout, sys.stderr) |
-
diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt
index ba8765b..30c57e5 100644
a
|
b
|
look like this:
|
61 | 61 | poll.opened = False |
62 | 62 | poll.save() |
63 | 63 | |
64 | | self.stdout.write('Successfully closed poll "%s"\n' % poll_id) |
| 64 | self.info('Successfully closed poll "%s"' % poll_id) |
65 | 65 | |
66 | 66 | .. note:: |
67 | 67 | When you are using management commands and wish to provide console |
68 | | output, you should write to ``self.stdout`` and ``self.stderr``, |
69 | | instead of printing to ``stdout`` and ``stderr`` directly. By |
70 | | using these proxies, it becomes much easier to test your custom |
71 | | command. |
| 68 | output, you should use the :meth:`~BaseCommand.info` or |
| 69 | :meth:`~BaseCommand.error` methods, instead of printing to ``stdout`` |
| 70 | and ``stderr`` directly. By using these proxies, it becomes much easier |
| 71 | to test your custom command or redirect output when needed. |
72 | 72 | |
73 | 73 | The new custom command can be called using ``python manage.py closepoll |
74 | 74 | <poll_id>``. |
… |
… |
the :meth:`~BaseCommand.handle` method must be implemented.
|
261 | 261 | |
262 | 262 | The actual logic of the command. Subclasses must implement this method. |
263 | 263 | |
| 264 | .. method:: BaseCommand.info(message) |
| 265 | |
| 266 | Output a message on the console (stdout), unless the default |
| 267 | 'django.commands' logger has been customized. |
| 268 | |
| 269 | .. method:: BaseCommand.error(message) |
| 270 | |
| 271 | Output an error message on the console (stderr), unless the default |
| 272 | 'django.commands' logger has been customized. |
| 273 | |
264 | 274 | .. _ref-basecommand-subclasses: |
265 | 275 | |
266 | 276 | BaseCommand subclasses |
-
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index f3ce5f8..7ddc368 100644
a
|
b
|
these changes.
|
276 | 276 | * The function ``django.utils.itercompat.product`` will be removed. The Python |
277 | 277 | builtin version should be used instead. |
278 | 278 | |
| 279 | * The self.stdout and self.stderr attributes of management BaseCommand will be |
| 280 | removed. They are replaced by the info() and error() methods, added in 1.5. |
| 281 | |
279 | 282 | 2.0 |
280 | 283 | --- |
281 | 284 | |
-
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 51766dc..6425a03 100644
a
|
b
|
Django 1.4 also includes several smaller improvements worth noting:
|
670 | 670 | useful in tests that don't need to hit a database. See |
671 | 671 | :ref:`testcase_hierarchy_diagram`. |
672 | 672 | |
| 673 | |
673 | 674 | Backwards incompatible changes in 1.4 |
674 | 675 | ===================================== |
675 | 676 | |
-
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index 5a92e7d..f86ad12 100644
a
|
b
|
Django 1.5 also includes several smaller improvements worth noting:
|
44 | 44 | * :mod:`django.utils.timezone` provides a helper for converting aware |
45 | 45 | datetimes between time zones. See :func:`~django.utils.timezone.localtime`. |
46 | 46 | |
| 47 | * Management commands have now ``info()`` and ``error()`` methods for console |
| 48 | output. Output directed to those methods can also be configured by customizing |
| 49 | the 'django.commands' logger. |
| 50 | |
47 | 51 | Backwards incompatible changes in 1.5 |
48 | 52 | ===================================== |
49 | 53 | |
-
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index aa2afba..4ee2d9f 100644
a
|
b
|
For performance reasons, SQL logging is only enabled when
|
455 | 455 | ``settings.DEBUG`` is set to ``True``, regardless of the logging |
456 | 456 | level or handlers that are installed. |
457 | 457 | |
| 458 | ``django.commands`` |
| 459 | ~~~~~~~~~~~~~~~~~~~ |
| 460 | |
| 461 | Messages related to management commands. By default, this logger outputs |
| 462 | message on standard output (console). |
| 463 | |
458 | 464 | Handlers |
459 | 465 | -------- |
460 | 466 | |
-
diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
index d22010d..7174cea 100644
a
|
b
|
class FixtureLoadingTests(TestCase):
|
185 | 185 | exclude_list=['fixtures.Article', 'fixtures.Book', 'sites']) |
186 | 186 | |
187 | 187 | # Excluding a bogus app should throw an error |
188 | | self.assertRaises(SystemExit, |
189 | | self._dumpdata_assert, |
190 | | ['fixtures', 'sites'], |
191 | | '', |
192 | | exclude_list=['foo_app']) |
| 188 | with self.assertRaisesRegexp(management.CommandError, |
| 189 | "Unknown app in excludes: foo_app"): |
| 190 | self._dumpdata_assert(['fixtures', 'sites'], '', exclude_list=['foo_app']) |
193 | 191 | |
194 | 192 | # Excluding a bogus model should throw an error |
195 | | self.assertRaises(SystemExit, |
196 | | self._dumpdata_assert, |
197 | | ['fixtures', 'sites'], |
198 | | '', |
199 | | exclude_list=['fixtures.FooModel']) |
| 193 | with self.assertRaisesRegexp(management.CommandError, |
| 194 | "Unknown model in excludes: fixtures.FooModel"): |
| 195 | self._dumpdata_assert(['fixtures', 'sites'], '', exclude_list=['fixtures.FooModel']) |
200 | 196 | |
201 | 197 | def test_dumpdata_with_filtering_manager(self): |
202 | 198 | spy1 = Spy.objects.create(name='Paul') |
… |
… |
class FixtureLoadingTests(TestCase):
|
233 | 229 | def test_ambiguous_compressed_fixture(self): |
234 | 230 | # The name "fixture5" is ambigous, so loading it will raise an error |
235 | 231 | new_io = StringIO.StringIO() |
236 | | management.call_command('loaddata', 'fixture5', verbosity=0, stderr=new_io, commit=False) |
237 | | output = new_io.getvalue().strip().split('\n') |
238 | | self.assertEqual(len(output), 1) |
239 | | self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture5'")) |
| 232 | with self.assertRaisesRegexp(management.CommandError, |
| 233 | "Multiple fixtures named 'fixture5'"): |
| 234 | management.call_command('loaddata', 'fixture5', verbosity=0, |
| 235 | stderr=new_io, commit=False) |
240 | 236 | |
241 | 237 | def test_db_loading(self): |
242 | 238 | # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly |
… |
… |
class FixtureLoadingTests(TestCase):
|
261 | 257 | new_io = StringIO.StringIO() |
262 | 258 | management.call_command('loaddata', 'invalid.json', verbosity=0, stderr=new_io, commit=False) |
263 | 259 | output = new_io.getvalue().strip().split('\n') |
264 | | self.assertRegexpMatches(output[-1], "Error: Could not load fixtures.Article\(pk=1\): .*$") |
| 260 | # Last lines may be polluted by error formatting |
| 261 | self.assertIn("Error: Could not load fixtures.Article(pk=1):", "".join(output[-3:])) |
265 | 262 | |
266 | 263 | def test_loading_using(self): |
267 | 264 | # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly |
… |
… |
class FixtureTransactionTests(TransactionTestCase):
|
317 | 314 | # Try to load fixture 2 using format discovery; this will fail |
318 | 315 | # because there are two fixture2's in the fixtures directory |
319 | 316 | new_io = StringIO.StringIO() |
320 | | management.call_command('loaddata', 'fixture2', verbosity=0, stderr=new_io) |
321 | | output = new_io.getvalue().strip().split('\n') |
322 | | self.assertEqual(len(output), 1) |
323 | | self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture2'")) |
| 317 | with self.assertRaisesRegexp(management.CommandError, |
| 318 | "Multiple fixtures named 'fixture2'"): |
| 319 | management.call_command('loaddata', 'fixture2', verbosity=0, stderr=new_io) |
324 | 320 | |
325 | 321 | # object list is unaffected |
326 | 322 | self.assertQuerysetEqual(Article.objects.all(), [ |
-
diff --git a/tests/modeltests/user_commands/management/commands/dance.py b/tests/modeltests/user_commands/management/commands/dance.py
index 4ad5579..47875d2 100644
a
|
b
|
class Command(BaseCommand):
|
15 | 15 | |
16 | 16 | def handle(self, *args, **options): |
17 | 17 | example = options["example"] |
18 | | self.stdout.write("I don't feel like dancing %s." % options["style"]) |
| 18 | self.info("I don't feel like dancing %s." % options["style"]) |
-
diff --git a/tests/modeltests/user_commands/tests.py b/tests/modeltests/user_commands/tests.py
index 896dd66..c1e2bf9 100644
a
|
b
|
class CommandTests(TestCase):
|
11 | 11 | out = StringIO() |
12 | 12 | management.call_command('dance', stdout=out) |
13 | 13 | self.assertEqual(out.getvalue(), |
14 | | "I don't feel like dancing Rock'n'Roll.") |
| 14 | "I don't feel like dancing Rock'n'Roll.\n") |
15 | 15 | |
16 | 16 | def test_command_style(self): |
17 | 17 | out = StringIO() |
18 | 18 | management.call_command('dance', style='Jive', stdout=out) |
19 | 19 | self.assertEqual(out.getvalue(), |
20 | | "I don't feel like dancing Jive.") |
| 20 | "I don't feel like dancing Jive.\n") |
21 | 21 | |
22 | 22 | def test_language_preserved(self): |
23 | 23 | out = StringIO() |
-
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 780718b..d8a93cc 100644
a
|
b
|
class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
820 | 820 | self.perform_cull_test(50, 18) |
821 | 821 | |
822 | 822 | def test_second_call_doesnt_crash(self): |
823 | | err = StringIO.StringIO() |
824 | | management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False, stderr=err) |
825 | | self.assertTrue("Cache table 'test cache table' could not be created" in err.getvalue()) |
| 823 | with self.assertRaisesRegexp(management.CommandError, |
| 824 | "Cache table 'test cache table' could not be created"): |
| 825 | management.call_command( |
| 826 | 'createcachetable', |
| 827 | self._table_name, |
| 828 | verbosity=0, |
| 829 | interactive=False |
| 830 | ) |
826 | 831 | |
827 | 832 | |
828 | 833 | @override_settings(USE_TZ=True) |
-
diff --git a/tests/regressiontests/fixtures_regress/tests.py b/tests/regressiontests/fixtures_regress/tests.py
index ed8b404..5c1a70e 100644
a
|
b
|
class TestFixtures(TestCase):
|
119 | 119 | Test for ticket #4371 -- Loading data of an unknown format should fail |
120 | 120 | Validate that error conditions are caught correctly |
121 | 121 | """ |
122 | | stderr = StringIO() |
123 | | management.call_command( |
124 | | 'loaddata', |
125 | | 'bad_fixture1.unkn', |
126 | | verbosity=0, |
127 | | commit=False, |
128 | | stderr=stderr, |
129 | | ) |
130 | | self.assertEqual( |
131 | | stderr.getvalue(), |
132 | | "Problem installing fixture 'bad_fixture1': unkn is not a known serialization format.\n" |
133 | | ) |
| 122 | with self.assertRaisesRegexp(CommandError, |
| 123 | "Problem installing fixture 'bad_fixture1': unkn is not a known serialization format."): |
| 124 | management.call_command( |
| 125 | 'loaddata', |
| 126 | 'bad_fixture1.unkn', |
| 127 | verbosity=0, |
| 128 | commit=False, |
| 129 | ) |
134 | 130 | |
135 | 131 | def test_invalid_data(self): |
136 | 132 | """ |
… |
… |
class TestFixtures(TestCase):
|
138 | 134 | using explicit filename. |
139 | 135 | Validate that error conditions are caught correctly |
140 | 136 | """ |
141 | | stderr = StringIO() |
142 | | management.call_command( |
143 | | 'loaddata', |
144 | | 'bad_fixture2.xml', |
145 | | verbosity=0, |
146 | | commit=False, |
147 | | stderr=stderr, |
148 | | ) |
149 | | self.assertEqual( |
150 | | stderr.getvalue(), |
151 | | "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" |
152 | | ) |
| 137 | with self.assertRaisesRegexp(CommandError, |
| 138 | "No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"): |
| 139 | management.call_command( |
| 140 | 'loaddata', |
| 141 | 'bad_fixture2.xml', |
| 142 | verbosity=0, |
| 143 | commit=False, |
| 144 | ) |
153 | 145 | |
154 | 146 | def test_invalid_data_no_ext(self): |
155 | 147 | """ |
… |
… |
class TestFixtures(TestCase):
|
157 | 149 | without file extension. |
158 | 150 | Validate that error conditions are caught correctly |
159 | 151 | """ |
160 | | stderr = StringIO() |
161 | | management.call_command( |
162 | | 'loaddata', |
163 | | 'bad_fixture2', |
164 | | verbosity=0, |
165 | | commit=False, |
166 | | stderr=stderr, |
167 | | ) |
168 | | self.assertEqual( |
169 | | stderr.getvalue(), |
170 | | "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" |
171 | | ) |
| 152 | with self.assertRaisesRegexp(CommandError, |
| 153 | "No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"): |
| 154 | management.call_command( |
| 155 | 'loaddata', |
| 156 | 'bad_fixture2', |
| 157 | verbosity=0, |
| 158 | commit=False, |
| 159 | ) |
172 | 160 | |
173 | 161 | def test_empty(self): |
174 | 162 | """ |
175 | 163 | Test for ticket #4371 -- Loading a fixture file with no data returns an error. |
176 | 164 | Validate that error conditions are caught correctly |
177 | 165 | """ |
178 | | stderr = StringIO() |
179 | | management.call_command( |
180 | | 'loaddata', |
181 | | 'empty', |
182 | | verbosity=0, |
183 | | commit=False, |
184 | | stderr=stderr, |
185 | | ) |
186 | | self.assertEqual( |
187 | | stderr.getvalue(), |
188 | | "No fixture data found for 'empty'. (File format may be invalid.)\n" |
189 | | ) |
190 | | |
191 | | def test_abort_loaddata_on_error(self): |
192 | | """ |
193 | | Test for ticket #4371 -- If any of the fixtures contain an error, |
194 | | loading is aborted. |
195 | | Validate that error conditions are caught correctly |
196 | | """ |
197 | | stderr = StringIO() |
198 | | management.call_command( |
199 | | 'loaddata', |
200 | | 'empty', |
201 | | verbosity=0, |
202 | | commit=False, |
203 | | stderr=stderr, |
204 | | ) |
205 | | self.assertEqual( |
206 | | stderr.getvalue(), |
207 | | "No fixture data found for 'empty'. (File format may be invalid.)\n" |
208 | | ) |
| 166 | with self.assertRaisesRegexp(CommandError, |
| 167 | "No fixture data found for 'empty'. \(File format may be invalid.\)"): |
| 168 | management.call_command( |
| 169 | 'loaddata', |
| 170 | 'empty', |
| 171 | verbosity=0, |
| 172 | commit=False, |
| 173 | ) |
209 | 174 | |
210 | 175 | def test_error_message(self): |
211 | 176 | """ |
212 | 177 | (Regression for #9011 - error message is correct) |
213 | 178 | """ |
214 | | stderr = StringIO() |
215 | | management.call_command( |
216 | | 'loaddata', |
217 | | 'bad_fixture2', |
218 | | 'animal', |
219 | | verbosity=0, |
220 | | commit=False, |
221 | | stderr=stderr, |
222 | | ) |
223 | | self.assertEqual( |
224 | | stderr.getvalue(), |
225 | | "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" |
226 | | ) |
| 179 | with self.assertRaisesRegexp(CommandError, |
| 180 | "No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"): |
| 181 | management.call_command( |
| 182 | 'loaddata', |
| 183 | 'bad_fixture2', |
| 184 | 'animal', |
| 185 | verbosity=0, |
| 186 | commit=False, |
| 187 | ) |
227 | 188 | |
228 | 189 | def test_pg_sequence_resetting_checks(self): |
229 | 190 | """ |
… |
… |
class TestFixtures(TestCase):
|
337 | 298 | platypus_json = '{"pk": %d, "model": "fixtures_regress.animal", "fields": {"count": 2, "weight": 2.2, "name": "Platypus", "latin_name": "Ornithorhynchus anatinus"}}' |
338 | 299 | platypus_json = platypus_json % animal.pk |
339 | 300 | |
340 | | self.assertEqual(len(data), len('[%s]' % ', '.join([lion_json, emu_json, platypus_json]))) |
| 301 | # -1 is for the final newline |
| 302 | self.assertEqual(len(data)-1, len('[%s]' % ', '.join([lion_json, emu_json, platypus_json]))) |
341 | 303 | self.assertTrue(lion_json in data) |
342 | 304 | self.assertTrue(emu_json in data) |
343 | 305 | self.assertTrue(platypus_json in data) |
… |
… |
class TestFixtures(TestCase):
|
358 | 320 | ) |
359 | 321 | self.assertEqual( |
360 | 322 | stdout.getvalue(), |
361 | | """[{"pk": %d, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}]""" |
| 323 | """[{"pk": %d, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}]\n""" |
362 | 324 | % widget.pk |
363 | 325 | ) |
364 | 326 | |
… |
… |
class TestFixtures(TestCase):
|
387 | 349 | commit=False, |
388 | 350 | stderr=stderr, |
389 | 351 | ) |
390 | | self.assertTrue( |
391 | | stderr.getvalue().startswith('Problem installing fixture') |
392 | | ) |
| 352 | self.assertIn('Problem installing fixture', stderr.getvalue()) |
393 | 353 | |
394 | 354 | _cur_dir = os.path.dirname(os.path.abspath(__file__)) |
395 | 355 | |
… |
… |
class TestFixtures(TestCase):
|
414 | 374 | """ |
415 | 375 | Regression for #7043 - Error is quickly reported when no fixtures is provided in the command line. |
416 | 376 | """ |
417 | | stderr = StringIO() |
418 | | management.call_command( |
419 | | 'loaddata', |
420 | | verbosity=0, |
421 | | commit=False, |
422 | | stderr=stderr, |
423 | | ) |
424 | | self.assertEqual( |
425 | | stderr.getvalue(), 'No database fixture specified. Please provide the path of at least one fixture in the command line.\n' |
426 | | ) |
| 377 | with self.assertRaisesRegexp(CommandError, |
| 378 | "No database fixture specified. " |
| 379 | "Please provide the path of at least one fixture in the command line."): |
| 380 | management.call_command( |
| 381 | 'loaddata', |
| 382 | verbosity=0, |
| 383 | commit=False, |
| 384 | ) |
427 | 385 | |
428 | 386 | def test_loaddata_not_existant_fixture_file(self): |
429 | 387 | stdout_output = StringIO() |
… |
… |
class NaturalKeyFixtureTests(TestCase):
|
524 | 482 | ) |
525 | 483 | self.assertEqual( |
526 | 484 | stdout.getvalue(), |
527 | | """[{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]""" |
| 485 | """[{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]\n""" |
528 | 486 | ) |
529 | 487 | |
530 | 488 | def test_dependency_sorting(self): |
-
diff --git a/tests/regressiontests/i18n/commands/compilation.py b/tests/regressiontests/i18n/commands/compilation.py
index 1f38910..2cd02d5 100644
a
|
b
|
|
1 | 1 | import os |
2 | | try: |
3 | | from cStringIO import StringIO |
4 | | except ImportError: |
5 | | from StringIO import StringIO |
| 2 | from StringIO import StringIO |
6 | 3 | |
7 | | from django.core.management import CommandError |
8 | | from django.core.management.commands.compilemessages import compile_messages |
| 4 | from django.core.management import call_command, CommandError |
9 | 5 | from django.test import TestCase |
10 | 6 | from django.test.utils import override_settings |
11 | 7 | from django.utils import translation |
12 | 8 | |
13 | 9 | test_dir = os.path.abspath(os.path.dirname(__file__)) |
14 | 10 | |
| 11 | |
15 | 12 | class MessageCompilationTests(TestCase): |
16 | 13 | |
17 | 14 | def setUp(self): |
… |
… |
class PoFileTests(MessageCompilationTests):
|
28 | 25 | |
29 | 26 | def test_bom_rejection(self): |
30 | 27 | os.chdir(test_dir) |
31 | | # We don't use the django.core.management infrastructure (call_command() |
32 | | # et al) because CommandError's cause exit(1) there. We test the |
33 | | # underlying compile_messages function instead |
34 | 28 | out = StringIO() |
35 | | self.assertRaises(CommandError, compile_messages, out, locale=self.LOCALE) |
| 29 | self.assertRaises(CommandError, call_command, 'compilemessages', |
| 30 | locale=self.LOCALE, stdout=out, stderr=out) |
36 | 31 | self.assertFalse(os.path.exists(self.MO_FILE)) |
37 | 32 | |
38 | 33 | |
… |
… |
class PoFileContentsTests(MessageCompilationTests):
|
48 | 43 | |
49 | 44 | def test_percent_symbol_in_po_file(self): |
50 | 45 | os.chdir(test_dir) |
51 | | # We don't use the django.core.management infrastructure (call_command() |
52 | | # et al) because CommandError's cause exit(1) there. We test the |
53 | | # underlying compile_messages function instead |
54 | | out = StringIO() |
55 | | compile_messages(out, locale=self.LOCALE) |
| 46 | call_command('compilemessages', locale=self.LOCALE, stdout=StringIO()) |
56 | 47 | self.assertTrue(os.path.exists(self.MO_FILE)) |
57 | 48 | |
58 | 49 | |
… |
… |
class PercentRenderingTests(MessageCompilationTests):
|
67 | 58 | def test_percent_symbol_escaping(self): |
68 | 59 | from django.template import Template, Context |
69 | 60 | os.chdir(test_dir) |
70 | | # We don't use the django.core.management infrastructure (call_command() |
71 | | # et al) because CommandError's cause exit(1) there. We test the |
72 | | # underlying compile_messages function instead |
73 | | out = StringIO() |
74 | | compile_messages(out, locale=self.LOCALE) |
| 61 | call_command('compilemessages', locale=self.LOCALE, stdout=StringIO()) |
75 | 62 | with translation.override(self.LOCALE): |
76 | 63 | t = Template('{% load i18n %}{% trans "Looks like a str fmt spec %% o but shouldn\'t be interpreted as such" %}') |
77 | 64 | rendered = t.render(Context({})) |
-
diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py
index 7f30cb9..31a6c6f 100644
a
|
b
|
class TestFindStatic(CollectionTestCase, TestDefaults):
|
187 | 187 | Test ``findstatic`` management command. |
188 | 188 | """ |
189 | 189 | def _get_file(self, filepath): |
190 | | _stdout = sys.stdout |
191 | | sys.stdout = StringIO() |
192 | | try: |
193 | | call_command('findstatic', filepath, all=False, verbosity='0') |
194 | | sys.stdout.seek(0) |
195 | | lines = [l.strip() for l in sys.stdout.readlines()] |
196 | | contents = codecs.open( |
197 | | smart_unicode(lines[1].strip()), "r", "utf-8").read() |
198 | | finally: |
199 | | sys.stdout = _stdout |
| 190 | out = StringIO() |
| 191 | call_command('findstatic', filepath, all=False, verbosity='0', stdout=out) |
| 192 | lines = [l.strip() for l in out.getvalue().split('\n') if l != ''] |
| 193 | contents = codecs.open( |
| 194 | smart_unicode(lines[1].strip()), "r", "utf-8").read() |
200 | 195 | return contents |
201 | 196 | |
202 | 197 | def test_all_files(self): |
203 | 198 | """ |
204 | 199 | Test that findstatic returns all candidate files if run without --first. |
205 | 200 | """ |
206 | | _stdout = sys.stdout |
207 | | sys.stdout = StringIO() |
208 | | try: |
209 | | call_command('findstatic', 'test/file.txt', verbosity='0') |
210 | | sys.stdout.seek(0) |
211 | | lines = [l.strip() for l in sys.stdout.readlines()] |
212 | | finally: |
213 | | sys.stdout = _stdout |
| 201 | out = StringIO() |
| 202 | call_command('findstatic', 'test/file.txt', verbosity='0', stdout=out) |
| 203 | lines = [l.strip() for l in out.getvalue().split('\n') if l != ''] |
214 | 204 | self.assertEqual(len(lines), 3) # three because there is also the "Found <file> here" line |
215 | 205 | self.assertIn('project', lines[1]) |
216 | 206 | self.assertIn('apps', lines[2]) |