在 Django 管理命令中添加虚假换行符
Spurious newlines added in Django management commands
运行 Python 3.5.0 上的 Django v1.10:
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 = ''
但是这个 hack 意味着您无法获得打印功能的所有功能,您必须使用 self.stdout.write
并手动准备字节。
首先,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
是一个 bytes
实例且结尾是 str
,则 msg.endswith(ending)
将失败。
此外,当我明确设置 self.stdout.ending = ''
时,print
和 self.stdout
确实可以正常工作;然而,这样做可能意味着其他使用 self.stdout.write
期望它插入换行符的代码会失败。
在你的情况下,我要做的是为 Command
:
定义一个 print
方法
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')
显式设置 self.stdout.ending
时,打印命令按预期工作。
当file=self.stdout
时,需要在self.stdout.ending
中设置行结束符,因为那是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)
Returns
hello world
如 Django 1.10's Custom Management Commands 文档中所述:
When you are using management commands and wish to provide console output, you should write to self.stdout and self.stderr, instead of printing to stdout and stderr directly. By using these proxies, it becomes much easier to test your custom command. Note also that you don’t need to end messages with a newline character, it will be added automatically, unless you specify the ending parameter:
self.stdout.write("Unterminated line", ending='')
因此,为了在 Command
class 中打印,您应该将 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()
的调用中。因此,最好在 self.stdout.write()
函数中使用 ending
参数(如上面的示例代码所示)。
正如您提到的 "But this hack means you don't get all the features of the print function, you must use self.stdout.write and prepare the bytes manually." 不,您不必担心创建 bytes
或 print()
的其他功能,因为self.stdout.write()
function belonging to OutputWrapper
class expects data to be in str
format. Also I would like to mention that print()
and OutputWrapper.write()
behaves quite similar both acting as a wrapper around sys.stdout.write()
。
print()
和OutputWrapper.write()
的唯一区别是:
print()
接受消息字符串作为 *args
和 separator
参数来连接多个字符串,而
OutputWrapper.write()
接受单个消息字符串
但是通过使用 分隔符 显式连接字符串并将其传递给 OutputWrapper.write()
.
可以轻松处理这种差异
结论:您不必担心 print()
提供的附加功能,因为有 none,应该继续使用 self.stdout.write()
根据此答案的引用内容从 Custom Management Commands 文档中建议。
如果您有兴趣,可以查看 BaseCommand
和 OutputWrapper
classes 的源代码,网址为:Source code for django.core.management.base
. It might help in clearing some of your doubts. You may also check PEP-3105 related to Python 3's print()
.
运行 Python 3.5.0 上的 Django v1.10:
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 = ''
但是这个 hack 意味着您无法获得打印功能的所有功能,您必须使用 self.stdout.write
并手动准备字节。
首先,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
是一个 bytes
实例且结尾是 str
,则 msg.endswith(ending)
将失败。
此外,当我明确设置 self.stdout.ending = ''
时,print
和 self.stdout
确实可以正常工作;然而,这样做可能意味着其他使用 self.stdout.write
期望它插入换行符的代码会失败。
在你的情况下,我要做的是为 Command
:
print
方法
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')
显式设置 self.stdout.ending
时,打印命令按预期工作。
当file=self.stdout
时,需要在self.stdout.ending
中设置行结束符,因为那是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)
Returns
hello world
如 Django 1.10's Custom Management Commands 文档中所述:
When you are using management commands and wish to provide console output, you should write to self.stdout and self.stderr, instead of printing to stdout and stderr directly. By using these proxies, it becomes much easier to test your custom command. Note also that you don’t need to end messages with a newline character, it will be added automatically, unless you specify the ending parameter:
self.stdout.write("Unterminated line", ending='')
因此,为了在 Command
class 中打印,您应该将 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()
的调用中。因此,最好在 self.stdout.write()
函数中使用 ending
参数(如上面的示例代码所示)。
正如您提到的 "But this hack means you don't get all the features of the print function, you must use self.stdout.write and prepare the bytes manually." 不,您不必担心创建 bytes
或 print()
的其他功能,因为self.stdout.write()
function belonging to OutputWrapper
class expects data to be in str
format. Also I would like to mention that print()
and OutputWrapper.write()
behaves quite similar both acting as a wrapper around sys.stdout.write()
。
print()
和OutputWrapper.write()
的唯一区别是:
print()
接受消息字符串作为*args
和separator
参数来连接多个字符串,而OutputWrapper.write()
接受单个消息字符串
但是通过使用 分隔符 显式连接字符串并将其传递给 OutputWrapper.write()
.
结论:您不必担心 print()
提供的附加功能,因为有 none,应该继续使用 self.stdout.write()
根据此答案的引用内容从 Custom Management Commands 文档中建议。
如果您有兴趣,可以查看 BaseCommand
和 OutputWrapper
classes 的源代码,网址为:Source code for django.core.management.base
. It might help in clearing some of your doubts. You may also check PEP-3105 related to Python 3's print()
.