flask v0.1源码简单分析

栏目: Python · 发布时间: 6年前

内容简介:flask v0.1源码简单分析

想学习 python 的flask,看了《FlaskWeb开发:基于Python的Web应用开发实战》这本书,觉得很OK:ok_hand:。之前彭师兄分析过flask v0.1的源码,听说代码不长,加之网上解释甚多,所以自己也想看看,就其本身,本文无意义,有错误更是可能,只是记录而已。

0x01.获取

第一步获取 git clone https://github.com/pallets/flask.git ,这是最新版本的

第二步 git tag 看版本

第三步 git reset --hard xx 回退到对应版本

最后是这样

flask v0.1源码简单分析

主要内容在flask.py文件里面

0x02.简要分析

1.包

flask.py导入的包情况

from __future__ import with_statement
import os
import sys

from threading import local
from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase, \
     LocalStack, LocalProxy, create_environ, cached_property, \
     SharedDataMiddleware
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.contrib.securecookie import SecureCookie

# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
from werkzeug import abort, redirect
from jinja2 import Markup, escape

Werkzeug是 PythonWSGI 规范的实用函数库,而对于WSGI,廖先生解释的很好啊 https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386832689740b04430a98f614b6da89da2157ea3efe2000

盗一张简书的图

flask v0.1源码简单分析

而jinja2是flask其默认的页面模版,其格式有点像最初的将 java 代码写在jsp页面中…

2.上下文

flask.py最下面是这几行,要看懂flask需要了解其上下文机制

_request_ctx_stack = LocalStack()#建立request栈
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)#栈当前app
request = LocalProxy(lambda: _request_ctx_stack.top.request)#栈当前request
session = LocalProxy(lambda: _request_ctx_stack.top.session)#栈当前session
g = LocalProxy(lambda: _request_ctx_stack.top.g)#当栈前g

书中有提到

flask v0.1源码简单分析

对于上下文的解释更有 https://blog.tonyseek.com/post/the-context-mechanism-of-flask/

解释的很好。

3.简单流程

下面一大串其实主要为了解释上门那张图

一. app=Flask(__name__)

#程序入口 建立app实例
class Flask(object):

并且其中初始化一些东西

self.template_context_processors = [_default_template_ctx_processor]

        self.url_map = Map()#URL endpoint function 对应的map

        if self.static_path is not None:
            self.url_map.add(Rule(self.static_path + '/<filename>',
                                  build_only=True, endpoint='static'))
            if pkg_resources is not None:
                target = (self.package_name, 'static')
            else:
                target = os.path.join(self.root_path, 'static')
            self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {#静态
                self.static_path: target
            })

        #: the Jinja2 environment.  It is created from the
        #: :attr:`jinja_options` and the loader that is returned
        #: by the :meth:`create_jinja_loader` function.
        self.jinja_env = Environment(loader=self.create_jinja_loader(),
                                     **self.jinja_options)
        self.jinja_env.globals.update(
            url_for=url_for,
            get_flashed_messages=get_flashed_messages
        )

这里还是很关键的

Flask应用使用werkzeug库中的Map类和Rule类来处理URL的模式匹配,每一个URL模式对应一个Rule实例,这些Rule实例最终会作为参数传递给Map类构造包含所有URL模式的一个“地图”。
Flask使用SharedDataMiddleware来对静态内容的访问支持,也即是static目录下的资源可以被外部,

flask的route设计思路,这篇足以 https://segmentfault.com/a/1190000004213652

二. app.run()

开始运行flask类下的 run() 函数,run函数最后一句

return run_simple(host, port, self, **options)#开始运行,这里的run_simple是werkzeug里面的

在对应·的 /lib/python版本/site-packes/werkzeug/serving.py 文件中可以找到该函数,该函数属于

class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):

