简体   繁体   English

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

[英]Spurious newlines added in Django management commands

Running Django v1.10 on Python 3.5.0: 在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)

Expected output: 预期产量:

hello world

Actual output: 实际产量:

hello 

world

How do I correctly pass the ending character? 如何正确传递结束字符? I currently use a workaround of setting explicitly: 我目前使用明确设置的解决方法:

 self.stdout.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. 但是这个hack意味着你没有获得print函数的所有功能,你必须使用self.stdout.write并手动准备字节。

As is mentioned in Django 1.10's Custom Management Commands document: 正如Django 1.10的自定义管理命令文档中所述:

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. 当您使用管理命令并希望提供控制台输出时,您应该写入self.stdoutself.stderr ,而不是直接打印到stdoutstderr 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='') 

Hence, in order to print in your Command class, you should define your handle() function as: 因此,为了在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

Also, by explicitly setting self.stdout.ending = '' , you are modifying the property of self.stdout object. 此外,通过显式设置self.stdout.ending = '' ,您正在修改self.stdout对象的属性。 But you may not want this to be reflected in future calls of self.stdout.write() . 但您可能不希望在将来的self.stdout.write()调用中反映出这一点。 Hence it will be better to use ending parameter within self.stdout.write() function (as demonstrated in sample code above). 因此,最好在self.stdout.write()函数中使用ending参数(如上面的示例代码所示)。

As you mentioned "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." 正如您所提到的“但是这个hack意味着您没有获得打印功能的所有功能,您必须使用self.stdout.write并手动准备字节。” No, you do not have to worry about creating the bytes or other features of print() , as self.stdout.write() function belonging to OutputWrapper class expects data to be in str format. 不,您不必担心创建print()bytes或其他功能,因为属于OutputWrapper类的self.stdout.write()函数需要数据为str格式。 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()行为非常相似,都充当了sys.stdout.write()的包装器。

The only difference between print() and OutputWrapper.write() is: print()OutputWrapper.write()之间的唯一区别是:

  • print() accepts message string as *args with separator parameter to join the the multiple strings, whereas print()接受消息字符串为*args with separator参数以连接多个字符串,而
  • OutputWrapper.write() accepts single message string OutputWrapper.write()接受单个消息字符串

But this difference could be easily handled by explicitly joining the strings with separator and passing it to OutputWrapper.write() . 但是,通过使用分隔符显式连接字符串并将其传递给OutputWrapper.write()可以轻松处理这种差异。

Conclusion: You do not have to worry about the additional features provided by print() as there are none, and should go ahead with using self.stdout.write() as suggested in this answer's quoted content from Custom Management Commands document. 结论:您不必担心print()提供的其他功能,因为没有,并且应该继续使用self.stdout.write()如本回答引用的自定义管理命令文档中的内容所示。

If you are interested, you may check the source code of BaseCommand and OutputWrapper classes available at: Source code for django.core.management.base . 如果您感兴趣,可以在以下位置检查BaseCommandOutputWrapper类的源代码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的print()相关的PEP-3105

First of all, self.stdout is an instance of django.core.management.base.OutputWrapper command. 首先, self.stdoutdjango.core.management.base.OutputWrapper命令的一个实例。 Its write expects an str , not bytes , thus you can use 它的write期望str ,而不是bytes ,因此您可以使用

self.stdout.write('hello ', ending='')
self.stdout.write('world')

Actually self.stdout.write does accept bytes but only whenever the ending is an empty string - that's because its write method is defined 实际上, 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)))

If ending is true, then msg.endswith(ending) will fail if msg is a bytes instance and ending is a str . 如果ending为true,那么msg.endswith(ending)将失败,如果msg是一个bytes实例,结尾是str

Furthermore, print with self.stdout does work correctly when I set the self.stdout.ending = '' explicitly; 此外,当我明确设置self.stdout.ending = ''时,使用self.stdout print确实可以正常工作; however doing this might mean that other code that uses self.stdout.write expecting it to insert newlines, would fail. 但是这样做可能意味着使用self.stdout.write其他代码期望它插入换行符会失败。


In your case, what I'd do is to define a print method for the Command : 在你的情况下,我要做的是为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')

You can make this into your own BaseCommand subclass - and you can use it with different files too: 您可以将它变成您自己的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')

When setting self.stdout.ending explicitly, the print command works as expected. 显式设置self.stdout.ending ,print命令按预期工作。

The line ending needs to be set in self.stdout.ending when file=self.stdout , because that is an instance of django.core.management.base.OutputWrapper . 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM