Django管理命令中添加了虚假新行

wim*_*wim 15 python django stdout python-3.x django-management-command

在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)
Run Code Online (Sandbox Code Playgroud)

预期产量:

hello world
Run Code Online (Sandbox Code Playgroud)

实际产量:

hello 

world
Run Code Online (Sandbox Code Playgroud)

如何正确传递结束字符?我目前使用明确设置的解决方法:

 self.stdout.ending = ''
Run Code Online (Sandbox Code Playgroud)

但是这个hack意味着你没有获得打印功能的所有功能,你必须self.stdout.write手动使用和准备字节.

Moi*_*dri 12

正如Django 1.10的自定义管理命令文档中所述:

当您使用管理命令并希望提供控制台输出时,您应该写入self.stdoutself.stderr,而不是直接打印到stdoutstderr.通过使用这些代理,可以更轻松地测试自定义命令.另请注意,除非指定结束参数,否则不需要使用换行符结束消息,它将自动添加.

self.stdout.write("Unterminated line", ending='')
Run Code Online (Sandbox Code Playgroud)

因此,为了在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
Run Code Online (Sandbox Code Playgroud)

此外,通过显式设置self.stdout.ending = '',您正在修改self.stdout对象的属性.但你可能不希望这在未来的电话中反映出来self.stdout.write().因此,最好endingself.stdout.write()函数内使用参数(如上面的示例代码所示).

正如您所提到的"但是这个hack意味着您没有获得打印功能的所有功能,您必须使用self.stdout.write并手动准备字节." 不,您不必担心创建bytes其他功能print(),因为self.stdout.write()属于OutputWrapper类的函数需要数据str格式化.另外我想提一下,print()并且OutputWrapper.write()表现得非常相似,都充当了包装器sys.stdout.write().

print()和之间唯一的区别OutputWrapper.write()是:

  • print()接受消息字符串*argsseparator参数一起加入多个字符串,而
  • OutputWrapper.write() 接受单个消息字符串

但是,通过使用分隔符显式连接字符串并将其传递给,可以轻松处理这种差异OutputWrapper.write().

结论:您不必担心print()由于没有提供的附加功能,并且应该继续self.stdout.write()按照本回答引用的自定义管理命令文档中引用的内容进行使用.

如果您有兴趣,可以查看以下内容的源代码BaseCommandOutputWrapper类:源代码django.core.management.base.它可能有助于消除你的一些疑虑.您也可以查看与Python 3相关的PEP-3105print().

  • 谢谢,您的索赔退房!这种`OutputWrapper`的设计是一个糟糕的决定,它导致`self.stdout`违反了最小惊喜的原则.你希望它的行为类似于`sys.stdout`,但它没有,这意味着你不能正确地使用`self.stdout`和print函数,除非你是RTFM.这让我在测试中遇到了几次,现在我终于明白了原因!Imho界面应该匹配`sys.stdout`.或者,或者我们应该有一个名为`self.print`的包装器. (2认同)

Ant*_*ala 5

首先,self.stdout是一个django.core.management.base.OutputWrapper命令实例.它write期望一个str,而不是bytes,因此你可以使用

self.stdout.write('hello ', ending='')
self.stdout.write('world')
Run Code Online (Sandbox Code Playgroud)

实际上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)))
Run Code Online (Sandbox Code Playgroud)

如果ending为真,那么msg.endswith(ending)如果msgbytes实例并且结尾是a 则将失败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')
Run Code Online (Sandbox Code Playgroud)

您可以将它变成您自己的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')
Run Code Online (Sandbox Code Playgroud)


C14*_*14L 5

self.stdout.ending显式设置时,print命令按预期工作.

截至需求的行中设置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)
Run Code Online (Sandbox Code Playgroud)

返回

hello world
Run Code Online (Sandbox Code Playgroud)