Browse Source

Fixed #18325 -- Wrapped self.stdout/stderr in OutputWrapper class

Claude Paroz 13 years ago
parent
commit
822d6d6dab

+ 7 - 7
django/contrib/auth/management/commands/createsuperuser.py

@@ -80,7 +80,7 @@ class Command(BaseCommand):
                     if default_username and username == '':
                         username = default_username
                     if not RE_VALID_USERNAME.match(username):
-                        sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
+                        self.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.")
                         username = None
                         continue
                     try:
@@ -88,7 +88,7 @@ class Command(BaseCommand):
                     except User.DoesNotExist:
                         break
                     else:
-                        sys.stderr.write("Error: That username is already taken.\n")
+                        self.stderr.write("Error: That username is already taken.")
                         username = None
 
                 # Get an email
@@ -98,7 +98,7 @@ class Command(BaseCommand):
                     try:
                         is_valid_email(email)
                     except exceptions.ValidationError:
-                        sys.stderr.write("Error: That e-mail address is invalid.\n")
+                        self.stderr.write("Error: That e-mail address is invalid.")
                         email = None
                     else:
                         break
@@ -109,19 +109,19 @@ class Command(BaseCommand):
                         password = getpass.getpass()
                         password2 = getpass.getpass('Password (again): ')
                         if password != password2:
-                            sys.stderr.write("Error: Your passwords didn't match.\n")
+                            self.stderr.write("Error: Your passwords didn't match.")
                             password = None
                             continue
                     if password.strip() == '':
-                        sys.stderr.write("Error: Blank passwords aren't allowed.\n")
+                        self.stderr.write("Error: Blank passwords aren't allowed.")
                         password = None
                         continue
                     break
             except KeyboardInterrupt:
-                sys.stderr.write("\nOperation cancelled.\n")
+                self.stderr.write("\nOperation cancelled.")
                 sys.exit(1)
 
         User.objects.db_manager(database).create_superuser(username, email, password)
         if verbosity >= 1:
-          self.stdout.write("Superuser created successfully.\n")
+          self.stdout.write("Superuser created successfully.")
 

+ 2 - 5
django/contrib/staticfiles/management/commands/collectstatic.py

@@ -4,7 +4,7 @@ from optparse import make_option
 
 from django.core.files.storage import FileSystemStorage
 from django.core.management.base import CommandError, NoArgsCommand
-from django.utils.encoding import smart_str, smart_unicode
+from django.utils.encoding import smart_unicode
 from django.utils.datastructures import SortedDict
 
 from django.contrib.staticfiles import finders, storage
@@ -178,15 +178,12 @@ Type 'yes' to continue, or 'no' to cancel: """
                                    ', %s post-processed'
                                    % post_processed_count or ''),
             }
-            self.stdout.write(smart_str(summary))
+            self.stdout.write(summary)
 
     def log(self, msg, level=2):
         """
         Small log helper
         """
-        msg = smart_str(msg)
-        if not msg.endswith("\n"):
-            msg += "\n"
         if self.verbosity >= level:
             self.stdout.write(msg)
 

+ 2 - 4
django/contrib/staticfiles/management/commands/findstatic.py

@@ -23,9 +23,7 @@ class Command(LabelCommand):
                 result = [result]
             output = u'\n  '.join(
                 (smart_unicode(os.path.realpath(path)) for path in result))
-            self.stdout.write(
-                smart_str(u"Found '%s' here:\n  %s\n" % (path, output)))
+            self.stdout.write(u"Found '%s' here:\n  %s" % (path, output))
         else:
             if verbosity >= 1:
-                self.stderr.write(
-                    smart_str("No matching file found for '%s'.\n" % path))
+                self.stderr.write("No matching file found for '%s'." % path)

+ 31 - 7
django/core/management/base.py

@@ -45,6 +45,29 @@ def handle_default_options(options):
         sys.path.insert(0, options.pythonpath)
 
 
+class OutputWrapper(object):
+    """
+    Wrapper around stdout/stderr
+    """
+    def __init__(self, out, style_func=None):
+        self._out = out
+        self.style_func = None
+        if hasattr(out, 'isatty') and out.isatty():
+            self.style_func = style_func
+
+    def __getattr__(self, name):
+        return getattr(self._out, name)
+
+    def write(self, msg, style_func=None, ending='\n'):
+        if ending and not msg.endswith(ending):
+            msg += ending
+        if style_func is not None:
+            msg = style_func(msg)
+        elif self.style_func is not None:
+            msg = self.style_func(msg)
+        self._out.write(smart_str(msg))
+
+
 class BaseCommand(object):
     """
     The base class from which all management commands ultimately
@@ -210,6 +233,9 @@ class BaseCommand(object):
         # But only do this if we can assume we have a working settings file,
         # because django.utils.translation requires settings.
         saved_lang = None
+        self.stdout = OutputWrapper(options.get('stdout', sys.stdout))
+        self.stderr = OutputWrapper(options.get('stderr', sys.stderr), self.style.ERROR)
+
         if self.can_import_settings:
             try:
                 from django.utils import translation
