详解Python的Flask框架中的signals信号机制
Flask提供了信号(Signals)功能,是一种消息分发机制。类似于钩子(Hooks)。使用信号功能可以降低程序的耦合,分解复杂的业务模型。例如在更新了产品数据后,可以发送一个信号。当有需要对产品数据进行处理的功能时,就可以捕获信号进行处理。比如要建立产品缓存,或是更新搜索索引等。
定义信号
Flask信号功能使用了Blinker模块,所以需要先安装Blinker模块
pipinstallblinker
定义一个信号:
fromblinkerimportNamespace product_saved=Namespace()
也可以使用Flask包装的singles对象:
fromflask.singlesimportNamespace
发送信号
发送信号需要带上app实例方法,示例如下:
product_saved.send(app,product=product)
app后面可以添加要传递的参数,但必须以name=value的格式,不支持使用单个变量名的方式。
收接信号
接收信号可以使用connect_via装饰器函数:
@product_saved.connect_via(app) defupdateCache(app,product): print(product)
Flask中有以下核心信号:
1.flask.template_rendered
这个信号发送于一个模板被渲染成功后。信号传递的template是模板的实例,context是环境对象是一个字典。
订阅示例:
deflog_template_renders(sender,template,context,**extra): sender.logger.debug('Renderingtemplate"%s"withcontext%s', template.nameor'stringtemplate', context) fromflaskimporttemplate_rendered template_rendered.connect(log_template_renders,app)
2.flask.request_started
这个信号发送于请求开始之前,且请求环境设置完成之后。因为请求环境已经绑定,所以订阅者可以用标准的全局代理,如request来操作请求。
订阅示例:
deflog_request(sender,**extra): sender.logger.debug('Requestcontextissetup') fromflaskimportrequest_started request_started.connect(log_request,app) flask.request_finished
这个信号发送于向客户端发送响应之前。信号传递的response为将要发送的响应。
订阅示例:
deflog_response(sender,response,**extra): sender.logger.debug('Requestcontextisabouttoclosedown.' 'Response:%s',response) fromflaskimportrequest_finished request_finished.connect(log_response,app) flask.got_request_exception
这个信号发送于请求进行中发生异常的时候。它的发送早于标准异常处理介于。在调试模式下,虽然没有异常处理,但发生异常时也发送这个信号。信号传递的exception是异常对象。
订阅示例:
deflog_exception(sender,exception,**extra): sender.logger.debug('Gotexceptionduringprocessing:%s',exception) fromflaskimportgot_request_exception got_request_exception.connect(log_exception,app) flask.request_tearing_down
这个信号发送于请求崩溃的时候,不管是否引发异常。目前,侦听此信号的函数在一般崩溃处理器后调用,但是没有什么东西可用。
订阅示例:
defclose_db_connection(sender,**extra): session.close()fromflaskimportappcontext_tearing_down request_tearing_down.connect(close_db_connection,app)
在Flask版本0.9中,这还会传递一个exc关键字参数,如果这个参数存在的话。这个参数是引发崩溃的异常的引用。
3.flask.appcontext_tearing_down
当应用环境崩溃时发送这个信号。这个信号总是会发送,甚至是因为一个异常引发的崩溃。侦听这个信号的函数会在常规崩溃处理器后被调用,但是你无法回馈这个信号。
订阅示例:
defclose_db_connection(sender,**extra): session.close()fromflaskimportrequest_tearing_down appcontext_tearing_down.connect(close_db_connection,app)
这还会传递一个exc关键字参数,如果这个参数存在的话。这个参数是引发崩溃的异常的引用。
4.flask.appcontext_pushed
当一个应用的环境被压入时,应用会发送这个信号。这个信号通常用于在单元测试中临时钩接信息。例如可以用于改变g对象中现存的资源。
用法示例:
fromcontextlibimportcontextmanagerfrom flaskimportappcontext_pushed @contextmanagerdefuser_set(app,user): defhandler(sender,**kwargs): g.user=user withappcontext_pushed.connected_to(handler,app): yield
在测试代码中这样写:
deftest_user_me(self): withuser_set(app,'john'): c=app.test_client() resp=c.get('/users/me') assertresp.data=='username=john' Newinversion0.10.
5.appcontext_popped
当一个应用的环境被弹出时,应用会发送这个信号。这个信号通常写成appcontext_tearing_down信号。
6.flask.message_flashed
当应用闪现一个消息时会发出这个信号。message`参数是消息内容,`category参数是消息类别。
订阅示例:
recorded=[]defrecord(sender,message,category,**extra): recorded.append((message,category)) fromflaskimportmessage_flashed message_flashed.connect(record,app)
小结
信号可以让你在一瞬间安全地订阅它们。例如,这些临时的订阅对测试很有帮助。使用信号时,不要让信号订阅者(接收者)发生异常,因为异常会造成程序中断。