内容简介: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.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是 Python 的 WSGI 规范的实用函数库,而对于WSGI,廖先生解释的很好啊 https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386832689740b04430a98f614b6da89da2157ea3efe2000
盗一张简书的图
而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
书中有提到
对于上下文的解释更有 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找到对应的函数然后就是运行
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。