def run_simple(hostname, port, application, use_reloader=False,
               use_debugger=False, use_evalex=True,
               extra_files=None, reloader_interval=1,
               reloader_type='auto', threaded=False,
               processes=1, request_handler=None, static_files=None,
               passthrough_errors=False, ssl_context=None):
    """Start a WSGI application. Optional features include a reloader,
    multithreading and fork support.

该函数下同时执行 inner() 函数,

_log('info', ' * Running on %s://%s:%d/ %s',
         ssl_context is None and 'http' or 'https',
         display_hostname, port, quit_msg)

def inner():
    try:
        fd = int(os.environ['WERKZEUG_SERVER_FD'])
    except (LookupError, ValueError):
        fd = None
    srv = make_server(hostname, port, application, threaded,
                      processes, request_handler,
                      passthrough_errors, ssl_context,
                      fd=fd)
    if fd is None:
        log_startup(srv.socket)
    srv.serve_forever()

这里执行了 make_server ,make_server()函数默认生成一个WSGIServer类,同时最后一句执行 serve_forever() 函数,该函数属于 class BaseWSGIServer(HTTPServer, object): 类,跟进

def serve_forever(self):
    self.shutdown_signal = False
    try:
        HTTPServer.serve_forever(self)
    except KeyboardInterrupt:
        pass
    finally:
        self.server_close()

执行 HTTPServer.serve_forever(self) 进入对应 socketserver.py 文件跟进

三.进入socketserver.py

def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            while not self.__shutdown_request:
                # XXX: Consider using another file descriptor or
                # connecting to the socket to wake this up instead of
                # polling. Polling reduces our responsiveness to a
                # shutdown request and wastes cpu at all other times.
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()

                self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

其中 if self in r:self._handle_request_noblock() 如果请求成功进入 _handle_request_noblock() 函数

跟进得

def _handle_request_noblock(self):
    """Handle one request, without blocking.

    I assume that select.select has returned that the socket is
    readable before this function was called, so there should be
    no risk of blocking in get_request().
    """
    try:
        request, client_address = self.get_request()
    except socket.error:
        return
    if self.verify_request(request, client_address):
        try:
            self.process_request(request, client_address)
        except:
            self.handle_error(request, client_address)
            self.shutdown_request(request)

这里调用 get_request() 函数得到请求,这个函数会pass,同时后面调用了 process_request() 函数处理请求,跟进得

def process_request(self, request, client_address):
        """Call finish_request.

        Overridden by ForkingMixIn and ThreadingMixIn.

        """
        self.finish_request(request, client_address)
        self.shutdown_request(request)

先调用 finish_request() 函数

def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)
class WSGIRequestHandler(BaseHTTPRequestHandler)://werkzeug的serving.py

class BaseHTTPRequestHandler(socketserver.StreamRequestHandler)://

class StreamRequestHandler(BaseRequestHandler): //socketserver.py


finish_request方法中执行了self.RequestHandlerClass(request, client_address, self)
self.RequestHandlerClass = RequestHandlerClass(就在__init__方法中)。所以finish_request方法本质上就是创建了一个服务处理实例,当我们创建服务处理类实例时,就会运行handle()方法,而handle()方法则一般是我们处理事务逻辑的代码块。

四.在此回到werkzeug的serving.py

进入werkzeug的handle函数得

def handle(self):
        """Handles a request ignoring dropped connections."""
        rv = None
        try:
            rv = BaseHTTPRequestHandler.handle(self)
        except (socket.error, socket.timeout) as e:
            self.connection_dropped(e)
        except Exception:
            if self.server.ssl_context is None or not is_ssl_error():
                raise
        if self.server.shutdown_signal:
            self.initiate_shutdown()
        return rv

调用了 BaseHTTPRequestHandler.handle(self) ,进入得

def handle(self):
       """Handle multiple requests if necessary."""
       self.close_connection = 1

       self.handle_one_request()
       while not self.close_connection:
           self.handle_one_request()

调用了 handle_one_request() 该方法重写跳一步回到werkzeug的该函数中

def handle_one_request(self):
        """Handle a single HTTP request."""
        self.raw_requestline = self.rfile.readline()
        if not self.raw_requestline:
            self.close_connection = 1
        elif self.parse_request():
            return self.run_wsgi()

调用了 run_wsgi() 函数,该函数内部进行写和开始回复请求等函数,同时还有一个 execute() 函数

def execute(app):
            application_iter = app(environ, start_response)
            try:
                for data in application_iter:
                    write(data)
                if not headers_sent:
                    write(b'')
            finally:
                if hasattr(application_iter, 'close'):
                    application_iter.close()
                application_iter = None

execute函数中通过application_iter = app(environ, start_response)调用了Flask应用实例app,最后实际执行为 __call__ 函数,最后 __call__ 函数上,该函数最后一句,调用wsgi_app函数

return self.wsgi_app(environ, start_response)

wsgi_app 函数中有

#将environ参数放入request_context中开始创建请求上下文函数
    #上诉函数实际即为压栈操作
    with self.request_context(environ):
        rv = self.preprocess_request()#预处理请求
        if rv is None:
            rv = self.dispatch_request()#分发处理请求,得到对应编写函数的结果
        response = self.make_response(rv)#将结果指定到制造返回的函数中
        response = self.process_response(response)
        return response(environ, start_response)

其中在dispatch_request()中这句尤为关键,其函数中有:

return self.view_functions[endpoint](**values)#通过endpoint找到对应的函数然后就是运行

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微交互

微交互

塞弗 (Dan Saffer) / 李松峰 / 人民邮电出版社 / 2013-11-1 / 35.00元

平庸的产品与伟大的产品差就差在细节上。作者Dan Saffer将通过这本书展示怎么设计微交互,即位于功能之内或周边的那些交互细节。你的手机怎么静音?你怎么知道有新邮件了?怎么修改应用的设置?诸如此类的交互细节,既可以毁掉一个产品,也可以成就一个产品。高效而有趣的微交互 ,涉及触发器、规则、循环和模式,还有反馈。透过书中生动、真实的设备及应用示例,读者将理解微交互对于塑造产品个性、赋予产品卖点的重要......一起来看看 《微交互》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具