自己写 Flask 扩展要遵守哪些约定?连接之类的请求级资源放哪、怎么关?状态为什么不能放在扩展对象上?这篇写一个完整的最小扩展,每条约定都跑一遍。
核心结构(节选):
1 | class Hello:
2 | def __init__(self, app=None, greeting=None):
3 | self._greeting = greeting
4 | if app is not None: self.init_app(app)
5 | def init_app(self, app):
6 | app.config.setdefault("HELLO_GREETING", "hello")
7 | app.extensions["hello"] = {"inst": self, "greeting": self._greeting or app.config["HELLO_GREETING"]}
8 | app.teardown_appcontext(self._teardown)
9 | ...
同一个 hello 对象,应用 1: hello, Ann
应用 2(HELLO_GREETING=你好): 你好, Ann
配置里被填入的默认值: {'HELLO_GREETING': 'hello', 'HELLO_DSN': 'mem://default'}
对同一个应用重复 init_app → Hello 已经在这个应用上初始化过了
应用上下文之外使用 → Hello 需要应用上下文:请在 app.app_context() 里使用
没 init_app 的应用里使用 → 这个应用没有 init_app(Hello)上一篇:Flask-CORS;下一篇:Flask-SQLAlchemy 初始化过程。
第 71 篇讲了 init_app 的时机,这一篇把约定落成代码。
模板过滤器 shout: HEY! after_request 加的头 X-Hello: hello | 正文: hello, world flask hello Bob → hello, Bob (cli) (退出码 0 ) flask --help 的 Commands: ['hello', 'limiter', 'routes', 'run', 'shell']
5 个请求(2 个不用连接、3 个用):opened=3 closed=3 视图抛异常(HTTP 500)之后:opened=1 closed=1(teardown 照样执行)
bad 8 个并发请求(各睡 0.2 秒再读自己写的值):读到别人的值的请求 7 个 good 8 个并发请求(各睡 0.2 秒再读自己写的值):读到别人的值的请求 0 个
并发请求用的是 gunicorn 的 gthread worker(每个请求一个线程)。
构造参数 greeting="ctor" + HELLO_GREETING="cfg" → ctor, x 只有 HELLO_GREETING="cfg" → cfg, x 都没设 → hello, x
3 个互相独立的应用的响应: ['g0, world', 'g1, world', 'g2, world'] FakeConn 是类属性计数器——共享的状态会让测试互相污染:各用例开始前要手动清零 → opened = 0