werkzeug源码阅读笔记(二) 下
wsgi.py
————第二部分
pop_path_info()
函数
先测试一下这个函数的作用:
>>> from werkzeug.wsgi import pop_path_info
>>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
>>> pop_path_info(env)
'a'
>>> env['SCRIPT_NAME']
'/foo/a'
>>> env['PATH_INFO']
'/b'
>>> pop_path_info(env)
'b'
>>> env['SCRIPT_NAME']
'/foo/a/b'
>>> env['PATH_INFO']
''
从上面的测试,我们可以看到,pop_path_info()
函数的作用是:每执行一次,PATH_INFO
中减少一部分,同时在SCRIPT_NAME
会增加一部分
代码很简单,分三种情况:
1. PATH_INTO:类似于`/b/a`形式(去掉开头的‘/’后,剩下的部分有'/')
2. PATH_INTO:类似于`/a`形式(去掉开头的‘/’后,剩下的部分没有'/')
3. PATH_INTO:类似于`b/a`形式(不是以'/'开头)
这里就不详细说了。。
class SharedDataMiddleware()
这个类是一个WSGI的中间件(关于WSGI中间件,请看WSGI部分),用于在开发环境中提供用户请求的静态文件,这是官方给的例子:
import os
from werkzeug.wsgi import SharedDataMiddleware
app = SharedDataMiddleware(app, {
'/shared': os.path.join(os.path.dirname(__file__), 'shared') #‘/shared’:url路径, ‘shared’: 文件夹名,中间的部分为文件夹地址
})
当使用包的资源时,也有这种用法:
app = SharedDataMiddleware(app, {
'/shared': ('myapplication', 'shared_files') #‘myapplication’是python包,shared_files是这个包里面的静态文件夹
})
这样我们就可以通过访问host-address/shared
来访问静态文件。这个类的作用就是挂载静态文件到根目录
def __init__(self, app, exports, disallow=None, cache=True,
cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'):
self.app = app
self.exports = {}
self.cache = cache #header的缓存
self.cache_timeout = cache_timeout #缓存保存的时间
for key, value in iteritems(exports):
if isinstance(value, tuple): #使用包中的资源,见上例
loader = self.get_package_loader(*value)
elif isinstance(value, string_types): #根据路径找资源,见上例
if os.path.isfile(value):
loader = self.get_file_loader(value) #路径是个静态文件
else:
loader = self.get_directory_loader(value) #路径是个文件夹
else:
raise TypeError('unknown def %r' % value)
self.exports[key] = loader
if disallow is not None:
from fnmatch import fnmatch
self.is_allowed = lambda x: not fnmatch(x, disallow)
self.fallback_mimetype = fallback_mimetype
这是该类的初始化方法。从前面的例子可以看出,传入的exports
是个字典。
在这段代码里面,有几个有意思的地方,我挑出一个,其他的都大同小异:loader = self.get_file_loader(value)
这个get_file_loader()
函数也是该类的方法,我们看一下:
def get_file_loader(self, filename):
return lambda x: (os.path.basename(filename), self._opener(filename))
我们分析下结构:这个函数使用了闭包,当调用get_file_loader(filename)
的时候,会返回一个匿名函数,如果想获得匿名函数的内容,需要这样:
test = get_file_loader(filename)
t = test(x)
我们先不管这个x
到底是什么,这时候,得到的t就是匿名函数中的tuple
了
然后,我们看这个匿名函数里面的东西,发现_opener()
函数,它也是这个类中的方法:
def _opener(self, filename):
return lambda: (
open(filename, 'rb'),
datetime.utcfromtimestamp(os.path.getmtime(filename)),
int(os.path.getsize(filename))
)
_opener()
函数也返回一个匿名函数,所以同样的,需要获得匿名函数中的内容,需要这样:
opener = _opener(filename)
op = opener()
这时候,op
就是匿名函数中的tuple
了
可是我们还不明白这样写的作用是什么,于是我接着往后看代码,在类中有个__call__
函数,我截取相关部分:
cleaned_path = get_path_info(environ) #获取path_info的部分
cleaned_path = cleaned_path.strip('/')
for sep in os.sep, os.altsep: #os.sep:用来取代当前操作系统的路径分隔符(windows中是'\\',Linux中是‘/’)
if sep and sep != '/': #windows系统
cleaned_path = cleaned_path.replace(sep, '/')
path = '/' + '/'.join(x for x in cleaned_path.split('/')
if x and x != '..')
file_loader = None
for search_path, loader in iteritems(self.exports):
if search_path == path:
real_filename, file_loader = loader(None) #real_filename=文件名 file_loader=_opener函数返回的匿名函数
if file_loader is not None:
break
'''请无视这部分
if not search_path.endswith('/'):
search_path += '/'
if path.startswith(search_path):
real_filename, file_loader = loader(path[len(search_path):])
if file_loader is not None:
break
if file_loader is None or not self.is_allowed(real_filename):
eturn self.app(environ, start_response)
'''
guessed_type = mimetypes.guess_type(real_filename)
mime_type = guessed_type[0] or self.fallback_mimetype
f, mtime, file_size = file_loader() #调用前面的file_loader(),返回_opener()中的匿名函数的内容:文件流,文件创建时间,文件大小
根据__init__()
中的定义,self.exports
是个字典,所以search_path
是地址,loader
是前面分析的loader
. 具体分析在代码备注中
阅读__call__
函数,我们可以发现,该函数的作用是当服务器程序接受到用户请求后,传给这个中间件,中间件找应用程序,针对这个请求去读取静态文件,再把这个结果(文件)传递给服务器程序,从而完成交互
class DispatcherMiddleware()
这个类的作用是:可以在一个应用程序上挂载多个应用程序。好像有点不好理解,举个例子:
通常在一个项目中,我们只有一个应用程序,此时的结构是这样的:
app1/
app.py
__init.py
但是,我想做个博客程序,一般需要两个应用程序:一个用来给用户看;一个作为后台,自己可以添加、更改文章等等。 而往往nginx或者apache是不允许同时运行2个应用程序的。这时候,我们就需要在一个应用程序上挂载另一个应用程序了。此时的文件结构是这样的:
app1/
app.py (包含运行程序)
__init__.py (初始化程序)
app2/
app.py
__init__.py
app.py(用于统一一个应用程序)
我们便需要这个class DispatcherMiddleware()
类了
在app.py
中:
from werkzeug.wsgi import DispatcherMiddleware
from app1.app import app as app1
from app2.app import app as app2
app = DispatcherMiddleware(app1, {'/app2': app2})
使用这个方法,我们可以在WSGI层面上组合多个WSGI应用(可以是Flask应用,可以是Django应用或者其他应用). 每个应用都是独立的,都以各自的配置运行,互不干扰,并在WSGI层面被调度
其原理是:每个独立的应用都是一个合法的WSGI应用,它们通过调度中间件组合为一个基于前缀调度的大应用.
了解了这个类的作用,源码就显得特别简单了:
class DispatcherMiddleware(object):
def __init__(self, app, mounts=None):
self.app = app
self.mounts = mounts or {}
def __call__(self, environ, start_response):
script = environ.get('PATH_INFO', '') #获得路径
path_info = ''
while '/' in script:
if script in self.mounts:
app = self.mounts[script] #获得挂载的对应的应用
break
script, last_item = script.rsplit('/', 1) #获得减去挂载名的路径
path_info = '/%s%s' % (last_item, path_info)
else:
app = self.mounts.get(script, self.app) #获得原应用
original_script_name = environ.get('SCRIPT_NAME', '')
environ['SCRIPT_NAME'] = original_script_name + script #更新SCRIPT_NAME
environ['PATH_INFO'] = path_info #更新PATH_INFO
return app(environ, start_response)
解析见上面源码中的备注
werkzeug源码阅读笔记(二) 下的更多相关文章
- werkzeug源码阅读笔记(二) 上
因为第一部分是关于初始化的部分的,我就没有发布出来~ wsgi.py----第一部分 在分析这个模块之前, 需要了解一下WSGI, 大致了解了之后再继续~ get_current_url()函数 很明 ...
- Werkzeug源码阅读笔记(四)
今天主要讲一下werkzeug中的routing模块.这个模块是werkzeug中的重点模块,Flask中的路由相关的操作使用的都是这个模块 routing模块的用法 在讲解模块的源码之前,先讲讲这个 ...
- Werkzeug源码阅读笔记(三)
这次主要讲下werkzeug中的Local. 源码在werkzeug/local.py Thread Local 在Python中,状态是保存在对象中.Thread Local是一种特殊的对象,它是对 ...
- Detectron2源码阅读笔记-(二)Registry&build_*方法
Trainer解析 我们继续Detectron2代码阅读笔记-(一)中的内容. 上图画出了detectron2文件夹中的三个子文件夹(tools,config,engine)之间的关系.那么剩下的 ...
- Android源码阅读笔记二 消息处理机制
消息处理机制: .MessageQueue: 用来描述消息队列2.Looper:用来创建消息队列3.Handler:用来发送消息队列 初始化: .通过Looper.prepare()创建一个Loope ...
- Apollo源码阅读笔记(二)
Apollo源码阅读笔记(二) 前面 分析了apollo配置设置到Spring的environment的过程,此文继续PropertySourcesProcessor.postProcessBeanF ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- Three.js源码阅读笔记-5
Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...
- jdk源码阅读笔记-LinkedHashMap
Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...
随机推荐
- ubuntu 配置JDK环境变量
ubuntu 配置JDK环境变量 (2011-11-25 16:45:59) 转载▼ 标签: ubuntu jdk 环境变量 杂谈 分类: Linux_Ubuntu_CentOs 过程如下: 1. 先 ...
- SQL高级查询的练习题
Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 问题 ...
- C++容器在遍历时的删除问题
容器是非常便捷常用的,经常用容器来存储多条数据,然后对数据进行增删查改. 有时要在遍历的同时删除一条数据,但是这样删除的时候程序会导致程序崩溃. 这个问题在GCC 中不会出现,而在VS2008,VS2 ...
- zoj1183 Scheduling Lectures
这道题题意不想说了,跑了640ms,感觉水过去了,应该能通过单调队列优化,很长时间没碰已经不知道怎么写了,就说说现在的写法吧. 状态定义很关键:dp[i][j]把前j个topic放在前i堂课. 因为这 ...
- 用邻接表实现DFS和BFS
#include <stdio.h> #include <stdlib.h> #define MAXVERTEX 10 typedef char VertexType; //顶 ...
- log4net自定义扩展及配置说明
log4net文件保存配置我就不说太多了,网上一大把的,数据库配置其实网上也有,只是我第一次按照网上的配置没有跑通,我就说下数据库配置需要注意的地方吧. 下面是一个log4net的数据库代码配置 pu ...
- NoSQL的价值到底在哪里?
关系型数据库的价值 持久化数据:通过数据库来保存数据 处理并发:通过事务方式处理并发 集成:共享数据库集成,多个应用程序可以同时访问同一份数据 标准模型:前几种功能已经成标准,开发人员学习成本低,虽然 ...
- IBM developerWorks 的Ajax系列教程
掌握 Ajax,第 1 部分: Ajax 入门简介 http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro1.html?csrf-799150205 ...
- Maven管理Android项目1
maven-android-plugin网站:https://code.google.com/p/maven-android-plugin/wiki/GettingStarted android ...
- raspberrypi VNC server
安装apt-get install tightvncserver tightvnc-java 启动vncserver -name vnc_raspi -depth 24 -geometry 800x6 ...