我是如何构建一个 Python Web 框架并成为一名开源维护者的

2025-06-08

我如何构建 Python Web 框架并成为一名开源维护者

注意

这篇文章最初发表于blog.florimond.dev

我已经有一段时间没写博客了。实际上,差不多两个月了。那么,我最近都去哪儿了呢?

首先,工程学最后一年占用了我比预想的更多时间。不过我并不抱怨——我学到了很多有趣又实用的东西,既有理论知识,也有实践经验。而且,我想借此机会充分享受这最后一年。

故事的另一面是——我一直在构建Bocadillo,一个开源的异步 Python Web 框架。这段旅程最初是为了让我了解 Web 框架的内部原理,但到目前为止,它一直令人兴奋不已。

这意味着,因为一天只有24小时,我不得不暂时放下博客。不过现在放假了,我终于可以抽出时间回顾一下这段时间发生的事情了。🥳

在这篇博文中,我想记录下我构建 Web 框架,尤其是启动一个开源项目的整个过程。从编程技巧到项目管理以及开发工具,我已经学到了很多东西,所以我想和大家分享我的经验!

菜单上有什么?

首先,让我来向你介绍一下 Bocadillo!(我保证,这次见面会很简短,也很精彩。)

Bocadillo 是什么?

GitHub 徽标 bocadilloproject / bocadillo

(未维护)为每个人提供快速、可扩展且实时的 Web API

注意

Bocadillo 现已处于“未维护”状态。此仓库应该很快就会被归档。我们建议用户迁移到其他支持良好的替代方案,例如StarletteFastAPI更多信息请参阅#334 。


构建状态 测试覆盖率 pypi版本 代码风格


文档:https://bocadilloproject.github.io


Bocadillo 是一个Python 异步 Web 框架,它使构建高性能、高并发的 Web API 变得有趣且每个人都可以访问。

要求

Python 3.6+

安装

pip 安装 bocadillo
Enter fullscreen mode Exit fullscreen mode

例子

 bocadillo 导入 App配置

应用程序 = 应用程序()
配置应用程序

@ app.route ( “/
) asyncdefindex (  req  res ) : res.json  = {  hello: world} 
Enter fullscreen mode Exit fullscreen mode

将其保存为app.py,然后启动uvicorn服务器(已启用热重载!):

uvicorn 应用程序:应用程序——重新加载
Enter fullscreen mode Exit fullscreen mode

问好!

$ curl http://localhost:8000
{ 你好世界}
Enter fullscreen mode Exit fullscreen mode

准备好深入了解了吗?请访问文档网站

变更日志

Bocadillo 的所有更改都记录在...

电梯演讲

Bocadillo 是一个现代的 Python Web 框架,它提供了一个健全的工具包,用于使用异步编程构建高性能 Web 应用程序和服务。它兼容 Python 3.6+ 并遵循 MIT 许可证。

想开始吗?阅读文档

关键数据

Bocadillo 的首次提交是在 2018 年 11 月 3 日。截至 12 月 21 日,即一个半月后,Bocadillo 的情况如下:

  • bocadilloproject/bocadillo repo上有 500 多个提交和 31 个星号— — 欢迎随意添加您的提交!
  • PyPI发布了 10 个版本— 最新版本为 v0.7
  • PePy测量显示下载量超过 8000 次
  • 大约 30 页的文档
  • 2 位开源贡献者 — — 并欢迎更多人加入!🥳

这些数字当然很保守,但我已经很满意了。当然,如果你想支持 Bocadillo,你可以:

此外,如果您已经尝试过 Bocadillo,请通过 Twitter 或Gitter聊天室与我联系- 我很乐意听到您的声音!

哲学

从理念上来说,Bocadillo 对初学者友好,但同时也致力于为高级用户提供所需的灵活性。它注重开发者体验,同时鼓励最佳实践。

