Python-Swift互操作PythonKit:更改默认的Stdin编码 更新:

问题描述

我已经使用PythonKit在使用XCode的Swift软件包(SPM)中将自定义Python软件包导入了Swift。各个地方的Python代码执行f = open("somefile","r"),然后执行f.readline()

从Swift调用时,从Python内部引发以下错误


File "/Users/user/[...]/lib/python3.8/site-packages/module/unihan_variants.py",line 115,in unihan_variants_dict
    line = f.readline()

...

Python exception: 'ascii' codec can't decode byte 0xc2 in position 121: ordinal not in range(128)

进一步研究,我发现认的stdin解码是ascii而不是utf-8:

let sys = Python.import("sys")
print(sys.stdin.encoding) // asciii
print(sys.stdout.encoding) // ascii

当我转到原始的python模块时,我发现可以通过指定f = open("somefile","r",encoding="utf-8")来避免PythonKit中的错误,但是不幸的是,到目前为止,我只是在所有项目中都假定使用utf-8。

是否可以通过XCode或PythonKit更改stdin和stdout的认编码?

还是有必要/建议回到我的所有Python代码中并指定utf-8?

(这不起作用):

sys.stdin.encoding = "utf-8" //Python exception: readonly attribute: file /Users/brianparker/Library/Developer/Xcode/DerivedData/Morphology-efqracfjfhxtguetmduhdszyzezb/SourcePackages/checkouts/PythonKit/PythonKit/Python.swift,line 540

更多说明:

  • print(platform.python_version())返回3.8.5
  • 我导入Python软件包的方法是,将virtualenv中site-packages的路径添加到Swift / PythonKit中的sys.path
  • print(sys.getdefaultencoding())表示“ utf-8”。似乎与stdin / stdout不同

更新:

通过设置sys.stdin.encoding环境变量,我设法使PYTHONIOENCODING表示“ utf-8”。但是,相同的错误也会引发到readline()

解决方法

Python的TextIOWrapper open的默认编码不是由我的问题中建议的sys属性或环境变量确定的。而是默认为locale.getpreferredencoding(False)

我的Python环境显示以下内容,这说明了为什么打开文件时通常不需要指定编码:

>>> locale.getlocale()
('en_US','UTF-8')
>>> locale.getpreferredencoding(do_setlocale=False)
'UTF-8'

但是Swift中的Python没有默认语言环境(至少在我的计算机上运行时):

let locale = Python.import("locale")
print(locale.getlocale()) // (None,None)

以下代码将在Swift应用程序中为Python设置所需的语言环境,从而在打开文件时允许TextIOWrapper默认为utf-8:

let locale = Python.import("locale")
if locale.getlocale().tuple2 == (Python.None,Python.None) {
   locale.setlocale(locale.LC_ALL,locale: PythonObject(["en_US","UTF-8"]))
}

请注意,从2019年开始有Python proposal会在不检查locale的情况下进行utf-8 TextIOWrapper的默认编码,尽管我不确定是否会采用它。