Flask第三方插件扩展原理

flask这种轻量级的框架本身并没有包含我们需要的功能,因此必须通过扩展插件才可以。今天学习了FLask的扩展插件导入(import)原理。
通常我们import第三方插件时,如:flask.ext.principal import identity_loaded。
当执行该语句时,首先要执行flask/ext/ init .py模块,该模块内函数如下:

def setup(): from ..exthook import ExtensionImporter importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], name ) importer.install()

setup() del setup

看如上代码,会创建一个importer类,执行install方法,再往下看,调到ExtensionImporter类。

def install(self): sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]

sys.meta_path要优先于sys.path。
也就是说当执行flask.ext.principal import identity_loaded时,会调用find_modules(flask.ext.principal),根据重载的函数,我们可以找到真实的模块名称(一般为flask_principal,flaskext_principal),即作用相当于import flask_principal。下面是find_module和load_module的method.

code>
def find_module(self, fullname, path=None):
        if fullname.startswith(self.prefix):
            return self

def load_module(self, fullname):
    if fullname in sys.modules:
        return sys.modules[fullname]
    modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]
    for path in self.module_choices:
        realname = path % modname
        try:
            __import__(realname)
        except ImportError:
            exc_type, exc_value, tb = sys.exc_info()
            # since we only establish the entry in sys.modules at the
            # very this seems to be redundant, but if recursive imports
            # happen we will call into the move import a second time.
            # On the second invocation we still don't have an entry for
            # fullname in sys.modules, but we will end up with the same
            # fake module name and that import will succeed since this
            # one already has a temporary entry in the modules dict.
            # Since this one "succeeded" temporarily that second
            # invocation now will have created a fullname entry in
            # sys.modules which we have to kill.
            sys.modules.pop(fullname, None)

            # If it's an important traceback we reraise it, otherwise
            # we swallow it and try the next choice.  The skipped frame
            # is the one from __import__ above which we don't care about
            if self.is_important_traceback(realname, tb):
                reraise(exc_type, exc_value, tb.tb_next)
            continue
        module = sys.modules[fullname] = sys.modules[realname]
        if '.' not in modname:
            setattr(sys.modules[self.wrapper_module], modname, module)
        return module
    raise ImportError('No module named %s' % fullname)

--------EOF---------
微信分享/微信扫码阅读