flask-信号
Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为。
1 pip3 install blinker
1、内置信号
1 request_started = _signals.signal('request-started') # 请求到来前执行 2 request_finished = _signals.signal('request-finished') # 请求结束后执行 3 4 before_render_template = _signals.signal('before-render-template') # 模板渲染前执行 5 template_rendered = _signals.signal('template-rendered') # 模板渲染后执行 6 7 got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行 8 9 request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否) 10 appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 请求上下文执行完毕后自动执行(无论成功与否) 11 12 appcontext_pushed = _signals.signal('appcontext-pushed') # 请求上下文push时执行 13 appcontext_popped = _signals.signal('appcontext-popped') # 请求上下文pop时执行 14 message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
源码示例:
1 class Flask(_PackageBoundObject): 2 3 def full_dispatch_request(self): 4 5 self.try_trigger_before_first_request_functions() 6 try: 7 # ############### 触发request_started 信号 ############### 8 request_started.send(self) 9 rv = self.preprocess_request() 10 if rv is None: 11 rv = self.dispatch_request() 12 except Exception as e: 13 rv = self.handle_user_exception(e) 14 response = self.make_response(rv) 15 response = self.process_response(response) 16 17 # ############### request_finished 信号 ############### 18 request_finished.send(self, response=response) 19 return response 20 21 def wsgi_app(self, environ, start_response): 22 23 ctx = self.request_context(environ) 24 ctx.push() 25 error = None 26 try: 27 try: 28 response = self.full_dispatch_request() 29 except Exception as e: 30 error = e 31 response = self.make_response(self.handle_exception(e)) 32 return response(environ, start_response) 33 finally: 34 if self.should_ignore_error(error): 35 error = None 36 ctx.auto_pop(error)
1 同上!
1 def render_template(template_name_or_list, **context): 2 """Renders a template from the template folder with the given 3 context. 4 5 :param template_name_or_list: the name of the template to be 6 rendered, or an iterable with template names 7 the first one existing will be rendered 8 :param context: the variables that should be available in the 9 context of the template. 10 """ 11 ctx = _app_ctx_stack.top 12 ctx.app.update_template_context(context) 13 return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list), 14 context, ctx.app) 15 16 def _render(template, context, app): 17 """Renders the template and fires the signal""" 18 19 # ############### before_render_template 信号 ############### 20 before_render_template.send(app, template=template, context=context) 21 rv = template.render(context) 22 23 # ############### template_rendered 信号 ############### 24 template_rendered.send(app, template=template, context=context) 25 return rv
1 同上!
1 class Flask(_PackageBoundObject): 2 3 def handle_exception(self, e): 4 5 exc_type, exc_value, tb = sys.exc_info() 6 7 # ############### got_request_exception 信号 ############### 8 got_request_exception.send(self, exception=e) 9 handler = self._find_error_handler(InternalServerError()) 10 11 if self.propagate_exceptions: 12 # if we want to repropagate the exception, we can attempt to 13 # raise it with the whole traceback in case we can do that 14 # (the function was actually called from the except part) 15 # otherwise, we just raise the error again 16 if exc_value is e: 17 reraise(exc_type, exc_value, tb) 18 else: 19 raise e 20 21 self.log_exception((exc_type, exc_value, tb)) 22 if handler is None: 23 return InternalServerError() 24 return handler(e) 25 26 def wsgi_app(self, environ, start_response): 27 28 ctx = self.request_context(environ) 29 ctx.push() 30 error = None 31 try: 32 try: 33 response = self.full_dispatch_request() 34 except Exception as e: 35 error = e 36 # 这里这里这里这里这里这里这里这里这里这里这里这里 # 37 response = self.make_response(self.handle_exception(e)) 38 return response(environ, start_response) 39 finally: 40 if self.should_ignore_error(error): 41 error = None 42 ctx.auto_pop(error)
1 class AppContext(object): 2 def push(self): 3 """Binds the app context to the current context.""" 4 self._refcnt += 1 5 if hasattr(sys, 'exc_clear'): 6 sys.exc_clear() 7 _app_ctx_stack.push(self) 8 # ############## 触发 appcontext_pushed 信号 ############## 9 appcontext_pushed.send(self.app) 10 11 def pop(self, exc=_sentinel): 12 """Pops the app context.""" 13 try: 14 self._refcnt -= 1 15 if self._refcnt <= 0: 16 if exc is _sentinel: 17 exc = sys.exc_info()[1] 18 # ############## 触发 appcontext_tearing_down 信号 ############## 19 self.app.do_teardown_appcontext(exc) 20 finally: 21 rv = _app_ctx_stack.pop() 22 assert rv is self, 'Popped wrong app context. (%r instead of %r)' \ 23 % (rv, self) 24 25 # ############## 触发 appcontext_popped 信号 ############## 26 appcontext_popped.send(self.app) 27 28 class RequestContext(object): 29 def push(self): 30 top = _request_ctx_stack.top 31 if top is not None and top.preserved: 32 top.pop(top._preserved_exc) 33 34 app_ctx = _app_ctx_stack.top 35 if app_ctx is None or app_ctx.app != self.app: 36 37 # #################################################### 38 app_ctx = self.app.app_context() 39 app_ctx.push() 40 self._implicit_app_ctx_stack.append(app_ctx) 41 else: 42 self._implicit_app_ctx_stack.append(None) 43 44 if hasattr(sys, 'exc_clear'): 45 sys.exc_clear() 46 47 _request_ctx_stack.push(self) 48 49 # Open the session at the moment that the request context is 50 # available. This allows a custom open_session method to use the 51 # request context (e.g. code that access database information 52 # stored on `g` instead of the appcontext). 53 self.session = self.app.open_session(self.request) 54 if self.session is None: 55 self.session = self.app.make_null_session() 56 57 class Flask(_PackageBoundObject): 58 59 60 def wsgi_app(self, environ, start_response): 61 62 ctx = self.request_context(environ) 63 ctx.push() 64 error = None 65 try: 66 try: 67 response = self.full_dispatch_request() 68 except Exception as e: 69 error = e 70 response = self.make_response(self.handle_exception(e)) 71 return response(environ, start_response) 72 finally: 73 if self.should_ignore_error(error): 74 error = None 75 ctx.auto_pop(error) 76 77 78 def pop(self, exc=_sentinel): 79 app_ctx = self._implicit_app_ctx_stack.pop() 80 81 try: 82 clear_request = False 83 if not self._implicit_app_ctx_stack: 84 self.preserved = False 85 self._preserved_exc = None 86 if exc is _sentinel: 87 exc = sys.exc_info()[1] 88 89 # ################## 触发 request_tearing_down 信号 ################## 90 self.app.do_teardown_request(exc) 91 92 # If this interpreter supports clearing the exception information 93 # we do that now. This will only go into effect on Python 2.x, 94 # on 3.x it disappears automatically at the end of the exception 95 # stack. 96 if hasattr(sys, 'exc_clear'): 97 sys.exc_clear() 98 99 request_close = getattr(self.request, 'close', None) 100 if request_close is not None: 101 request_close() 102 clear_request = True 103 finally: 104 rv = _request_ctx_stack.pop() 105 106 # get rid of circular dependencies at the end of the request 107 # so that we don't require the GC to be active. 108 if clear_request: 109 rv.request.environ['werkzeug.request'] = None 110 111 # Get rid of the app as well if necessary. 112 if app_ctx is not None: 113 # #################################################### 114 app_ctx.pop(exc) 115 116 assert rv is self, 'Popped wrong request context. ' \ 117 '(%r instead of %r)' % (rv, self) 118 119 def auto_pop(self, exc): 120 if self.request.environ.get('flask._preserve_context') or \ 121 (exc is not None and self.app.preserve_context_on_exception): 122 self.preserved = True 123 self._preserved_exc = exc 124 else: 125 self.pop(exc)
1 同上!
1 同上!
1 def flash(message, category='message'): 2 """Flashes a message to the next request. In order to remove the 3 flashed message from the session and to display it to the user, 4 the template has to call :func:`get_flashed_messages`. 5 6 .. versionchanged:: 0.3 7 `category` parameter added. 8 9 :param message: the message to be flashed. 10 :param category: the category for the message. The following values 11 are recommended: ``'message'`` for any kind of message, 12 ``'error'`` for errors, ``'info'`` for information 13 messages and ``'warning'`` for warnings. However any 14 kind of string can be used as category. 15 """ 16 # Original implementation: 17 # 18 # session.setdefault('_flashes', []).append((category, message)) 19 # 20 # This assumed that changes made to mutable structures in the session are 21 # are always in sync with the session object, which is not true for session 22 # implementations that use external storage for keeping their keys/values. 23 flashes = session.get('_flashes', []) 24 flashes.append((category, message)) 25 session['_flashes'] = flashes 26 27 # ############### 触发 message_flashed 信号 ############### 28 message_flashed.send(current_app._get_current_object(), 29 message=message, category=category)
2. 自定义信号
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, current_app, flash, render_template from flask.signals import _signals app = Flask(import_name=__name__) # 自定义信号 xxxxx = _signals.signal('xxxxx') def func(sender, *args, **kwargs): print(sender) # 自定义信号中注册函数 xxxxx.connect(func) @app.route("/x") def index(): # 触发信号 xxxxx.send('123123', k1='v1') return 'Index' if __name__ == '__main__': app.run()