如何让'import x'返回types.ModuleType的子类?

时间:2011-05-12 19:16:05

标签: python

Python的import语句可以使用导入钩子返回types.ModuleType的子类吗?我想覆盖__getattribute__以在特定模块之外的代码引用不在__all__中的名称时显示运行时警告。

我知道如何在导入后替换sys.modules['foo']。我想要的是在导入时模式匹配模式的模块,这样导入代码就有机会触发警告。

Python对于公开和私有的想法不屑一顾。这个想法不是要阻止模块的用户输入from somemodule import sys;相反,它是一个文档工具。这种检测应该通过包含正确的__all__来更轻松地记录模块的API。它应该可以帮助您避免意外地将sys引用为somemodule.sys,而不仅仅是import sys

3 个答案:

答案 0 :(得分:5)

由于我在你编辑之前错过了你的问题,我想我会再接受一次打击(留下原来的答案给后人)。

这是一个也不需要导入钩子的替代方案。它可以在逐个模块的基础上轻松使用:包含此代码的模块将具有特殊的__getattribute__()行为,而其他模块将像往常一样运行。

class StrictModule(types.ModuleType):

    def __getattribute__(self, key):
        if key is "__dict__":  # fake __dict__ with only visible attributes
            return dict((k, v) for k, v in globals().iteritems()
                        if k.startswith("__") or k in __all__)
        if (key.startswith("__") or key in __all__) and key in globals():
            return globals()[key]
        else:
            raise AttributeError("'module' object has no attribute '%s'"  % key)

    def __setattr__(self, key, value):
        globals()[key] = value

sys.modules[__name__] = StrictModule(__name__)

请注意,只需调用常规module类型的__getattribute__()(或者object类型的),就可以轻松解决此“限制”问题。我的印象是你试图为你的模块提供某种“私人成员”限制。对此几乎没有任何意义。

答案 1 :(得分:3)

您甚至不需要导入钩子。只需在Python的模块注册表sys.modules中以所需名称引用该对象,Python会话中的任何未来import语句(即使在其他模块中)都将导入对该实体的引用。

import types, sys

class MyModuleType(types.ModuleType):
    pass

sys.modules["foo"] = MyModuleType("foo")

import foo

print type(foo)   # MyModuleType

Python甚至不关心sys.modules中的对象是否实际上是模块或其他类型的对象。你可以在那里放一个int而不会给出一个f ***。

sys.modules["answer"] = 42
import answer

答案 2 :(得分:3)

你可以调整this ActiveState recipe,也许是这样的:

# safemodule.py
import sys
import types
import warnings

class EncapsulationWarning(RuntimeWarning): pass

class ModuleWrapper(types.ModuleType):
    def __init__(self, context):
        self.context = context
        super(ModuleWrapper, self).__init__(
                context.__name__,
                context.__doc__)

    def __getattribute__(self, key):
        context = object.__getattribute__(self, 'context')
        if hasattr(context, '__all__') and key not in context.__all__:
            warnings.warn('%s not in %s.__all__' % (key, context.__name__),
                          EncapsulationWarning,
                          2)
        return context.__getattribute__(key)

if 'old_import' not in globals():
    old_import = __import__

    def safe_import(*args, **kwargs):
        m = old_import(*args, **kwargs)
        return ModuleWrapper(m)

    __builtins__['__import__'] = safe_import

然后,像这样使用它:

C:\temp>python
ActivePython 2.5.2.2 (ActiveState Software Inc.) based on
Python 2.5.2 (r252:60911, Mar 27 2008, 17:57:18) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import safemodule
>>> import sys
>>> type(sys)
<class 'safemodule.ModuleWrapper'>
>>>

当然,您可以将其修改为仅包装某些模块等。