昨晚群里有同学求助了一个 pip 安装一个名为 wx_py 的包的过程中的一个报错问题。
刚好我在线,所以我也牵头一起做了一次小小的探索,现在回过头来总结一下。
首先在可以确认,出现问题的环境是 Python 3.7,我自己的 Windows 环境里面,也复现了这个问题。
疯狂 Google
Google 搜索 python UnicodeDecodeErrror gbk
,发现了一个相似的问题,有一个比较清晰的回答。
pip install的时候提示字符编码错误 · Issue #330 · sshwsfc/xadmin
https://github.com/sshwsfc/xadmin/issues/330
由此可见,应该是这个 pip 包安装过程,在执行 setup.py 的时候,执行的是一个不带 encoding 参数的 open 函数,结果在简体中文 Windows 下,因为默认编码是 gbk,Python 按照 gbk 的方式解析不了 README.rst 中的 utf-8 的字符,导致安装不能进行。
尝试操控 open 的参数
有没有办法能让 Python 的 open 函数认为它是 utf-8 呢?带着这个问题开始了进一步探索。
这里可以想到两条思路:
- 改代码,给 open 加上 encoding='utf-8',再通过某种方式手动安装
- 通过某种方式来运行 pip,在外面通过某种参数修改控制到 open 函数的默认 encoding 这个参数,让它完全以 utf-8 的方式执行完安装的过程。
思路1
看到 pip 运行的时候会将文件解压到一个临时目录里面,通过改代码的方式来实现,但修改的过程发现,每次运行 pip 这个临时目录都是不同的后缀?如果有更多的依赖包的话,手动一个个安装也是一件麻烦的事情,不如先试试另外一种思路。
Python 要读取系统的 locale,也许我们可以通过某种方式来启动 Python 来达到这种效果?于是,第二条路开始了。
思路2
首先,提取关键词,反复尝试 Google,最后以关键词 python3 open encoding
,挖到了一个 StackOverflow 的网址,通过它指路到了 Python 3.7 的官方文档对于 open 函数的介绍:
https://stackoverflow.com/questions/36303919/python-3-0-open-default-encoding
https://docs.python.org/3.7/library/functions.html#open
其中关键在于默认的 encoding 的获取。通过 locale 模块的 getpreferredencoding 可以拿到。
import locale
locale.getpreferredencoding()
在 Windows 下,输出的是 cp936,就是微软 Windows 的 code page 936 号,正好对应的是 gbk 编码:
而在 Linux 环境拿到的是 UTF-8。
所以这里的思路走向了,如何通过一些方式去实现控制这里获取的 encoding。
搜索到到一篇文章,在 Linux 下可以通过环境变量来设置程序运行的 Locale
Set the locale to UTF-8 for Python 3 - Webkul Blog
https://webkul.com/blog/setup-locale-python3/
但是它是在 Linux 下,所以接下来应该找一下能不能实现在 Windows 临时改变 locale 的方法。
开始@Tover 提到了 chcp 的方法,尝试了 chcp 65001
,但不管用,拿到的还是 cp936。
继续搜索,又在 StackOverflow 发现了相似的需求。
Changing the “locale preferred encoding” in Python 3 in Windows - Stack Overflow
但是,回答都是要改代码,就很。。。不知道有没有别的手段。还是说只能通过手改代码的方式来安装了?
评论区的哥们打消了我的这个念头,这里的 getpreferredencoding 是直接用的 Windows 的 GetACE 的系统调用,我们并不能简单触及到这个层次的修改。
所以这里的结论是,Windows 下,并不支持临时改变 locale,只能选择手改的方式了。
真正的问题所在
确认了这个点的解决方案之后,手动安装好了这个包,还是出现了各种依赖的问题。
最后尝试在 Linux 环境用 pip 安装同样的包,安装了两个依赖,然后继续发生依赖错误,安装失败。
看到报错信息的 pathlib2,猛然反应过来,莫非,这个包只是在 Python 2 环境跑的?
登录 PyPi 看 wx_py 这个包发现,最近更新的日期是 2015年4月13日,这个猜想越来越强烈。
然后尝试在 Windows 的 Python 2 环境用 pip 安装了这个包,一路畅通无阻!
看到 wxPython 的关键字,参考 wxPython 的文档,尝试了一下 Hello World,正常运行。
印证了我这个包只适合 Python 2 的猜想。
所以,最终得出的解决方案是:
安装一个 Python 2.7,然后通过 Python 2 的 pip 来安装这个包。
总结经验:
- Python 3 的 open 函数在未传入 encoding 参数时会跟随系统的 locale 配置。
- Windows 下,不像 Linux 环境可以通过环境变量的方式改变 locale,除了修改代码以外暂时没有其它办法。
- Windows 下 Python 的安装还是比较混乱,没有统一的标准,教程千奇百怪,容易把人带入歧途(也许是一个比较旺盛的需求点,非计算机专业使用 Python 经常撞这样的坑。
- 这里我有一点点陷入了 X-Y Problem 的误区,一开始就往 Python 3 的错误解决方向去思考,最后发现这个包本来就不适用于 Python 3,一开始没意识到。但由于还是学习的阶段,这段延伸还是蛮有价值的。