一. 扩展
Flask通常需要一些第三方库来工作,这些第三库通常可以分离出来。
二. 代码
关于载入扩展的代码存放于flask/ext/__init__.py和flask/exthook.py文件中,分别看看这两个文件
- flask/ext/__init__.py
```python
-- coding: utf-8 --
def setup(): from ..exthook import ExtensionImporter importer = ExtensionImporter([‘flask_%s’, ‘flaskext.%s’], name) importer.install()
setup() del setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
这段代码很简单,允许扩展名为flask_name或者flaskext.name的命名包,并安装此扩展包。
2. flask/exthook.py
```python
# -*- coding: utf-8 -*-
import sys
import os
import warnings
from ._compat import reraise
class ExtDeprecationWarning(DeprecationWarning):
pass
warnings.simplefilter('always', ExtDeprecationWarning)
class ExtensionImporter(object):
def __init__(self, module_choices, wrapper_module):
self.module_choices = module_choices
self.wrapper_module = wrapper_module
self.prefix = wrapper_module + '.'
self.prefix_cutoff = wrapper_module.count('.') + 1
def __eq__(self, other):
return self.__class__.__module__ == other.__class__.__module__ and \
self.__class__.__name__ == other.__class__.__name__ and \
self.wrapper_module == other.wrapper_module and \
self.module_choices == other.module_choices
def __ne__(self, other):
return not self.__eq__(other)
def install(self):
sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]
def find_module(self, fullname, path=None):
if fullname.startswith(self.prefix) and \
fullname != 'flask.ext.ExtDeprecationWarning':
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]
warnings.warn(
"Importing flask.ext.{x} is deprecated, use flask_{x} instead."
.format(x=modname), ExtDeprecationWarning, stacklevel=2
)
for path in self.module_choices:
realname = path % modname
try:
__import__(realname)
except ImportError:
exc_type, exc_value, tb = sys.exc_info()
sys.modules.pop(fullname, None)
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)
if realname.startswith('flaskext.'):
warnings.warn(
"Detected extension named flaskext.{x}, please rename it "
"to flask_{x}. The old form is deprecated."
.format(x=modname), ExtDeprecationWarning
)
return module
raise ImportError('No module named %s' % fullname)
def is_important_traceback(self, important_module, tb):
while tb is not None:
if self.is_important_frame(important_module, tb):
return True
tb = tb.tb_next
return False
def is_important_frame(self, important_module, tb):
g = tb.tb_frame.f_globals
if '__name__' not in g:
return False
module_name = g['__name__']
if module_name == important_module:
return True
filename = os.path.abspath(tb.tb_frame.f_code.co_filename)
test_string = os.path.sep + important_module.replace('.', os.path.sep)
return test_string + '.py' in filename or \
test_string + os.path.sep + '__init__.py' in filename
ExtensionImporter类主要是重导向flaskext.foo,用flask_foo替代,不需要升级旧的第三方库。
三. 解析
flask的扩展模块的导入实现非常简洁,主要是以下几个步骤,当导入from falsk.ext.foo import F时
- 首先执行的是ext/__.init__.py文件,实例化对象importer将调用install函数,向sys.meta_path添加模块装载类
- 当import时会调用其find_module,如果返回非None,会调用load_module加载