zoukankan      html  css  js  c++  java
  • Flask中信号的使用

    1、Flask中信号简介

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

    2、内置信号

      Flask中内置信号在·signals.py中定义:

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

       源码示例:

    def render_template(template_name_or_list, **context):
        """Renders a template from the template folder with the given
        context.
    
        :param template_name_or_list: the name of the template to be
                                      rendered, or an iterable with template names
                                      the first one existing will be rendered
        :param context: the variables that should be available in the
                        context of the template.
        """
        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):
        """Renders the template and fires the signal"""
        
        # ############### 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
    template_rendered
    def render_template(template_name_or_list, **context):
        """Renders a template from the template folder with the given
        context.
    
        :param template_name_or_list: the name of the template to be
                                      rendered, or an iterable with template names
                                      the first one existing will be rendered
        :param context: the variables that should be available in the
                        context of the template.
        """
        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):
        """Renders the template and fires the signal"""
        
        # ############### 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
    before_render_template
    class Flask(_PackageBoundObject):
        
        def full_dispatch_request(self):
            """Dispatches the request and on top of that performs request
            pre and postprocessing as well as HTTP exception catching and
            error handling.
    
            .. versionadded:: 0.7
            """
            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)
        def finalize_request(self, rv, from_error_handler=False):
            """Given the return value from a view function this finalizes
            the request by converting it into a response and invoking the
            postprocessing functions.  This is invoked for both normal
            request dispatching as well as error handlers.
    
            Because this means that it might be called as a result of a
            failure a special safe mode is available which can be enabled
            with the `from_error_handler` flag.  If enabled, failures in
            response processing will be logged and otherwise ignored.
    
            :internal:
            """
            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
        
        def wsgi_app(self, environ, start_response):
            """The actual WSGI application. This is not implemented in
            :meth:`__call__` so that middlewares can be applied without
            losing a reference to the app object. Instead of doing this::
    
                app = MyMiddleware(app)
    
            It's a better idea to do this instead::
    
                app.wsgi_app = MyMiddleware(app.wsgi_app)
    
            Then you still have the original application object around and
            can continue to call methods on it.
    
            .. versionchanged:: 0.7
                Teardown events for the request and app contexts are called
                even if an unhandled error occurs. Other events may not be
                called depending on when an error occurs during dispatch.
                See :ref:`callbacks-and-errors`.
    
            :param environ: A WSGI environment.
            :param start_response: A callable accepting a status code,
                a list of headers, and an optional exception context to
                start the response.
            """
            ctx = self.request_context(environ)
            error = None
            try:
                try:
                    ctx.push()
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                ctx.auto_pop(error)
    request_started
    class Flask(_PackageBoundObject):
        
        def full_dispatch_request(self):
            """Dispatches the request and on top of that performs request
            pre and postprocessing as well as HTTP exception catching and
            error handling.
    
            .. versionadded:: 0.7
            """
            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)
        def finalize_request(self, rv, from_error_handler=False):
            """Given the return value from a view function this finalizes
            the request by converting it into a response and invoking the
            postprocessing functions.  This is invoked for both normal
            request dispatching as well as error handlers.
    
            Because this means that it might be called as a result of a
            failure a special safe mode is available which can be enabled
            with the `from_error_handler` flag.  If enabled, failures in
            response processing will be logged and otherwise ignored.
    
            :internal:
            """
            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
        
        def wsgi_app(self, environ, start_response):
            """The actual WSGI application. This is not implemented in
            :meth:`__call__` so that middlewares can be applied without
            losing a reference to the app object. Instead of doing this::
    
                app = MyMiddleware(app)
    
            It's a better idea to do this instead::
    
                app.wsgi_app = MyMiddleware(app.wsgi_app)
    
            Then you still have the original application object around and
            can continue to call methods on it.
    
            .. versionchanged:: 0.7
                Teardown events for the request and app contexts are called
                even if an unhandled error occurs. Other events may not be
                called depending on when an error occurs during dispatch.
                See :ref:`callbacks-and-errors`.
    
            :param environ: A WSGI environment.
            :param start_response: A callable accepting a status code,
                a list of headers, and an optional exception context to
                start the response.
            """
            ctx = self.request_context(environ)
            error = None
            try:
                try:
                    ctx.push()
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                ctx.auto_pop(error)
    request_finished
    class RequestContext(object):
    
        def __init__(self, app, environ, request=None):
            self.app = app
            if request is None:
                request = app.request_class(environ)
            self.request = request
            self.url_adapter = app.create_url_adapter(self.request)
            self.flashes = None
            self.session = None
    
            self._implicit_app_ctx_stack = []
    
            self.preserved = False
    
            self._preserved_exc = None
    
            self._after_request_functions = []
    
            self.match_request()
    
            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]
                    ###################################################
                    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()
                
                if clear_request:
                    rv.request.environ['werkzeug.request'] = None
    
                # Get rid of the app as well if necessary.
                if app_ctx is not None:
                    app_ctx.pop(exc)
    
                assert rv is self, 'Popped wrong request context.  ' 
                    '(%r instead of %r)' % (rv, self)
                    
    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)
    request_tearing_down
    class Flask(_PackageBoundObject):
        def handle_exception(self, e):
            """Default exception handling that kicks in when an exception
            occurs that is not caught.  In debug mode the exception will
            be re-raised immediately, otherwise it is logged and the handler
            for a 500 internal server error is used.  If no such handler
            exists, a default 500 internal server error message is displayed.
    
            .. versionadded:: 0.3
            """
            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 we want to repropagate the exception, we can attempt to
                # raise it with the whole traceback in case we can do that
                # (the function was actually called from the except part)
                # otherwise, we just raise the error again
                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)
    got_request_exception
    class Flask(_PackageBoundObject):
        def do_teardown_appcontext(self, exc=_sentinel):
            """Called right before the application context is popped.
    
            When handling a request, the application context is popped
            after the request context. See :meth:`do_teardown_request`.
    
            This calls all functions decorated with
            :meth:`teardown_appcontext`. Then the
            :data:`appcontext_tearing_down` signal is sent.
    
            This is called by
            :meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
    
            .. versionadded:: 0.9
            """
            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)
    appcontext_tearing_down
    class AppContext(object):
        """The application context binds an application object implicitly
        to the current thread or greenlet, similar to how the
        :class:`RequestContext` binds request information.  The application
        context is also implicitly created if a request context is created
        but the application is not on top of the individual application
        context.
        """
    
        def __init__(self, app):
            self.app = app
            self.url_adapter = app.create_url_adapter(None)
            self.g = app.app_ctx_globals_class()
    
            # Like request context, app contexts can be pushed multiple times
            # but there a basic "refcount" is enough to track them.
            self._refcnt = 0
    
        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)
    
        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]
                    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)
    
        def __enter__(self):
            self.push()
            return self
    
        def __exit__(self, exc_type, exc_value, tb):
            self.pop(exc_value)
    
            if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
                reraise(exc_type, exc_value, tb)
    appcontext_pushed
    同上
    appcontext_popped
    def flash(message, category='message'):
        """Flashes a message to the next request.  In order to remove the
        flashed message from the session and to display it to the user,
        the template has to call :func:`get_flashed_messages`.
    
        .. versionchanged:: 0.3
           `category` parameter added.
    
        :param message: the message to be flashed.
        :param category: the category for the message.  The following values
                         are recommended: ``'message'`` for any kind of message,
                         ``'error'`` for errors, ``'info'`` for information
                         messages and ``'warning'`` for warnings.  However any
                         kind of string can be used as category.
        """
        # Original implementation:
        #
        #     session.setdefault('_flashes', []).append((category, message))
        #
        # This assumed that changes made to mutable structures in the session are
        # always in sync with the session object, which is not true for session
        # implementations that use external storage for keeping their keys/values.
        flashes = session.get('_flashes', [])
        flashes.append((category, message))
        session['_flashes'] = flashes
        message_flashed.send(current_app._get_current_object(),
    message_flashed

    3、自定义信号

    from flask import Flask,current_app,flash,render_template
    from flask.signals import _signals
    
    app = Flask(import_name=__name__)
    
    
    # 自定义信号
    xxxx = _signals.signal('xxxx')
    
    def func(sender, *args, **kwargs):
        print(sender)
    
    # 自定义信号中注册函数
    xxxx.connect(func)
    
    @app.route('/x')
    def index():
        # 触发信号
        xxxx.send("12345",k1="v1")
        return "Index"
    
    if __name__ == '__main__':
        app.run()

      执行结果:

    127.0.0.1 - - [07/Dec/2018 11:25:00] "GET / HTTP/1.1" 404 -
    127.0.0.1 - - [07/Dec/2018 11:25:15] "GET /x HTTP/1.1" 200 -
    12345
  • 相关阅读:
    深入NET框架
    解决idea中maven的pom文件不会自动下载jar包问题
    JSP中的作用域
    转发与重定向
    JSP内置对象
    JNDI与连接池
    文件上传
    七大设计原则
    第六章 初始继承和多态
    C#和.NET框架
  • 原文地址:https://www.cnblogs.com/bad-robot/p/10081937.html
Copyright © 2011-2022 走看看