内容简介: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使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript修炼之道
波顿纽威 / 巩朋、张铁 / 人民邮电 / 2011-11 / 29.00元
《JavaScript修炼之道》是JavaScript的实战秘籍。作者将自己多年的编程经验融入其中,不仅可以作为学习之用,更是日常JavaScript开发中不可多得的参考手册,使读者少走很多弯路。《JavaScript修炼之道》的内容涵盖了当今流行的JavaScript库的运行机制,也提供了许多应用案例。《JavaScript修炼之道》针对各任务采取对页式编排,在对各任务的讲解中,左页解释了任务的......一起来看看 《JavaScript修炼之道》 这本书的介绍吧!