2.启动函数返回值的剖析

2.启动函数返回值的剖析

本次剖析根据 flask 内部的处理流程将返回值推导出。

2.1 源码剖析

from werkzeug import run_simple

def func(environ,start_response):
    # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。
    print("请求来了")
	
    
    return ????? # return 的返回值是什么呢
if __name__ == '__main__':
    # app.run()
    run_simple("127.0.0.1",5000,func)
>>> 点击请求地址(请求过来时)
>>> 请求来了
>>> 报错
# 函数被执行相当于 func()

flask 的启动函数执行的是__call__()方法,该方法的源码为

def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
    """The WSGI server calls the Flask application object as the
    WSGI application. This calls :meth:`wsgi_app`, which can be
    wrapped to apply middleware.
    """
    return self.wsgi_app(environ, start_response)

此处调用相关的 wsgi_app(...)方法,该方法的源码如下

def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
    ctx = self.request_context(environ)
    error: t.Optional[BaseException] = None
    try:
        try:
            ctx.push()
            response = self.full_dispatch_request() # 创建response返回值对象。
        except Exception as e:
            error = e
            response = self.handle_exception(e)
        except:  # noqa: B001
            error = sys.exc_info()[1]
            raise
        # 这里最终返回的是一个 response 对象,response 来源于上方的 full_dispatch_request函数。
        return response(environ, start_response)  
    finally:
        if self.should_ignore_error(error):
            error = None
        ctx.auto_pop(error)

根据相关的注释信息去查看相关函数的full_dispatch_request函数的源码。

def full_dispatch_request(self) -> Response: # 此函数的返回值类型是Response
    """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.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) # 返回值执行的函数。rv 表示了返回值中的具体内容例如一个字符串

知识补充:->常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数的返回类型,也可以理解为给函数添加注解。

通俗理解:就是表明了本函数的参数类型和返回值的类型。

参考文献:https://blog.csdn.net/mahoon411/article/details/125363646

finalize_request函数源码,如下所示:

def finalize_request(
    self,
    rv: t.Union[ft.ResponseReturnValue, HTTPException],
    from_error_handler: bool = False,
) -> Response: # 描述了相关的返回值。
    """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) # 其中 rv 表示返回值的具体内容,此函数创建response对象
    try:
        response = self.process_response(response)
        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 #返回的是一个 response 对象,由上述的 make_response 进行返回。

make_response()函数的返回值如下,本函数内部较长将删除一些源码注释,提高观察效率,如有需要可以在源码中进行观看:

    def make_response(self, rv: ft.ResponseReturnValue) -> Response:
        status = headers = None

        # unpack tuple returns
        if isinstance(rv, tuple): # 检查 rv 是否为相关的类型
            len_rv = len(rv)

            # a 3-tuple is unpacked directly
            if len_rv == 3:
                rv, status, headers = rv  # type: ignore[misc]
            # decide if a 2-tuple has status or headers
            elif len_rv == 2:
                if isinstance(rv[1], (Headers, dict, tuple, list)):
                    rv, headers = rv
                else:
                    rv, status = rv  # type: ignore[assignment,misc]
            # other sized tuples are not allowed
            else:
                raise TypeError(
                    "The view function did not return a valid response tuple."
                    " The tuple must have the form (body, status, headers),"
                    " (body, status), or (body, headers)."
                )

        # the body must not be None
        if rv is None: 
            raise TypeError(
                f"The view function for {request.endpoint!r} did not"
                " return a valid response. The function either returned"
                " None or ended without a return statement."
            )

        # make sure the body is an instance of the response class
        if not isinstance(rv, self.response_class):
            if isinstance(rv, (str, bytes, bytearray)):
                # let the response class set the status and headers instead of
                # waiting to do it manually, so that the class can handle any
                # special logic
                rv = self.response_class(
                    rv,
                    status=status,
                    headers=headers,  # type: ignore[arg-type]
                )
                status = headers = None
            elif isinstance(rv, dict):
                rv = jsonify(rv)
            elif isinstance(rv, BaseResponse) or callable(rv):
                # evaluate a WSGI callable, or coerce a different response
                # class to the correct type
                try:
                    rv = self.response_class.force_type(
                        rv, request.environ  # type: ignore[arg-type]
                    )
                except TypeError as e:
                    raise TypeError(
                        f"{e}\nThe view function did not return a valid"
                        " response. The return type must be a string,"
                        " dict, tuple, Response instance, or WSGI"
                        f" callable, but it was a {type(rv).__name__}."
                    ).with_traceback(sys.exc_info()[2]) from None
            else:
                raise TypeError(
                    "The view function did not return a valid"
                    " response. The return type must be a string,"
                    " dict, tuple, Response instance, or WSGI"
                    f" callable, but it was a {type(rv).__name__}."
                )
		'''
		
		重点:以上的结构为三个大的if 判断结构,判断rv是否为相关的类型。
		'''
        rv = t.cast(Response, rv) # 此处是将rv 转换为对象的 Response 的对象。
        # prefer the status if it was provided
        if status is not None:
            if isinstance(status, (str, bytes, bytearray)):
                rv.status = status
            else:
                rv.status_code = status

        # extend existing headers with provided headers
        if headers:
            rv.headers.update(headers)  # type: ignore[arg-type]

        return rv

