Boost-Python:使用路径中带有Unicode字符的加载Python模块

2 投票
2 回答
677 浏览
提问于 2025-04-17 12:07

我正在做一个游戏项目,使用的是 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 个回答

0

这不是一个完全符合你需求的答案,但也许能给你一些启发。

我在使用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()]

不过使用这个方法时我还是遇到了两个问题:

  1. 在Windows资源管理器中双击启动文件(.pyw文件)时,如果应用程序安装在一个有问题的目录下,就无法正常工作。我认为这和Windows文件关联如何将参数传递给pythonw.exe有关(我猜Windows会将完整的路径字符串,包括那些无法编码的字符,作为参数传递给exe)。如果我创建一个批处理文件,让它调用Python可执行文件,只用启动器的文件名,并确保从同一目录启动,那就能正常启动。再次,我猜这是因为现在我可以使用相对路径作为python.exe的参数,从而避免路径中的那些问题字符。
  2. 使用py2exe打包我的应用程序时,如果将生成的exe放在这些有问题的路径下,它就无法运行。我认为这和zipimporter模块有关,而这个模块不幸的是是一个编译过的Python模块,所以我不能轻易修改它(我得重新编译等等)。
1

boost::python::import 需要一个 std::string 类型的字符串,所以可能是 import_path 里缺少了一些字符。

你需要在多个平台上工作吗?在Windows系统上,你可以使用 GetShortPathName 这个函数来获取短文件名,然后用这个短文件名来加载你的dll文件。

你可以做个简单的测试:

  1. 把你的扩展名改成 "JaiDéjàTestéÇaEtJaiDétestéÇa.pyd"。
  2. 在命令行中输入 dir /x *.pyd 来获取短文件名(在我电脑上是 JAIDJT~1.PYD)。
  3. 用这个短文件名来加载你的扩展。

+上面的文件名是法语,意思是“我已经测试过这个了,但我不喜欢它”。这个名字有点押韵,让处理Unicode的工作变得轻松一些;)

撰写回答