Ложные новые строки, добавленные в командах управления Django
Запуск Django v1.10 на Python 3.5.0:
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
print('hello ', end='', file=self.stdout)
print('world', file=self.stdout)
Ожидаемый результат:
hello world
Фактический выход:
hello
world
Как правильно передать символ окончания? В настоящее время я использую обходное решение для установки явно:
self.stdout.ending = ''
Но этот хак означает, что вы не получаете всех функций функции печати, вы должны использовать self.stdout.write
и вручную подготовить байты.
Ответы
Ответ 1
При настройке self.stdout.ending
явно команда печати работает так, как ожидалось.
Окончание строки должно быть установлено в self.stdout.ending
, когда file=self.stdout
, потому что это экземпляр django.core.management.base.OutputWrapper
.
class Command(BaseCommand):
def handle(self, *args, **options):
self.stdout.ending = ''
print('hello ', end='', file=self.stdout)
print('world', file=self.stdout)
Возвращает
hello world
Ответ 2
Как указано в Django 1.10 Команды пользовательского управления document:
Когда вы используете команды управления и хотите предоставить консольный вывод, вы должны написать self.stdout и self.stderr, вместо того чтобы печатать на stdout и stderr. Используя эти прокси-серверы, вам будет намного легче проверить свою пользовательскую команду. Также обратите внимание, что вам не нужно заканчивать сообщения символом новой строки, он будет добавлен автоматически, если вы не укажете конечный параметр:
self.stdout.write("Unterminated line", ending='')
Следовательно, чтобы напечатать в вашем классе Command
, вы должны определить свою функцию handle()
как:
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
self.stdout.write("hello ", ending='')
self.stdout.write("world", ending='')
# prints: hello world
Кроме того, явно устанавливая self.stdout.ending = ''
, вы изменяете свойство объекта self.stdout
. Но вы не хотите, чтобы это отражалось в будущих вызовах self.stdout.write()
. Следовательно, лучше использовать параметр ending
в функции self.stdout.write()
(как показано в примере выше).
Как вы уже упоминали: "Но этот взлом означает, что вы не получаете всех функций функции печати, вы должны использовать self.stdout.write и вручную подготовить байты". Нет, вам не нужно беспокоиться о создании bytes
или других функций print()
, в качестве self.stdout.write()
функции, принадлежащей OutputWrapper
ожидает, что данные будут находиться в формате str
. Также я хотел бы упомянуть, что print()
и OutputWrapper.write()
ведут себя очень похоже, как действуя как обертка вокруг sys.stdout.write()
.
Единственное различие между print()
и OutputWrapper.write()
:
-
print()
принимает строку сообщения как *args
с параметром separator
для объединения нескольких строк, тогда как
-
OutputWrapper.write()
принимает строку с одним сообщением
Но эту разницу можно было бы легко обработать, явно связав строки с разделителем и передав их в OutputWrapper.write()
.
Вывод: Вам не нужно беспокоиться о дополнительных функциях, предоставляемых print()
, поскольку их нет, и следует продолжать использовать self.stdout.write()
, как это предлагается в этом ответе, цитируемый контент из Пользовательские команды управления.
Если вам интересно, вы можете проверить исходный код классов BaseCommand
и OutputWrapper
, доступных по адресу Исходный код для django.core.management.base
. Это может помочь в устранении некоторых из ваших сомнений. Вы также можете проверить PEP-3105, связанный с Python 3 print()
.
Ответ 3
Прежде всего, self.stdout
является экземпляром команды django.core.management.base.OutputWrapper
. Его write
ожидает str
, а не bytes
, поэтому вы можете использовать
self.stdout.write('hello ', ending='')
self.stdout.write('world')
Фактически self.stdout.write
принимает байты, но только когда ending
является пустой строкой - это потому, что его метод write
определен
def write(self, msg, style_func=None, ending=None):
ending = self.ending if ending is None else ending
if ending and not msg.endswith(ending):
msg += ending
style_func = style_func or self.style_func
self._out.write(force_str(style_func(msg)))
Если ending
истинно, тогда msg.endswith(ending)
завершится с ошибкой, если msg
является экземпляром bytes
и заканчивается как str
.
Кроме того, print
с self.stdout
работает корректно, когда я устанавливаю явно self.stdout.ending = ''
; однако это может означать, что другой код, который использует self.stdout.write
, ожидая, что он вставляет новые строки, потерпит неудачу.
В вашем случае, что бы я сделал, это определить метод print
для Command
:
from django.core.management.base import OutputWrapper
class PrintHelper:
def __init__(self, wrapped):
self.wrapped = wrapped
def write(self, s):
if isinstance(self.wrapped, OutputWrapper):
self.wrapped.write(s, ending='')
else:
self.wrapped.write(s)
class Command(BaseCommand):
def print(self, *args, file=None, **kwargs):
if file is None:
file = self.stdout
print(*args, file=PrintHelper(file), **kwargs)
def handle(self, *args, **options):
self.print('hello ', end='')
self.print('world')
Вы можете сделать это в своем собственном подклассе BaseCommand
, и вы также можете использовать его с разными файлами:
def handle(self, *args, **options):
for c in '|/-\\' * 100:
self.print('\rhello world: ' + c, end='', file=self.stderr)
time.sleep(0.1)
self.print('\bOK')