此外,Bocadillo 并非旨在追求极简主义(但也不是庞然大物)。其理念是包含一组精心挑选的内置功能,并设置合理的默认设置,以便您能够解决常见问题立即提高工作效率

目标

我的目标是让人们接受异步 Python 的新可能性,并让 Bocadillo 成为一种帮助人们更轻松、更高效地解决现实问题的工具

要达到这个目标还有很多工作要做,但是马拉松已经开始了!

说到异步Python,让我来回答一个你可能已经问过自己的问题……

异步怎么样?

通常,Web 应用程序实例会花费大量(如果不是大部分)请求处理时间等待 I/O 完成 - API 调用、数据库查询、文件系统操作等。大多数这些操作都是阻塞的,如果多个客户端请求服务器,这通常会限制性能。

像 Bocadillo 这样的异步框架旨在构建不会阻塞 I/O 操作的应用程序。为了实现这一点,我们利用了异步编程以及 Python 语言的最新功能,例如asyncioasync/await——分别在 Python 3.4+ 和 3.6+ 版本中提供。这使得我们可以将处理请求视为一个“计划”在不久的将来(即 CPU 可用时)运行的任务。

因此,除了更好地利用 CPU 之外,这种架构还有一个非常有趣的优势——我们现在可以同时处理多个请求

(注意:我没有写并行,因为异步仍然使用单个线程。并发不是并行。)

随着并发客户端数量的增加,这一特性最终会带来更稳定的吞吐量和性能。根据我自己(尚未发布)的基准测试,无论与 10 个还是 10,000 个客户端通信,Bocadillo 都能保持稳定的处理速率。另一方面,像 Flask 或 Django 这样的“同步”框架在高并发设置下,每秒请求数会显著下降。

想要了解更多关于 Python 异步编程的知识吗?以下是我推荐的几个讲座,可以按以下顺序观看:

执行

Bocadillo 基于Uvicorn(闪电般快速的 ASGI Web 服务器)和Starlette (便捷的 ASGI 工具包)构建。两者都是由Tom Christie创建的,他是 Django REST 框架的核心贡献者(以及其他贡献者)。

注意ASGI是 WSGI 的异步版本,即有关 Web 服务器如何与异步Python Web 应用程序通信的规范。

特征

Bocadillo 已包含:请求、响应、视图(基于函数和基于类)、路由和路由参数、媒体类型、重定向、模板、静态文件、后台任务、CORS、HSTS、GZip、“recipes”(又称蓝图)、中间件、钩子,甚至还有 CLI。更多功能正在发布中!

不过,Bocadillo 目前还缺少数据库层。我构建的大多数 Web 应用或 API 都需要以某种方式持久化数据,所以我相信这一点(或者至少官方会建议如何集成异步数据库层,比如Tortoise ORM)应该在某个时候融入到框架中。

你好世界!

让我们以传统的“Hello, World”脚本结束吧!

# api.py
from bocadillo import API

api = API()

@api.route("/")
async def hello(req, res):
    res.text = "Hello, World!"

if __name__ == "__main__":
    api.run()
Enter fullscreen mode Exit fullscreen mode

背后的故事

好了,关于 Bocadillo 的介绍就到此为止!既然你已经知道它是什么了,我想跟你分享一下促使我写这篇博文的故事。

它是如何开始的?又为何开始?其中有哪些最有意义的事件?让我们来一探究竟。

边做边学的项目

最初,Bocadillo 是让我深入了解 Web 框架内部原理的一种方式。在使用了近两年各种 Python 和 JS Web 框架之后,我希望能深入了解这些框架的幕后原理。我想知道它们究竟是如何运作的。

需要说明的是,Bocadillo 一开始并没有一个非常详细的计划。哎呀,我甚至都没想过是否需要另一个 Python 异步 Web 框架。我当时只想学习,什么都不想。

让我们重新发明轮子,并尽快发布它

