Fabric is convenient, isn't it? It's so convenient that you can do most of it with Fabric.
However, since Fabric is made by Python, handling unicode can sometimes be a problem. Specifically, unicode cannot be passed to fabric.api.abort or fabric.api.puts. abort writes str to sys.stderr and puts writes str to sys.stdout. Passing unicode will result in a UnicodeEncodeError.
To avoid this, you need to convert it to str instead of unicode in advance.
from fabric.api import abort, task
@task
def abort_test():
abort(u"Japanese error message".encode('CP932'))
That's okay, but it's awkward to encode everywhere you use it.
If the argument passed to abort is unicode, try converting it according to the encoding of the output destination.
fabric_patch.py
# -*- encoding: utf-8 -*-
import sys
import fabric
from fabric.api import abort, puts
encodings = {
abort: sys.stderr.encoding,
puts: sys.stdout.encoding,
}
def safe_msg(original):
u"""
fabric.api.abort/If you pass unicode to puts, you will get a UnicodeEncodeError on output, so
Decorator to avoid it
"""
def revised(msg, *args, **kwargs):
if isinstance(msg, unicode):
encoding = encodings.get(original, None)
if encoding:
msg = msg.encode(encoding)
return original(msg, *args, **kwargs)
return revised
setattr(fabric.api, "abort", safe_msg(abort))
setattr(fabric.api, "puts", safe_msg(puts))
Place fabric_patch.py in the same directory as fabfile.py and import it before abort. Now you can output Japanese error messages without any problems without converting unicode.
fabfile.py
# -*- encoding: utf-8 -*-
from __future__ import absolute_import
from . import fabric_patch
from fabric.api import abort, task
@task
def abort_test():
abort(u"Japanese error message")