使用PEP 302 Importer Protocol导入Python伪Java样式包

时间:2014-02-06 20:39:17

标签: python import namespaces

我正在尝试实现PEP 302中指定的导入器协议来创建Java样式的包导入器机制。这样做的好处是,包命名空间可以合并虚拟,但实际上并没有物理合并。

假设sys.path上列出的两个目录名都包含文件夹org。但是两个文件夹都有不同的内容。

文件夹1

org/
    a_module.py

文件夹2

org/
    a_package/
        __init__.py
        tools.py

现在,从Python代码开始,我想编写以下内容,以便能够将a_modulea_package.tools导入到我的Python程序中。

sys.meta_path.append(PackageImporter('org'))
import org.a_module
import org.a_package.tools as tools

现在,我遇到的一个奇怪的问题是,从通过我的自定义加载程序加载的包中导入os模块,我得到ImportError: no module name path。不过我可以毫无问题地导入很多其他软件包!

例如,org/a_module.py

# file: org/a_module.py
import os

test.py

# file: test.py
sys.meta_path.append(PackageImporter())
import org.a_module # ImportError: no module named path

我该如何解决这个问题?

实施

遗憾的是,代码有点冗长。

import os
import sys
import imp

class PackageImporter(object):

    def trace_module(self, dirname, parts):
        r""" Traces a module by the specified import *parts*. Returns
        the full path to the module that is to be imported. Directories
        will later by imported by the EmptyModuleLoader class and files
        by the ModuleLoader class. """

        for part in parts[:-1]:
            dirname = os.path.join(dirname, part)
            if not os.path.isdir(dirname):
                return None

        directory = os.path.join(dirname, parts[-1])
        modulefile = os.path.join(dirname, '%s.py' % parts[-1])
        initfile = os.path.join(directory, '__init__.py')

        if os.path.isfile(modulefile):
            return modulefile
        elif os.path.isfile(initfile):
            return initfile
        elif os.path.isdir(directory):
            return directory
        else:
            return None

    def find_module(self, fullname, path=None):
        if path is None:
            path = sys.path

        parts = fullname.split('.')
        fullpath = None
        for dirname in path:
            fullpath = self.trace_module(dirname, parts)
            if fullpath:
                break

        if fullpath:
            if os.path.isdir(fullpath):
                return EmptyModuleLoader()
            else:
                return ModuleLoader(fullpath)

        return None

class EmptyModuleLoader(object):

    def load_module(self, fullname):
        mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
        mod.__file__ = '<lazy package>'
        mod.__package__ = fullname
        mod.__loader__ = self
        mod.__path__ = sys.path
        return mod

class ModuleLoader(object):

    def __init__(self, filename):
        self.filename = filename

    def load_module(self, fullname):
        mod = sys.modules.get(fullname)
        if mod:
            return mod

        description = ('.py', 'r', imp.PY_SOURCE)
        with open(self.filename) as fp:
            mod = imp.load_module(fullname, fp, self.filename, description)

        mod.__file__ = self.filename
        mod.__loader__ = self
        if os.path.basename(self.filename) == '__init__.py':
            mod.__package__ = fullname
            mod.__path__ = sys.path
        else:
            mod.__package__ = fullname.rpartition('.')[0]

        return mod

sys.meta_path.append(PackageImporter())

import org
import org.a_module
import org.a_package.tools as tools

以上代码是GitHub上当前实现的缩写形式。提出这个问题的国家是c77c1d4e

更新

对我来说最令人困惑的事实是,我的导入器甚至没有被os.path查询,而是我得到了org.posixpathorg.genericpath全名值??

def find_module(self, fullname, path=None):
    if 'path' in fullname:
        print "="*40
        print fullname
        print "="*40


Niklass-MacBook-Air:Desktop niklas$ python test.py 
========================================
org.posixpath
========================================
========================================
org.genericpath
========================================
Traceback (most recent call last):
  File "test.py", line 94, in <module>
    import org.a_module
  File "test.py", line 79, in load_module
    mod = imp.load_module(fullname, fp, self.filename, description)
  File "/Users/niklas/Desktop/org/a_module.py", line 2, in <module>
    import os
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py", line 120, in <module>
    from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,
ImportError: No module named path

(差)解决方法

目前,我已添加了声明

from __future__ import absolute_import

在执行修复问题的Python模块之前进入源代码。但这听起来不是一个很好的方法。

0 个答案:

没有答案