于是,在 11 月 3 日那天,我开始实现一些非常常见的功能,感觉就像在重新发明轮子一样。这些功能包括请求、响应、视图、路由或应用服务器。“之前已经有数百个 Web 框架解决了这些问题”,我心想……

但我其实不在乎。正如@funkybob在推特上好心发给我的

重新发明轮子是一种很棒的学习方式......有时你所学到的只是你现有的框架为你做了多少事情。

支持重新发明轮子的有力论据让我意识到 Django 是一个多么庞大的野兽。

无论如何,最初的努力最终让我于 11 月 4 日在 PyPI 上发布了v0.1 版本。首次提交仅两天后,人们就能够pip install bocadillo构建一个极简的异步 Web 应用了。(谁说 Python 打包很麻烦?🐍)

潜力的初步迹象

v0.1 发布后,我继续实现更多功能,例如新类型的响应或错误处理。

11月6日,v0.2.1 版本发布。从那时起,我开始意识到 Bocadillo 很适合我的第一个成熟开源项目。这个想法很吸引我,所以我就这么做了!

那时,我还没有透露任何关于 Bocadillo 的消息,甚至连朋友都没透露过,所以我想先公开消息。我选择在 Twitter 上发布。

由于 Bocadillo 的初始代码设计和实现深受Kenneth Reitz 自己的异步框架Responder的启发,我决定向它致敬

Twitter 上关于 Bocadillo 的第一个公告,以及 Kenneth Reitz 的回答。❣️

Twitter 上关于 Bocadillo 的第一个公告,以及 Kenneth Reitz 的回答。❣️

Kenneth 的回答以及他转发该公告后的反应让我认为Bocadillo 确实很有潜力

仅仅一天时间,这个 repo 就获得了 20 颗星(这已经是个人最高纪录了!),尽管这看起来微不足道,但我认为这真的很酷。

嘘:如果您想帮助 Bocadillo 出名,您也可以为其加星标并传播这个消息!

因此,在 v0.2 发布后,我感到有动力继续开发 Bocadillo,并添加更多功能。

直到我意识到一些事情……

文档在哪儿?真的文档吗?

我很清楚:我希望 Bocadillo 成为我的第一个开源项目。我希望认真对待它,以便从中尽可能多地学习。

因此,从一开始,我就编写了一份内容详尽的README 文件,整理了一份CHANGELOG(借助keep a changelog功能),并添加了贡献指南。随着 11 月 6 日至 11 月 18 日之间越来越多的版本发布,我更新了变更日志,并在代码库的 README 文件中记录了新功能。

但很快,这种做法就变得不切实际了。README 文件越来越大,即使有目录,导航也变得很困难。

这是 11 月 17 日 repo 的目录。看到“使用情况”部分变得多么庞大了吗?

这是 11 月 17 日 repo 的目录。看到“使用情况”部分变得多么庞大了吗?

就在那时我意识到我需要适当的文件

如果你仔细想想,好的文档是让人们使用你所构建的东西的必要条件。而考虑到 Bocadillo 的规模,冗长的 README 并不算好的文档。

然后,我突然意识到——我使用和喜爱的许多大型开源工具、库或框架都有一个文档网站

这就是促使我于 11 月 18 日发布 v0.5 的原因,它有一个重要的附加功能:一个全新的文档网站,我用VuePress构建并托管在GitHub Pages上。

Bocadillo 的文档站点主页(2018 年 11 月底)。

Bocadillo 的文档站点主页(2018 年 11 月底)。

关于良好文档的必要性——Masonite 框架的创建者 Joe Mancuso曾经与我分享过这条很好的建议:

如果没有记录,它就不存在。

这就是为什么我非常重视文档并努力使其尽可能完美——你正在从事的每个项目也应该如此。

然后,在构建文档站点时,我做出了现在认为对于任何开源项目来说都非常重要的决定……

让 Bocadillo 独立发展

