【问题标题】:Encoding issue with python3 and click packagepython3的编码问题并单击包
【发布时间】:2015-11-20 22:50:12
【问题描述】:

当 libclick 检测到运行时是 python3 但编码是 ASCII 时,它会突然结束 python 程序:

RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Either switch to Python 2 or consult http://click.pocoo.org/python3/ for mitigation steps.

我发现了这个问题的原因,当我从我的 Mac 连接到我的 Linux 主机时,Terminal.app 将 SSH 会话语言环境设置为我的 Mac 语言环境 (es_ES.UTF-8) 但是我的 Linux 主机没有'没有安装这样的语言环境(仅 en_US.utf-8)。

我应用了一个初步的解决方法来修复它(但它有很多问题,请参阅接受的答案):

import locale, codecs
# locale.getpreferredencoding() == 'ANSI_X3.4-1968'
if codecs.lookup(locale.getpreferredencoding()).name == 'ascii':
    os.environ['LANG'] = 'en_US.utf-8'

编辑:要获得更好的补丁,请参阅我接受的答案。

我所有的 linux 主机都安装了“en_US.utf-8”作为语言环境(Fedora 使用它作为默认设置)。

我的问题是:有没有更好(更强大)的方法来选择/强制在 python3 脚本中的语言环境?例如,在系统中设置一个可用的语言环境。

也许有其他方法可以解决此问题,但我没有找到。

【问题讨论】:

  • 仅供参考,供那些想知道“为什么 python3 会因为诸如未设置语言环境(即环境变量LANGLC_ALL)之类的小问题而出错”-> 阅读PEP 538 和相关的@987654323 @。该错误似乎只是 python 3.0 到 3.6 的问题,因为 PEP 538 修复了 python >= 3.7 的问题。

标签: python-3.x encoding command-line-interface python-click


【解决方案1】:

如果您运行的是 python 3.6,那么您仍然会收到此错误。这是click的作者推荐的一个简单的解决方案:

#!/bin/bash
# before your python code executes set two environment variables
export LANG=en_US.utf8
export LC_ALL=en_US.utf8

【讨论】:

  • 问题是关于如何在 python 代码中修复它,而不是在调用 que 程序之前
【解决方案2】:

如果你的 python 版本 >= 3.7,那么你不需要做任何事情。如果您有 python 3.6,请参阅原始解决方案。

编辑 2017-12-08

我看到py3.7有一个PEP 538,它会在启动期间改变python3编码管理的整个行为,我认为新方法将解决原来的问题:https://www.python.org/dev/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() 方法引发了一个ValueError 因为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 默认语言环境)时,我的所有脚本现在都可以正常工作了。

如果您发现此方法有任何问题或有更好的解决方案,请添加新答案。

【讨论】:

  • 看起来问题在 Python 3.7 中不再存在。此外,如果是,则可以使用新的 sys.stdout/err/in.reconfigure(encoding='utf-8') 调用。
  • 我发现一个更简单的解决方案是两行 bash shell export LANG=en_US.utf8export LC_ALL=en_US.utf8 在你的 python 代码必须执行之前(用你的语言环境配置的任何值替换值)--> @ 987654323@.
  • 不,这不是这个问题的解决方案,问题是关于如何在python代码中修复它,在python代码不是一个选项之前做。
【解决方案3】:

我还没有找到这个简单的方法(在做任何事情之前用适当的环境重新执行脚本),所以出于某种原因,我会为未来使用旧 Python 版本的旅行者添加它。将它添加到下面的 import 是第一个:

if os.environ["LC_ALL"] != "C.UTF-8" or os.environ["LANG"] != "C.UTF-8":
    os.execve(sys.executable,
              [os.path.realpath(__file__)] + sys.argv,
              {"LC_ALL": "C.UTF-8", "LANG": "C.UTF-8"})

【讨论】:

    【解决方案4】:

    这是一个古老的线程,但是这个答案可能会在未来帮助其他人或我自己。如果是*nux

    env | grep LC_ALL

    如果已设置,请执行以下操作。就是这样。

    unset LC_ALL

    【讨论】:

    • 问题从来不是从控制台修复它,而是从 python 代码,一旦应用程序运行。
    • @reberto 需要在python 存在之前运行unset LC_ALL
    • 谢谢@caot,但正如我所说,修复环境之前启动python程序不是一个选项,问题是关于如何处理损坏的语言环境内部一个已经在运行的python程序。
    • 我知道这不是您想要的答案,但它是迄今为止解决问题的最简单方法。在启动 Python 程序之前,使用包装脚本启动您的应用程序,并在其中设置 export LC_ALL=en_US.UTF-8; export LANG=en_US.UTF-8(macOS 上)或 export LC_ALL=C.UTF-8; export LANG=C.UTF-8(Linux 上)。
    • @Roberto 你可以试试 import locale locale.setlocale(locale.LC_ALL, '') 参考:docs.python.org/2/library/locale.html and docs.python.org/3/library/locale.html
    猜你喜欢
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    相关资源
    最近更新 更多