8👍
When setting self.stdout.ending
explicitly, the print command works as expected.
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
.
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
14👍
As is mentioned in Django 1.10’s Custom Management Commands document:
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='')
Hence, in order to print in your Command
class, you should define your handle()
function as:
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).
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()
.
The only difference between print()
and OutputWrapper.write()
is:
print()
accepts message string as*args
withseparator
parameter to join the the multiple strings, whereasOutputWrapper.write()
accepts single message string
But this difference could be easily handled by explicitly joining the strings with separator and passing it to 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.
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()
.
6👍
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.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
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
.
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.
In your case, what I’d do is to define a print
method for the 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')
You can make this into your own BaseCommand
subclass – and you can use it with different files too:
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')
- How to describe parameters in DRF Docs
- Is there any list of blog engines, written in Django?
- How to disable request logging in Django and uWSGI?
- Why is Django's Meta an old-style class?