在发布文档之前,我将 Bocadillo 从个人仓库移至其自己的 GitHub 组织,即BocadilloProject

当时我的主要动机是想用该组织的 GitHub Pages 域名bocadilloproject.github.io来存放文档。它绝对比 GitHub Pages 更简洁、更易访问florimondmanca.github.io/bocadillo。🙃

然而,这也带来了积极的效果,让 Bocadillo 拥有了属于自己的在线空间。它不再与我的个人 GitHub 帐户绑定——该组织成为了 Bocadillo 源代码的新家。

后来,当我意识到 Bocadillo 公告正在接管我的个人 Twitter 帐户时,我创建了一个专用的 Twitter 帐户。

重点是:开源项目的存在不受其创建者的影响,这一点很重要

现在,回到故事本身——11 月 18 日,我上线了一个文档网站,供大家访问。接下来呢?

开放开发流程

截至 11 月 20 日,我都是通过私人 Trello 板来跟踪积压工作和进度。

这对我来说非常实用:我用 Trello 做各种各样的事情。但我意识到访问这个代码库的人根本不知道接下来会发生什么,也不知道他们能做出什么贡献。

事实上,从访问者的角度来看,我认为这个 repo 看起来就像任何其他个人项目一样 —— 没有问题,没有 PR,只是来自一个人的大量提交 —— 而不是社区驱动的努力,也就是我希望 Bocadillo 成为的样子。

因此,根据我的一位密友的建议并受到Dhanraj Acharya 的这篇文章的启发,我决定公开开发过程

我把所有 Trello 卡片都转换成了 GitHub 问题,并添加了有意义的标签(参见Dave Lunny 的Sane GitHub 标签)和描述。我觉得现在的仓库能更好地展示项目,也让新手更有动力。

我从中认识到,开源不仅仅意味着开放源代码。你还必须对开发过程保持开放态度。

我现在希望,通过充满问题和公共 PR 的 repo,它将吸引第一批开源贡献者。

剧透警告:确实如此!

耶!第一批贡献者!

大约在同一时间,随着 Bocadillo 规模的扩大,我开始感到需要外部建议。我担心自己可能会做出错误的设计决策,或者代码本来可以更好。简而言之,我需要贡献者

幸运的是,11 月 20 日,我非常高兴地迎来了对该 repo 的第一位贡献者,Alin Panaitiu对新“hooks”功能的PR #3进行了评论。

Alin 帮我修复了一些我最初不太确定的功能问题,并提出了一些改进建议,使其更加实用。他甚至 fork 了这个代码库,并给我发了一个 diff 来展示修复后的版本。

后来,11 月 23 日,Alin 的第一个 PR 被合并了。Bocadillo 正式迎来了第一位贡献者!🎉

PR #18 的屏幕截图。

PR #18 的屏幕截图。

我非常感动。

更棒的是,Alin 居然坚持了下来。在 v0.7 版本中,Alin 贡献了两个新功能(GZip 和 ASGI 中间件),从代码到合并的时间大概不到几个小时。谢谢 Alin,太棒了!

进入维护模式

从11月底开始,项目的进度慢了下来。一方面,我有点着急,学校的项目和考试都排在日程表上,但这并不能解释一切。我经历了一些其他的事情。

你知道,一开始,向 Bocadillo 提交代码感觉很容易。几乎没有什么遗留问题,所以很兴奋,我有很多想法。一切都还有待完成。

但随着功能的增加,工作量开始加大。发布时间也变长了——从几天变成了一周。测试、重构和文档撰写成了开发流程的主要部分。此外,我现在还负责管理 Bocadillo 的线上业务。

别误会——我没抱怨。事实上,我很高兴能够克服最初的兴奋,进入维护模式。此外,对于一个试图获得发展动力并接触社区的项目来说,这绝对是正常的转变。

现在我很享受开发 Bocadillo 的过程,不用像刚开始那样耗费我晚上的时间了。这引出了我的下一个想法……

