问题
我有以下简单的脚本:
test.py
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logging.info("€")
根据调用脚本的上下文,可能会产生以下错误:
UnicodeEncodeError: 'ascii'编解码器无法将字符'\u20ac'编码到位置10:超出范围(128)
为什么会出现这种情况?我该如何解决?
我已经发现的问题
观察
当我“正常”调用此脚本时,没有问题:
$ python3 test.py
INFO:root:€
然而,当我创建一个 PHP 文件 /var/www/html/test.php:
<?php
echo "# locale\n\n";
passthru("locale");
echo "\n\n# python\n\n";
passthru("python3 /var/www/html/test.py 2>&1");
然后通过Apache调用此文件时,会出现错误:
$ curl localhost/test.php
# locale
LANG=C
LANGUAGE=de_DE.UTF-8
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=
# python
--- Logging error ---
Traceback (most recent call last):
File "/usr/lib/python3.5/logging/__init__.py", line 983, in emit
stream.write(msg)
UnicodeEncodeError: 'ascii' codec can't encode character '\u20ac' in position 10: ordinal not in range(128)
Call stack:
File "/var/www/html/test.py", line 5, in <module>
logging.info("\u20ac")
Message: '\u20ac'
Arguments: ()
与此相比,如果我直接调用locale
,我得到的结果如下:
$ locale
LANG=de_DE.UTF-8
LANGUAGE=de_DE.UTF-8
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_PAPER="de_DE.UTF-8"
LC_NAME="de_DE.UTF-8"
LC_ADDRESS="de_DE.UTF-8"
LC_TELEPHONE="de_DE.UTF-8"
LC_MEASUREMENT="de_DE.UTF-8"
LC_IDENTIFICATION="de_DE.UTF-8"
LC_ALL=de_DE.UTF-8
如果我将 PHP 中的
passthru
调用更改为以下内容:passthru("LANG=de_DE.UTF-8 python3 /var/www/html/openWB/test.py 2>&1");
那么一切正常工作。
LANG=C
是从哪里来的? 不是从这里来的:
cat /etc/default/locale
# File generated by update-locale
LANG=de_DE.UTF-8
LC_ALL=de_DE.UTF-8
LANGUAGE=de_DE.UTF-8
系统是Raspbian GNU/Linux 9(stretch)。
解释
显然,脚本的成功取决于我的用户设置。我曾经认为Python脚本在系统之间大多是可移植的。现在我知道它们甚至不能从一个用户移植到另一个用户 ;-)。当然,环境变量改变了相关应用程序的外观,这是可以接受的,但不是整个应用程序都会崩溃的保证。
我猜我需要更改Python脚本以强制使用UTF-8(如果其他任何操作失败,不太确定为什么这不是默认行为),或者我需要为PHP脚本设置LANG变量。对于这两个选项,问题是:最简单/最短/最有效的方法是什么?最好的情况是有一个单一的选项,我可以全局更改以解决整个系统的问题。具备Root访问权限。
请注意,我目前卡在Python 3.5.3上,不能轻松升级。