Boost-Python:使用路径中带有Unicode字符的加载Python模块
我正在做一个游戏项目,使用的是 Python 2.7.2 来编写脚本。我的应用程序在使用非 Unicode 路径的 .exe 文件时运行得很好。但是,当我尝试用 Unicode 路径加载脚本时,就出现问题了,具体是用到了 boost::python::import (import_path.c_str()); 这段代码。
我还试过一个例子,叫做 5.3. 纯嵌入,链接在这里:http://docs.python.org/extending/embedding.html#embedding-python-in-c
这个例子也无法处理 Unicode 路径。我是把 Python 作为 DLL 链接的。请告诉我,如何处理这样的路径。
2 个回答
这不是一个完全符合你需求的答案,但也许能给你一些启发。
我在使用Python时遇到了一个非常类似的问题,我的应用程序是纯Python写的。我发现,如果我的应用程序安装在一个路径字符串无法用MBCS编码的目录下(根据我的理解,至少在Python 3.2之前,Python内部会将其转换为这种编码),那么Python解释器就会出错,声称找不到那个模块。
为了解决这个问题,我不得不写一个导入钩子,来欺骗它,让它还是能加载那些文件。
这是我想到的解决办法:
import imp, os, sys
class UnicodeImporter(object):
def find_module(self,fullname,path=None):
if isinstance(fullname,unicode):
fullname = fullname.replace(u'.',u'\\')
exts = (u'.pyc',u'.pyo',u'.py')
else:
fullname = fullname.replace('.','\\')
exts = ('.pyc','.pyo','.py')
if os.path.exists(fullname) and os.path.isdir(fullname):
return self
for ext in exts:
if os.path.exists(fullname+ext):
return self
def load_module(self,fullname):
if fullname in sys.modules:
return sys.modules[fullname]
else:
sys.modules[fullname] = imp.new_module(fullname)
if isinstance(fullname,unicode):
filename = fullname.replace(u'.',u'\\')
ext = u'.py'
initfile = u'__init__'
else:
filename = fullname.replace('.','\\')
ext = '.py'
initfile = '__init__'
if os.path.exists(filename+ext):
try:
with open(filename+ext,'U') as fp:
mod = imp.load_source(fullname,filename+ext,fp)
sys.modules[fullname] = mod
mod.__loader__ = self
return mod
except:
print 'fail', filename+ext
raise
mod = sys.modules[fullname]
mod.__loader__ = self
mod.__file__ = os.path.join(os.getcwd(),filename)
mod.__path__ = [filename]
#init file
initfile = os.path.join(filename,initfile+ext)
if os.path.exists(initfile):
with open(initfile,'U') as fp:
code = fp.read()
exec code in mod.__dict__
return mod
sys.meta_path = [UnicodeImporter()]
不过使用这个方法时我还是遇到了两个问题:
- 在Windows资源管理器中双击启动文件(.pyw文件)时,如果应用程序安装在一个有问题的目录下,就无法正常工作。我认为这和Windows文件关联如何将参数传递给pythonw.exe有关(我猜Windows会将完整的路径字符串,包括那些无法编码的字符,作为参数传递给exe)。如果我创建一个批处理文件,让它调用Python可执行文件,只用启动器的文件名,并确保从同一目录启动,那就能正常启动。再次,我猜这是因为现在我可以使用相对路径作为python.exe的参数,从而避免路径中的那些问题字符。
- 使用py2exe打包我的应用程序时,如果将生成的exe放在这些有问题的路径下,它就无法运行。我认为这和zipimporter模块有关,而这个模块不幸的是是一个编译过的Python模块,所以我不能轻易修改它(我得重新编译等等)。
boost::python::import
需要一个 std::string
类型的字符串,所以可能是 import_path
里缺少了一些字符。
你需要在多个平台上工作吗?在Windows系统上,你可以使用 GetShortPathName 这个函数来获取短文件名,然后用这个短文件名来加载你的dll文件。
你可以做个简单的测试:
- 把你的扩展名改成 "JaiDéjàTestéÇaEtJaiDétestéÇa.pyd"。
- 在命令行中输入
dir /x *.pyd
来获取短文件名(在我电脑上是 JAIDJT~1.PYD)。 - 用这个短文件名来加载你的扩展。
+上面的文件名是法语,意思是“我已经测试过这个了,但我不喜欢它”。这个名字有点押韵,让处理Unicode的工作变得轻松一些;)