这不是短跑,而是马拉松

我最近注意到我对这个项目的态度发生了变化。

我现在不再急于尽快推出功能,并希望大量用户涌入,大量发送星星,喜爱这个框架并乞求更多,而是更加平静地接受项目增长缓慢的想法。

这是因为维护一个开源项目是一场马拉松,而不是短跑

换句话说,成功应该永远是一个副产品,而不是目标

你可能已经注意到,Bocadillo 的目标声明中没有提到名气,也没有提到用户数量的门槛。它只是说,我希望 Bocadillo 能帮助一些解决问题。如果至少有一个人实现了这一点,我就认为这是一个胜利。如果很多人都实现了这一点,我就只会把它看作是一个副作用。

两个人站在灰色瓷砖路面上。@goian,unsplash.com

两个人站在灰色瓷砖路面上。@goian,unsplash.com

这就是 Bocadillo 背后的故事!正如你可能从这些讨论中注意到的那样,到目前为止,我一直很享受这段经历,而且现在我确信,当我决定克服对评判的恐惧,构建自己的 Web 框架时,我做出了正确的决定。这也引出了这篇博文的最后一部分。

启动您自己的开源项目的技巧

如果我希望你从这篇文章中得到什么,那一定是这样的:

开源是一种很棒的学习方式。你今天就应该开始自己的项目!

“听起来不错”,你想,“但我该怎么做呢?你有什么建议吗?”

嗯,我确实有一些。😋

首先,也是最重要的,尽可能多地学习,并确保自己乐在其中。开源永远不应该成为你的负担,如果真的成为了负担,试着找到可以委托他人或依靠社区来推动项目前进的方法。

对于其余部分,请做好准备——前面有分类的要点!

注意:如果您不确定如何实现以下任何项目和/或想了解我在实践中如何使用或配置工具,请随时查看Bocadillo 的 repo并复制您感兴趣的部分 - 毕竟它是开源的!

项目定义

  • 决定您想要构建什么。
  • 决定你为什么要构建它——尽管它不必很深奥或抽象,但有明确的动机来源会有所帮助。
  • 考虑范围设计理念:这将有助于做出明智的设计决策,并防止功能蔓延。
  • 确定您的目标客户:您的用户是 Web 开发人员、系统管理员、项目经理……?
  • 明确定义用户技能期望:您的用户应该熟悉什么,以及熟悉程度如何?
  • 决定如何分发您的项目(例如 PyPI 包)。

营销与传播

  • 建立身份:至少要有一个名字和一个标语。尽量简短、醒目、易记。并在各个地方展示它们(代码库、PyPI 页面、文档网站……)。
  • 创建视觉识别:这是您的标志和图形章程。一个可以接受的临时标志总比没有标志好。
  • 确定一个入口点,即一个人们可以自然地在线查找你的项目的位置。它可以是 GitHub 仓库、文档网站,或者任何合适的地方,但必须存在。(对于 Bocadillo 来说,我相信入口点是文档网站。)
  • 让您的项目拥有自己的生命:创建一个单独的 GitHub 组织或社交媒体帐户是一个好主意。
  • 使用社交媒体来传播新闻、公告和提示,并开始聚集人们参与项目(我使用 Twitter 来实现这一点)。
  • 为人们提供了解您前进方向的方法——路线图、问题列表、变更日志中的“未发布”部分等。

社区

  • 实施开源最佳实践:完善的README 文件、贡献指南、行为准则、问题/PR 模板等等(使用 GitHub 的清单!)。这将使代码库更受潜在贡献者的欢迎,并展现您对社区的关心。
  • 支持他人并友善待人。感谢他们的提问。提供有用的资源。
  • 创建一个非正式讨论的场所或许是个好主意。我最近决定尝试一下Gitter聊天室。

