flask-信号

作者: 马小跳_ | 来源:发表于2018-01-09 19:29 被阅读101次

    Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为。

    pip3 install blinker
    

    使用方法

    from flask.signals import request_started
    
    def func1(*args):
        print(111, *args)
    
    def func2(*args):
        print(222, *args)
    
    request_started.connect(func1)  # 将函数注册到信号中
    request_started.connect(func2)
    request_started.send()  # 触发这个信号,执行列表中的所有函数
    
    
    

    内置信号

    request_started = _signals.signal('request-started')                # 请求到来前执行
    request_finished = _signals.signal('request-finished')              # 请求结束后执行
     
    before_render_template = _signals.signal('before-render-template')  # 模板渲染前执行
    template_rendered = _signals.signal('template-rendered')            # 模板渲染后执行
     
    got_request_exception = _signals.signal('got-request-exception')    # 请求执行出现异常时执行
     
    request_tearing_down = _signals.signal('request-tearing-down')      # 请求执行完毕后自动执行(无论成功与否)
    appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 请求上下文执行完毕后自动执行(无论成功与否)
     
    appcontext_pushed = _signals.signal('appcontext-pushed')            # 请求上下文push时执行
    appcontext_popped = _signals.signal('appcontext-popped')            # 请求上下文pop时执行
    message_flashed = _signals.signal('message-flashed')                # 调用flask在其中添加数据时,自动触发
    

    源码示例

    appcontext_pushed:请求上下文push时触发

    wsgi_app -- ctx.push() -- app_ctx.push()
    class AppContext(object):
        def push(self):
            """Binds the app context to the current context."""
            self._refcnt += 1
            if hasattr(sys, 'exc_clear'):
                sys.exc_clear()
            _app_ctx_stack.push(self)
            # ############## 触发 appcontext_pushed 信号 ##############
            appcontext_pushed.send(self.app)
    

    request_started:在 @before_request之前

    wsgi_app -- full_dispatch_request
    class Flask(_PackageBoundObject):
        def full_dispatch_request(self):
            self.try_trigger_before_first_request_functions()
            try:
                # ############### 触发request_started 信号 ###############
                request_started.send(self)
                rv = self.preprocess_request()
                if rv is None:
                    rv = self.dispatch_request()
            except Exception as e:
                rv = self.handle_user_exception(e)
            return self.finalize_request(rv)
    

    request_finished:在@after_request之后

    wsgi_app -- full_dispatch_request -- finalize_request
    class Flask(_PackageBoundObject):
        def finalize_request(self, rv, from_error_handler=False):
            response = self.make_response(rv)
            try:
                response = self.process_response(response)
                # ############### request_finished 信号 ###############
                request_finished.send(self, response=response)
            except Exception:
                if not from_error_handler:
                    raise
                self.logger.exception('Request finalizing failed with an '
                                      'error while handling an error')
            return response
    

    before_render_template 和 template_rendered:在模板渲染时触发

    render_template -- _render
    def render_template(template_name_or_list, **context):
        ctx = _app_ctx_stack.top
        ctx.app.update_template_context(context)
        return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
                       context, ctx.app)
    
    def _render(template, context, app):
        # ############### before_render_template 信号 ###############
        before_render_template.send(app, template=template, context=context)
        rv = template.render(context)
        
        # ############### template_rendered 信号 ###############
        template_rendered.send(app, template=template, context=context)
        return rv
    

    got_request_exception :在视图函数前后的过程中遇到异常时触发

    wsgi_app -- handle_exception
    class Flask(_PackageBoundObject):
        def handle_exception(self, e):
            exc_type, exc_value, tb = sys.exc_info()
            # ############### 触发got_request_exception 信号 ###############
            got_request_exception.send(self, exception=e)
            handler = self._find_error_handler(InternalServerError())
    
            if self.propagate_exceptions:
                if exc_value is e:
                    reraise(exc_type, exc_value, tb)
                else:
                    raise e
    
            self.log_exception((exc_type, exc_value, tb))
            if handler is None:
                return InternalServerError()
            return self.finalize_request(handler(e), from_error_handler=True)
    

    request_tearing_down 和 appcontext_tearing_down:在wsgi_app最后触发

    wsgi_app -- ctx.auto_pop() -- ctx.pop() --  app.do_teardown_request() -- app_ctx.pop(exc)
    
    class RequestContext(object):
        def pop(self, exc=_sentinel):
            app_ctx = self._implicit_app_ctx_stack.pop()
    
            try:
                clear_request = False
                if not self._implicit_app_ctx_stack:
                    self.preserved = False
                    self._preserved_exc = None
                    if exc is _sentinel:
                        exc = sys.exc_info()[1]
                    # ################## 触发 request_tearing_down 信号 ##################
                    self.app.do_teardown_request(exc)
    
                    if hasattr(sys, 'exc_clear'):
                        sys.exc_clear()
    
                    request_close = getattr(self.request, 'close', None)
                    if request_close is not None:
                        request_close()
                    clear_request = True
            finally:
                rv = _request_ctx_stack.pop()
    
                # get rid of circular dependencies at the end of the request
                # so that we don't require the GC to be active.
                if clear_request:
                    rv.request.environ['werkzeug.request'] = None
    
                # Get rid of the app as well if necessary.
                if app_ctx is not None:
                    # ################## 触发 appcontext_tearing_down和appcontext_popped##################
                    app_ctx.pop(exc)
    
                assert rv is self, 'Popped wrong request context.  ' \
                    '(%r instead of %r)' % (rv, self)
    
    class AppContext(object):
        def pop(self, exc=_sentinel):
            """Pops the app context."""
            try:
                self._refcnt -= 1
                if self._refcnt <= 0:
                    if exc is _sentinel:
                        exc = sys.exc_info()[1]
                    # ################## 触发 appcontext_tearing_down##################
                    self.app.do_teardown_appcontext(exc)
            finally:
                rv = _app_ctx_stack.pop()
            assert rv is self, 'Popped wrong app context.  (%r instead of %r)' \
                % (rv, self)
            # ################## 触发 appcontext_popped##################
            appcontext_popped.send(self.app)
    
    class Flask(_PackageBoundObject):
        def do_teardown_request(self, exc=_sentinel):
            if exc is _sentinel:
                exc = sys.exc_info()[1]
            funcs = reversed(self.teardown_request_funcs.get(None, ()))
            bp = _request_ctx_stack.top.request.blueprint
            if bp is not None and bp in self.teardown_request_funcs:
                funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
            for func in funcs:
                func(exc)
            # ################## 触发 request_tearing_down 信号 ##################
            request_tearing_down.send(self, exc=exc)
    
        def do_teardown_appcontext(self, exc=_sentinel):
            if exc is _sentinel:
                exc = sys.exc_info()[1]
            for func in reversed(self.teardown_appcontext_funcs):
                func(exc)
            # ################## 触发 appcontext_tearing_down信号 ##################
            appcontext_tearing_down.send(self, exc=exc)
    
    
    

    message_flashed:在调用flash函数时触发

    def flash(message, category='message'):
        flashes = session.get('_flashes', [])
        flashes.append((category, message))
        session['_flashes'] = flashes
        # ############### 触发 message_flashed 信号 ###############
        message_flashed.send(current_app._get_current_object(),
                             message=message, category=category)
    

    自定义信号

    #!/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()
    

    相关文章

      网友评论

        本文标题:flask-信号

        本文链接:https://www.haomeiwen.com/subject/sztgnxtx.html