内容简介: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使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms for Image Processing and Computer Vision
Parker, J. R. / 2010-12 / 687.00元
A cookbook of algorithms for common image processing applications Thanks to advances in computer hardware and software, algorithms have been developed that support sophisticated image processing with......一起来看看 《Algorithms for Image Processing and Computer Vision》 这本书的介绍吧!