项目管理

  • 使用GitHub Issues列出你的待办事项。这样,当你还在犹豫下一步该做什么时,只需提交工单,开始处理即可!
  • 设置有意义的问题标签(请参阅Sane GitHub Labels)。
  • 您还可以设置一个GitHub 项目来在看板中显示您的问题和 PR。

代码质量

  • 设置CI/CD 管道
  • 测试要毫不宽容——除了确保您的软件按预期运行之外,它们还会帮助您和每个人捕捉回归并在进行更改时充满信心(我使用pytest作为测试框架)。
  • 测量测试覆盖率(我使用pytest-cov进行 pytest/coverage.py 集成,使用CodeCov进行覆盖率报告)。
  • 强制PR 在合并之前通过测试
  • 每个 PR 都应包含所有 3 个项目:代码、测试和文档。
  • 使用代码格式化程序来减少代码审查中的语法/代码样式噪音(我使用自以为是的Black格式化程序以及预提交钩子)。
  • 如果你没有其他审阅者,可以给自己发 PR,让他们先处理一下,过几天再回来。这样更容易看出代码是否乱了。

文档

  • 编写一份清晰且信息翔实的自述文件,其中至少包含项目描述、安装说明、快速启动示例以及文档链接或用户可以了解更多信息的地方。
  • 保留更新日志,您以后会感谢自己的。
  • 如果您要构建的不仅仅是一个简单的库,请构建一个文档站点(我使用VuePress作为静态站点生成器)。
  • 构建您的文档:教程、讨论、操作方法、参考(提示:我使用PydocMd直接从我的 Python 文档字符串生成 Markdown API 参考)。
  • 请记住:如果没有记录,它就不存在
  • 在您的 README 中添加漂亮的徽章(例如使用shields.io)。

版本控制和发布

  • 使用语义版本控制(参见SemVer)。
  • 使用bumpversion等工具自动进行版本升级
  • 自动化发布流程。我使用TravisCI将标记的提交发布到 PyPI。
  • 设置特殊的发布分支。例如,我用于release/docs文档部署和release/test发布到测试 PyPI。

成就解锁了吗?

这篇博文即将结束,让我们总结一下吧!

尽管 Bocadillo 最初只是让我了解 Web 框架内部结构的一种方式,但它现在已经变成了一个成熟的开源项目

我已经付出了很多努力来构建框架、记录框架、配置 repo 和管理版本,现在我开始将自己视为一名开源维护者——我认为这是一次非常丰富的经历!

虽然我为自己迄今为止取得的成就感到自豪,但这一切也让我感到谦卑。我现在意识到管理一个开源项目是多么具有挑战性,更不用说围绕它建立一个社区了。这太难了!

话虽如此,我确实相信你应该去尝试并创建自己的开源项目。它可以是一个简单的工具或库,也可以是一整套应用程序框架——无论哪种方式,你都会在这个过程中学到很多东西。

当然,如果您正在寻找构建开源项目或设置工具的灵感,欢迎随时查看Bocadillo 仓库。opensource.guide上也有大量精彩资源,不妨也看看这个网站!

感谢您阅读本文!一如既往,我们非常欢迎您的反馈。我尤其想听听您维护开源项目的故事。另外,如果这篇文章对您有所启发,请务必留言!❣️

祝大家节日快乐。✌️

保持联系!

如果您喜欢这篇文章,您可以在 Twitter 上找到我,了解更新、公告和新闻。🐤

鏂囩珷鏉ユ簮锛�https://dev.to/bocadillo/how-i-built-a-python-web-framework-and-became-an-open-source-maintainer-3okd
PREV
HTML 中应避免的 7 种过时做法
NEXT
你应该知道的 18 个最重要的 Git 命令简介先决条件 Git 配置初始化项目当前状态将文件添加到暂存区删除文件放弃文件的更改提交到本地列出分支从远程获取更改并将当前分支与上游合并创建新分支将本地更改推送到远程删除分支切换到新分支结论