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

时间:2022-01-18 23:19:44

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并手动准备字节。

3 个解决方案

#1


5  

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

#2


11  

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. 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和self.stderr,而不是直接打印到stdout和stderr。通过使用这些代理,测试自定义命令变得更加容易。另请注意,除非指定结束参数,否则不需要使用换行符结束消息,它将自动添加。

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. But you may not want this to be reflected in future calls of 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.ending ='',您正在修改self.stdout对象的属性。但您可能不希望在将来调用self.stdout.write()时反映出这一点。因此,最好在self.stdout.write()函数中使用结束参数(如上面的示例代码所示)。

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." 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. Also I would like to mention that print() and OutputWrapper.write() behaves quite similar both acting as a wrapper around sys.stdout.write().

正如您所提到的“但是这个hack意味着您没有获得打印功能的所有功能,您必须使用self.stdout.write并手动准备字节。”不,您不必担心创建print()的字节或其他功能,因为属于OutputWrapper类的self.stdout.write()函数需要数据为str格式。另外我想提一下,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. It might help in clearing some of your doubts. You may also check PEP-3105 related to Python 3's print().

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

#3


5  

First of all, self.stdout is an instance of django.core.management.base.OutputWrapper command. Its write expects an str, not bytes, thus you can use

首先,self.stdout是django.core.management.base.OutputWrapper命令的一个实例。它的写入期望str,而不是字节,因此您可以使用

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确实接受字节,但只有当结尾是一个空字符串时 - 这是因为它的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.

如果结尾为true,那么如果msg是一个字节实例并且结尾是str,则msg.endswith(结束)将失败。

Furthermore, print with self.stdout does work correctly when I set the self.stdout.ending = '' explicitly; however doing this might mean that other code that uses self.stdout.write expecting it to insert newlines, would fail.

此外,当我明确设置self.stdout.ending =''时,使用self.stdout打印确实可以正常工作;但是这样做可能意味着使用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')

#1


5  

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

#2


11  

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. 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和self.stderr,而不是直接打印到stdout和stderr。通过使用这些代理,测试自定义命令变得更加容易。另请注意,除非指定结束参数,否则不需要使用换行符结束消息,它将自动添加。

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. But you may not want this to be reflected in future calls of 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.ending ='',您正在修改self.stdout对象的属性。但您可能不希望在将来调用self.stdout.write()时反映出这一点。因此,最好在self.stdout.write()函数中使用结束参数(如上面的示例代码所示)。

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." 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. Also I would like to mention that print() and OutputWrapper.write() behaves quite similar both acting as a wrapper around sys.stdout.write().

正如您所提到的“但是这个hack意味着您没有获得打印功能的所有功能,您必须使用self.stdout.write并手动准备字节。”不,您不必担心创建print()的字节或其他功能,因为属于OutputWrapper类的self.stdout.write()函数需要数据为str格式。另外我想提一下,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. It might help in clearing some of your doubts. You may also check PEP-3105 related to Python 3's print().

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

#3


5  

First of all, self.stdout is an instance of django.core.management.base.OutputWrapper command. Its write expects an str, not bytes, thus you can use

首先,self.stdout是django.core.management.base.OutputWrapper命令的一个实例。它的写入期望str,而不是字节,因此您可以使用

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确实接受字节,但只有当结尾是一个空字符串时 - 这是因为它的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.

如果结尾为true,那么如果msg是一个字节实例并且结尾是str,则msg.endswith(结束)将失败。

Furthermore, print with self.stdout does work correctly when I set the self.stdout.ending = '' explicitly; however doing this might mean that other code that uses self.stdout.write expecting it to insert newlines, would fail.

此外,当我明确设置self.stdout.ending =''时,使用self.stdout打印确实可以正常工作;但是这样做可能意味着使用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')