@@ -221,12 +247,10 @@ class BaseCommand(object):
                 if show_traceback:
                     traceback.print_exc()
                 else:
-                    sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
+                    self.stderr.write('Error: %s' % e)
                 sys.exit(1)
 
         try:
-            self.stdout = options.get('stdout', sys.stdout)
-            self.stderr = options.get('stderr', sys.stderr)
             if self.requires_model_validation and not options.get('skip_validation'):
                 self.validate()
             output = self.handle(*args, **options)
@@ -237,15 +261,15 @@ class BaseCommand(object):
                     from django.db import connections, DEFAULT_DB_ALIAS
                     connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
                     if connection.ops.start_transaction_sql():
-                        self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()) + '\n')
+                        self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()))
                 self.stdout.write(output)
                 if self.output_transaction:
-                    self.stdout.write('\n' + self.style.SQL_KEYWORD("COMMIT;") + '\n')
+                    self.stdout.write('\n' + self.style.SQL_KEYWORD("COMMIT;"))
         except CommandError as e:
             if show_traceback:
                 traceback.print_exc()
             else:
-                self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
+                self.stderr.write('Error: %s' % e)
             sys.exit(1)
         finally:
             if saved_lang is not None:
@@ -266,7 +290,7 @@ class BaseCommand(object):
             error_text = s.read()
             raise CommandError("One or more models did not validate:\n%s" % error_text)
         if display_num_errors:
-            self.stdout.write("%s error%s found\n" % (num_errors, num_errors != 1 and 's' or ''))
+            self.stdout.write("%s error%s found" % (num_errors, num_errors != 1 and 's' or ''))
 
     def handle(self, *args, **options):
         """

+ 2 - 2
django/core/management/commands/createcachetable.py

@@ -56,8 +56,8 @@ class Command(LabelCommand):
             curs.execute("\n".join(full_statement))
         except DatabaseError as e:
             self.stderr.write(
-                self.style.ERROR("Cache table '%s' could not be created.\nThe error was: %s.\n" %
-                    (tablename, e)))
+                "Cache table '%s' could not be created.\nThe error was: %s." %
+                    (tablename, e))
             transaction.rollback_unless_managed(using=db)
         else:
             for statement in index_output:

+ 3 - 2
django/core/management/commands/dumpdata.py

@@ -109,8 +109,9 @@ class Command(BaseCommand):
                     objects.extend(model._default_manager.using(using).all())
 
         try:
-            return serializers.serialize(format, objects, indent=indent,
-                        use_natural_keys=use_natural_keys)
+            self.stdout.write(serializers.serialize(format, objects,
+                              indent=indent, use_natural_keys=use_natural_keys),
+                              ending='')
         except Exception as e:
             if show_traceback:
                 raise

+ 18 - 18
django/core/management/commands/loaddata.py

@@ -34,11 +34,11 @@ class Command(BaseCommand):
         using = options.get('database')
 
         connection = connections[using]
-        self.style = no_style()
 
         if not len(fixture_labels):
             self.stderr.write(
-                self.style.ERROR("No database fixture specified. Please provide the path of at least one fixture in the command line.\n")
+                "No database fixture specified. Please provide the path of at "
+                "least one fixture in the command line."
             )
             return
 
@@ -124,11 +124,11 @@ class Command(BaseCommand):
 
                     if formats:
                         if verbosity >= 2:
-                            self.stdout.write("Loading '%s' fixtures...\n" % fixture_name)
+                            self.stdout.write("Loading '%s' fixtures..." % fixture_name)
                     else:
                         self.stderr.write(
-                            self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" %
-                                (fixture_name, format)))
+                            "Problem installing fixture '%s': %s is not a known serialization format." %
+                                (fixture_name, format))
                         if commit:
                             transaction.rollback(using=using)
                             transaction.leave_transaction_management(using=using)
@@ -141,7 +141,7 @@ class Command(BaseCommand):
 
                     for fixture_dir in fixture_dirs:
                         if verbosity >= 2:
-                            self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir))
+                            self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir))
 
                         label_found = False
                         for combo in product([using, None], formats, compression_formats):
@@ -154,7 +154,7 @@ class Command(BaseCommand):
                             )
 
                             if verbosity >= 3:
-                                self.stdout.write("Trying %s for %s fixture '%s'...\n" % \
+                                self.stdout.write("Trying %s for %s fixture '%s'..." % \
                                     (humanize(fixture_dir), file_name, fixture_name))
                             full_path = os.path.join(fixture_dir, file_name)
                             open_method = compression_types[compression_format]
@@ -162,13 +162,13 @@ class Command(BaseCommand):
                                 fixture = open_method(full_path, 'r')
                             except IOError:
                                 if verbosity >= 2:
-                                    self.stdout.write("No %s fixture '%s' in %s.\n" % \
+                                    self.stdout.write("No %s fixture '%s' in %s." % \
                                         (format, fixture_name, humanize(fixture_dir)))
                             else:
                                 try:
                                     if label_found:
-                                        self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" %
-                                            (fixture_name, humanize(fixture_dir))))
+                                        self.stderr.write("Multiple fixtures named '%s' in %s. Aborting." %
+                                            (fixture_name, humanize(fixture_dir)))
                                         if commit:
                                             transaction.rollback(using=using)
                                             transaction.leave_transaction_management(using=using)
@@ -178,7 +178,7 @@ class Command(BaseCommand):
                                     objects_in_fixture = 0
                                     loaded_objects_in_fixture = 0
                                     if verbosity >= 2:
-                                        self.stdout.write("Installing %s fixture '%s' from %s.\n" % \
+                                        self.stdout.write("Installing %s fixture '%s' from %s." % \
                                             (format, fixture_name, humanize(fixture_dir)))
 
                                     objects = serializers.deserialize(format, fixture, using=using)
@@ -209,8 +209,8 @@ class Command(BaseCommand):
                                 # error was encountered during fixture loading.
                                 if objects_in_fixture == 0:
                                     self.stderr.write(
-                                        self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" %
-                                            (fixture_name)))
+                                        "No fixture data found for '%s'. (File format may be invalid.)" %
+                                            (fixture_name))
                                     if commit:
                                         transaction.rollback(using=using)
                                         transaction.leave_transaction_management(using=using)
@@ -231,16 +231,16 @@ class Command(BaseCommand):
                 traceback.print_exc()
             else:
                 self.stderr.write(
-                    self.style.ERROR("Problem installing fixture '%s': %s\n" %
+                    "Problem installing fixture '%s': %s" %
                          (full_path, ''.join(traceback.format_exception(sys.exc_type,
-                             sys.exc_value, sys.exc_traceback)))))
+                             sys.exc_value, sys.exc_traceback))))
             return
 
 
         # If we found even one object in a fixture, we need to reset the
         # database sequences.
         if loaded_object_count > 0:
-            sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
+            sequence_sql = connection.ops.sequence_reset_sql(no_style(), models)
             if sequence_sql:
                 if verbosity >= 2:
                     self.stdout.write("Resetting sequences\n")
@@ -253,10 +253,10 @@ class Command(BaseCommand):
 
         if verbosity >= 1:
             if fixture_object_count == loaded_object_count:
-                self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % (
+                self.stdout.write("Installed %d object(s) from %d fixture(s)" % (
                     loaded_object_count, fixture_count))
             else:
-                self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)\n" % (
+                self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)" % (
                     loaded_object_count, fixture_object_count, fixture_count))
 
         # Close the DB connection. This is required as a workaround for an

+ 2 - 2
django/core/management/commands/runserver.py

@@ -120,12 +120,12 @@ class Command(BaseCommand):
                 error_text = ERRORS[e.args[0].args[0]]
             except (AttributeError, KeyError):
                 error_text = str(e)
-            sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
+            sys.stderr.write("Error: %s" % error_text)
             # Need to use an OS exit because sys.exit doesn't work in a thread
             os._exit(1)
         except KeyboardInterrupt:
             if shutdown_message:
-                self.stdout.write("%s\n" % shutdown_message)
+                self.stdout.write(shutdown_message)
             sys.exit(0)
 
 

+ 2 - 4
django/core/management/templates.py

@@ -16,7 +16,6 @@ from os import path
 import django
 from django.template import Template, Context
 from django.utils import archive
-from django.utils.encoding import smart_str
 from django.utils._os import rmtree_errorhandler
 from django.core.management.base import BaseCommand, CommandError
 from django.core.management.commands.makemessages import handle_extensions
@@ -166,11 +165,10 @@ class TemplateCommand(BaseCommand):
                     shutil.copymode(old_path, new_path)
                     self.make_writeable(new_path)
                 except OSError:
-                    notice = self.style.NOTICE(
+                    self.stderr.write(
                         "Notice: Couldn't set permission bits on %s. You're "
                         "probably using an uncommon filesystem setup. No "
-                        "problem.\n" % new_path)
-                    sys.stderr.write(smart_str(notice))
+                        "problem." % new_path, self.style.NOTICE)
 
         if self.paths_to_remove:
             if self.verbosity >= 2:

+ 1 - 1
docs/howto/custom-management-commands.txt

@@ -61,7 +61,7 @@ look like this:
                 poll.opened = False
                 poll.save()
 
-                self.stdout.write('Successfully closed poll "%s"\n' % poll_id)
+                self.stdout.write('Successfully closed poll "%s"' % poll_id)
 
 .. note::
     When you are using management commands and wish to provide console

+ 2 - 2
tests/modeltests/user_commands/tests.py

@@ -11,13 +11,13 @@ class CommandTests(TestCase):
         out = StringIO()
         management.call_command('dance', stdout=out)
         self.assertEqual(out.getvalue(),
-            "I don't feel like dancing Rock'n'Roll.")
+            "I don't feel like dancing Rock'n'Roll.\n")
 
     def test_command_style(self):
         out = StringIO()
         management.call_command('dance', style='Jive', stdout=out)
         self.assertEqual(out.getvalue(),
-            "I don't feel like dancing Jive.")
+            "I don't feel like dancing Jive.\n")
 
     def test_language_preserved(self):
         out = StringIO()