cast()函数的源码如图所示

def cast(typ, val):
    """Cast a value to a type.

    This returns the value unchanged.  To the type checker this
    signals that the return value has the designated type, but at
    runtime we intentionally don't check anything (we want this
    to be as fast as possible).
    """
    # 通过注释可以发现,这个函数的功能就是转换数据到执行的类型
    return val

rv = t.cast(Response, rv) 此处是将rv 转换为对象的 Response 的对象。通过源码可知,第一个参数为对象的类型。查看对象类型源码如下:

from werkzeug.wrappers import Response as ResponseBase # 源码内部的导包

class Response(ResponseBase): #该类继承于 ResponeBase 类。即Response
    """The response object that is used by default in Flask.  Works like the
    response object from Werkzeug but is set to have an HTML mimetype by
    default.  Quite often you don't have to create this object yourself because
    :meth:`~flask.Flask.make_response` will take care of that for you.

    If you want to replace the response object used you can subclass this and
    set :attr:`~flask.Flask.response_class` to your subclass.

    .. versionchanged:: 1.0
        JSON support is added to the response, like the request. This is useful
        when testing to get the test client response data as JSON.

    .. versionchanged:: 1.0

        Added :attr:`max_cookie_size`.
    """

    default_mimetype = "text/html"

    json_module = json

    autocorrect_location_header = False

    @property
    def max_cookie_size(self) -> int:  # type: ignore
        """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.

        See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
        Werkzeug's docs.
        """
        if current_app:
            return current_app.config["MAX_COOKIE_SIZE"]

        # return Werkzeug's default when not in an app context
        return super().max_cookie_size

通过源码的注释,可以发现这是一个 flask 的内置类。可以直接使用,通过 pycharm 的路径显示(该类在wrappers.py文件下,改文件在flask 文件夹下【源码文件】。)我们也可以得出他的导包路径为

from flask.wrappers import Response

而此类则是继承于Response类(源码内部到包时修改为Response),该类的源码如下图所示:

class Response(_SansIOResponse):
	pass
# 该类的内容太长,不在此处粘贴。

该类是werkzug下的类,源码文件位于werkzug文件夹下的 warppers文件夹下的response文件中,因此导包路径为

from werkzeug.wrappers.response import Response

至此,可得 flask 中请求的核心功能大多都是有Werkzug包内部完成的。

2.2 返回值的示例

2.2.1 依赖flask

根据 wsgi_app()中的源码可知,response对象创建后需要将参数environ, start_response再进行传入。

def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
    ctx = self.request_context(environ)
    error: t.Optional[BaseException] = None
    try:
        try:
            ctx.push()
            response = self.full_dispatch_request() # 创建response返回值对象。
        except Exception as e:
            error = e
            response = self.handle_exception(e)
        except:  # noqa: B001
            error = sys.exc_info()[1]
            raise
        # 这里最终返回的是一个 response 对象,response 来源于上方的 full_dispatch_request函数。
        return response(environ, start_response)  
    finally:
        if self.should_ignore_error(error):
            error = None
        ctx.auto_pop(error)

根据上述流程,将代码进行改造

from werkzeug import run_simple
from flask.wrappers import Response

def func(environ, start_response):
    # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。
    print("请求来了")
    response = Response("Hello World") # 创建相关的Response
    
    return response(environ,start_response) # 封装参数进行返回

if __name__ == '__main__':
    # app.run()
    run_simple("127.0.0.1", 5000, func)
    
>>> # 正常在浏览器中响应,正常返回
>>> * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
>>> 请求来了
>>> 127.0.0.1 - - [21/Jul/2022 10:09:09] "GET / HTTP/1.1" 200 -
>>> 请求来了
>>> 127.0.0.1 - - [21/Jul/2022 10:09:09] "GET /favicon.ico HTTP/1.1" 200 -
  • 此方法,依赖于flask中的值,而 flask 的请求核心都是依赖于werkzug,说明此处可以用相关的父类进行替代。
2.2.2 原生实现
from werkzeug import run_simple
from werkzeug.wrappers.response import Response # 导入的是werkzug 包

def func(environ, start_response):
    # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。
    print("请求来了")
    response = Response("Hello World")
    return response(environ,start_response) # return 的返回值是什么呢

if __name__ == '__main__':
    # app.run()
    run_simple("127.0.0.1", 5000, func)

>>> # 正常在浏览器中响应,正常返回    
>>> * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
>>> 127.0.0.1 - - [21/Jul/2022 10:16:40] "GET / HTTP/1.1" 200 -
>>> 请求来了
>>> 请求来了
>>> 127.0.0.1 - - [21/Jul/2022 10:16:40] "GET /favicon.ico HTTP/1.1" 200 -
  • 总结:flask的网络请求核心全部都是由werkzug进行的合成。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值