如果你的 python 版本 >= 3.7,那么你不需要做任何事情。如果您有 python 3.6,请参阅原始解决方案。
编辑 2017-12-08
我已经看到 py3.7 有一个 PEP 538,它将在启动期间改变 python3 编码管理的整个行为,我认为新方法将解决原来的问题:https ://www.python.org/开发/peps/pep-0538/
恕我直言,针对 python 3.7 的编码问题的更改,应该在几年前就已经计划好了,但我猜,迟到总比没有好。
编辑 2015-09-01
有一个未解决的问题(增强),http ://bugs.python.org/issue15216,它将允许轻松更改已创建(未使用)流中的编码(sys.std*)。但是针对python 3.7 所以,我们还得等一会儿。
针对python 3.6版的原始解决方案
注意:运行 python 版本 >= 3.7 的任何人都不需要此解决方案,请参阅 PEP 538
好吧,我最初的解决方法有很多缺陷,我必须通过click
有关编码的库检查,但编码本身并不固定,所以当输入参数或输出包含非 ascii 字符时会出现异常。
我必须实现一个更复杂的方法,有 3 个步骤:设置语言环境,在 std in/out 中正确编码并重新编码命令行参数,此外,如果第一次尝试设置语言环境,我添加了一个“友好”退出没有按预期工作:
def prevent_ascii_env():
"""
To avoid issues reading unicode chars from stdin or writing to stdout, we need to ensure that the
python3 runtime is correctly configured, if not, we try to force to utf-8,
but It isn't possible then we exit with a more friendly message that the original one.
"""
import locale, codecs, os, sys
# locale.getpreferredencoding() == 'ANSI_X3.4-1968'
if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
os.environ['LANG'] = 'en_US.utf-8'
if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
print("The current locale is not correctly configured in your system")
print("Please set the LANG env variable to the proper value before to call this script")
sys.exit(-1)
#Once we have the proper locale.getpreferredencoding() We can change current stdin/out streams
_, encoding = locale.getdefaultlocale()
import io
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding=encoding, errors="replace", line_buffering=True)
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding=encoding, errors="replace", line_buffering=True)
sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding=encoding, errors="replace", line_buffering=True)
# And finally we need to re-encode the input parameters
for i, p in enumerate(sys.argv):
sys.argv[i] = os.fsencode(p).decode()
这个补丁解决了几乎所有的问题,但是它有一个警告,该方法shutils.get_terminal_size()
引发了 aValueError
因为sys.__stdout__
已经分离,click
lib 使用该方法打印帮助,要修复它我必须在click
lib上应用猴子补丁
def wrapper_get_terminal_size():
"""
Replace the original function termui.get_terminal_size (click lib) by a new one
that uses a fallback if ValueError exception has been raised
"""
from click import termui, formatting
old_get_term_size = termui.get_terminal_size
def _wrapped_get_terminal_size():
try:
return old_get_term_size()
except ValueError:
import os
sz = os.get_terminal_size()
return sz.columns, sz.lines
termui.get_terminal_size = _wrapped_get_terminal_size
formatting.get_terminal_size = _wrapped_get_terminal_size
通过这些更改,当环境配置了错误的语言环境但系统支持 en_US.utf-8(这是 Fedora 默认语言环境)时,我的所有脚本现在都可以正常工作。
如果您发现此方法有任何问题或有更好的解决方案,请添加新答案。