Python微型Web框架Bottle源码分析

简介: Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python。它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系。 选择源码分析的版本是 Release 于 2009 年 7 月 11 日的 0.4.10 (这是我能找到的最早的发布版本了)。

Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python。它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系。

选择源码分析的版本是 Release 于 2009 年 7 月 11 日的 0.4.10 (这是我能找到的最早的发布版本了)。

为什么要分析 Bottle 这个比较冷门的框架?

8481c8f592b7f349aa84a1de5c171db681516edfBottle 从发布至今一直贯彻的微型 Web 框架的理念。
8481c8f592b7f349aa84a1de5c171db681516edf Bottle 一直坚持单文件发布,也就是只有一个 bottle.py 文件。
8481c8f592b7f349aa84a1de5c171db681516edf 除了 Python 标准库之外没有依赖关系。
8481c8f592b7f349aa84a1de5c171db681516edf 与 Flask、Django 都遵循 PEP-3333 的 WSGI 协议。
8481c8f592b7f349aa84a1de5c171db681516edf 0.4.10 版本代码量小,加上大量注释也只有不到 1000 行的代码。

所以,抛开框架的高级功能,单单从一个 Web 框架怎么处理请求的角度来看,Bottle 是最佳的选择。

Flask 从第一版开始就是依赖于 werkzeug 实现,更多的实现细节需要从 werkzeug 中查找。

Django 是个重型框架,不适合整体代码阅读,各个组件看看就可以。

Tornado 是个异类,和 WSGI 没有什么关系。

在阅读之前最好从 Github 上下载一份 0.4.10 版本的 Bottle 的源码,边看边阅读本文。

阅读本文你需要有如下技能:

8481c8f592b7f349aa84a1de5c171db681516edf 熟悉 Python 的语法
8481c8f592b7f349aa84a1de5c171db681516edf 熟悉 HTTP 协议
8481c8f592b7f349aa84a1de5c171db681516edf 至少使用过一种 WSGI 的框架
8481c8f592b7f349aa84a1de5c171db681516edf 了解 CGI
8481c8f592b7f349aa84a1de5c171db681516edf 看得懂中文
流程结构分析

代码虽然不多,但是毫无目的的看难免思绪混乱,会看的心烦意乱,甚至会有产生「写的这是什么鬼?」的想法。

一个 Web 框架最核心也是最基本的功能就是处理 请求响应

但是在这之前,需要先创建一个 Server,才能开始处理啊!

所以大体的流程如下:

8481c8f592b7f349aa84a1de5c171db681516edf怎么创建一个 WSGI 的 Server 。
8481c8f592b7f349aa84a1de5c171db681516edf怎么处理到来的请求。
8481c8f592b7f349aa84a1de5c171db681516edf怎么处理响应。
创建 WSGI Server

在 Bottle 中关于创建一个标准的 WSGI Server 涉及的类或者方法只有 3 个。

注意,这里只关心一个标准的 WSGI,和核心功能。包括注释、错误处理、参数处理,会统统删除。

从文档中可以看到 Bottle 是通过一个 run 方法启动的。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

WSGIRefServer 继承自 ServerAdapter,并且覆盖了 run 方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这个 run 方法本身也是很简单,通过 Python 标准库中的 make_server 创建了一个 WSGI Server 然后跑了起来。

注意在 run 方法中的 WSGIHandler 和 WSGIRefServer.run 中的 handler 参数,这个就是如何处理一次请求和响应的关键所在。

在这之前,还需要先看看 Bottle 对 Request 和 Respouse 的定义。

Request 定义

Bottle 为每次请求都会把一些参数保存在当前的线程中,通过继承 threading.local 实现线程安全。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Request 是由一个方法和 8 个属性构成。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

bind 方法除了初始化一些变量以外,还添加 environ 到本次请求当中,environ 是一个字典包含了 CGI 的环境变量,更多 environ 内容参考PEP-3333 中 environ Variables 部分

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这三个属性比较简单,只是从 _environ 中取出了CGI 的某个环境变量。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

GET 属性把 query_string 解析成字典放入当前请求的变量中,所以在请求中获取 GET 方法的参数可以使用 requst.GET['xxxx'] 这样子的用法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

POST 属性从 wsgi.input 中获取内容(也就是表单提交的内容)放入当前请求的变量中,可以通过request.POST['xxxx'] 来获取数据。

从 GET 和 POST 这两属性的使用来看,包括 Flask 和 Django 都实现了类似的方法,这方法属性拥有一样的步骤就是获取数据,然后转换成标准的字典格式,实现上来看没什么复杂的,就是普通的字符串处理而已。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

params 属性提供了一个便利访问数据的方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Bottle 的 COOKIES 管理比较简单,只是单纯的从 CGI 中获取请求的 Cookie,如果存在的话直接返回。

以上就是 Bottle 的请求定义的内容。

简单总结来看,Request 从 CGI 中获取数据并且做一些数据处理,然后绑定到变量上。

Response 定义

整体结构和 Resquest 大致一样。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

bind 方法只是初始化了一些变量。其中比较有意思的是 HeaderDict。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这是一个扩展于 dict 的字典,转化成大小写无关的 Title key ,还可以以列表方式添加多个成员。这个 HeaderDict 有意思的地方有两个:

8481c8f592b7f349aa84a1de5c171db681516edf 与大小无关的 Ttile key,也就是会吧 key 转成以大写头其他小写的 key
8481c8f592b7f349aa84a1de5c171db681516edf 存储重复 kv 值时候 values 会以 list 形式存储。如果 values 是多层 list,会自动解析成一层数据。
8481c8f592b7f349aa84a1de5c171db681516edf 重写 items 方法,以二元元组方式返回数据,包括多值数据。
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Response 对 Cookie 的初始化,并且提供了设置的方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

为 content_type 属性提供了 set 和 get 方法,针对的是 Header 中的 Content-Type。

添加路由和 handler

这部分由一个装饰器和三个方法组成。

8481c8f592b7f349aa84a1de5c171db681516edf compile_route:路由正则
8481c8f592b7f349aa84a1de5c171db681516edf add_route:添加路由
8481c8f592b7f349aa84a1de5c171db681516edf route:路由装饰器
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

路由装饰器,简化 add_route 的调用。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

ROUTES_SIMPLE 和 ROUTES_REGEXP 是两个全局字典,用于存储路由相关数据(方法,参数,地址)。

简单路由放入 ROUTES_SIMPLE,以 method 为 key ,在 method 中再以路由地址为 key,处理函数 handler 为 value 存储。

复杂路由放入 ROUTES_REGEXP,以 method 为 key,以 route 和 handler 组成的元组列表存储。

处理请求和响应

根据 PEP-3333 文档需要为编写一个可调用对象(可以是函数,或者是具有 __call__ 方法的类)。

Bottle 中的 WSGIHandler 正是这么一个可调用对象。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

为了和代码契合度高,分析已经注释在当中。

处理流程如下:

8481c8f592b7f349aa84a1de5c171db681516edf拿到线程独立的 request 和 response
8481c8f592b7f349aa84a1de5c171db681516edfbind environ 数据
8481c8f592b7f349aa84a1de5c171db681516edf根据 match_url 找到处理的 handler 和参数,执行 
8481c8f592b7f349aa84a1de5c171db681516edf处理 Bottle 错误
8481c8f592b7f349aa84a1de5c171db681516edf处理内部错误
8481c8f592b7f349aa84a1de5c171db681516edf如果是文件则发送文件,不是的话正常返回字符串
8481c8f592b7f349aa84a1de5c171db681516edf设置 Set-Cookie header
8481c8f592b7f349aa84a1de5c171db681516edf结束
结束
Bottle 0.4.10 版本的核心内容就差么多,其他都是一些错误处理之类的。

该版本的 Bottle 以简单的过程,描述出了一个基于 WSGI 的 Web 框架是怎么样处理请求和响应的过程,完全基于 Python 标准库实现。


原文发布时间为:2017-05-22

本文作者:正小歪

本文来自云栖社区合作伙伴“Python中文社区”,了解相关信息可以关注“Python中文社区”微信公众号

相关文章
|
3天前
|
数据采集 NoSQL 中间件
python-scrapy框架(四)settings.py文件的用法详解实例
python-scrapy框架(四)settings.py文件的用法详解实例
8 0
|
3天前
|
存储 数据采集 数据库
python-scrapy框架(三)Pipeline文件的用法讲解
python-scrapy框架(三)Pipeline文件的用法讲解
6 0
|
3天前
|
存储 数据采集 JSON
python-scrapy框架(二)items文件夹的用法讲解
python-scrapy框架(二)items文件夹的用法讲解
11 0
|
3天前
|
数据采集 前端开发 中间件
python-scrapy框架(一)Spider文件夹的用法讲解
python-scrapy框架(一)Spider文件夹的用法讲解
10 0
|
3天前
|
存储 程序员 API
python web开发示例详解
python web开发示例详解
12 0
|
4天前
|
数据采集 Python
使用Python实现简单的Web爬虫
本文将介绍如何使用Python编写一个简单的Web爬虫,用于抓取网页上的信息。通过分析目标网页的结构,利用Python中的requests和Beautiful Soup库,我们可以轻松地提取所需的数据,并将其保存到本地或进行进一步的分析和处理。无论是爬取新闻、股票数据,还是抓取图片等,本文都将为您提供一个简单而有效的解决方案。
|
5天前
|
持续交付 Python
使用Python实现自动化Web内容巡检
使用Python实现自动化Web内容巡检
17 1
|
5天前
|
数据采集 存储 XML
如何利用Python构建高效的Web爬虫
本文将介绍如何使用Python语言以及相关的库和工具,构建一个高效的Web爬虫。通过深入讨论爬虫的基本原理、常用的爬虫框架以及优化技巧,读者将能够了解如何编写可靠、高效的爬虫程序,实现数据的快速获取和处理。
|
6天前
|
前端开发 JavaScript Python
使用Python读取本地行情csv文件,做出web网页画出K线图实现案例
【5月更文挑战第4天】使用Python绘制K线图的步骤:1) 安装pandas, matplotlib和Flask;2) 用pandas读取CSV文件并处理数据;3) 创建Flask应用,渲染包含K线图数据的HTML;4) 编写HTML,使用ECharts库绘制K线图。
25 0
|
11天前
|
中间件 Go API
【Go 语言专栏】Go 语言中的 Web 框架比较与选择
【4月更文挑战第30天】本文对比了Go语言中的四个常见Web框架:功能全面的Beego、轻量级高性能的Gin、简洁高效的Echo,以及各自的性能、功能特性、社区支持。选择框架时需考虑项目需求、性能要求、团队经验和社区生态。开发者应根据具体情况进行权衡,以找到最适合的框架。