做硬件,用python web框架哪个好那个框架好?

15个最受欢迎的Python开源框架
发表于 10:53|
来源伯乐在线|
作者伯乐在线
摘要:我们从GitHub中整理出了15个最受欢迎的Python开源框架,这些框架包括事件I/O、OLAP、Web开发、高性能网络通信、测试、爬虫等。
我们从GitHub中整理出了15个最受欢迎的Python开源框架,这些框架包括事件I/O、OLAP、Web开发、高性能网络通信、测试、爬虫等。
: Python Web应用开发框架
Django 应该是最出名的Python框架,GAE甚至Erlang都有框架受它影响。Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构、以及全功能的管理后台。
:基于Greenlet的事件I/O框架
Diesel提供一个整洁的API来编写网络客户端和服务器。支持TCP和UDP。
:一个用Python编写的轻量级Web应用框架
Flask是一个使用Python编写的轻量级Web应用框架。基于Werkzeug WSGI工具箱和Jinja2 模板引擎。Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能。Flask没有默认使用的数据库、窗体验证工具。
:轻量级Python OLAP框架
Cubes是一个轻量级Python框架,包含OLAP、多维数据分析和浏览聚合数据(aggregated data)等工具。
:创造矢量地图的轻量级Python框架
Kartograph是一个Python库,用来为ESRI生成SVG地图。Kartograph.py目前仍处于beta阶段,你可以在virtualenv环境下来测试。
:Python的事件驱动并发框架
Pulsar是一个事件驱动的并发框架,有了pulsar,你可以写出在不同进程或线程中运行一个或多个活动的异步服务器。
:全栈式Web框架
Web2py是一个为Python语言提供的全功能Web应用框架,旨在敏捷快速的开发Web应用,具有快速、安全以及可移植的数据库驱动的应用,兼容Google
App Engine。
:构建云API和网络应用后端的高性能Python框架
Falcon是一个构建云API的高性能Python框架,它鼓励使用REST架构风格,尽可能以最少的力气做最多的事情。
:Python版的Spark
DPark是Spark的Python克隆,是一个Python实现的分布式计算框架,可以非常方便地实现大规模数据处理和迭代计算。DPark由豆瓣实现,目前豆瓣内部的绝大多数数据分析都使用DPark完成,正日趋完善。
:基于Python的持续集成测试框架
Buildbot是一个开源框架,可以自动化软件构建、测试和发布等过程。每当代码有改变,服务器要求不同平台上的客户端立即进行代码构建和测试,收集并报告不同平台的构建和测试结果。
:基于ZeroMQ的高性能分布式RPC框架
Zerorpc是一个基于ZeroMQ和MessagePack开发的远程过程调用协议(RPC)实现。和 Zerorpc 一起使用的 Service
API 被称为 zeroservice。Zerorpc 可以通过编程或命令行方式调用。
:微型Python Web框架
Bottle是一个简单高效的遵循WSGI的微型python Web框架。说微型,是因为它只有一个文件,除Python标准库外,它不依赖于任何第三方模块。
:异步非阻塞IO的Python Web框架
Tornado的全称是Torado Web Server,从名字上看就可知道它可以用作Web服务器,但同时它也是一个Python Web的开发框架。最初是在FriendFeed公司的网站上使用,FaceBook收购了之后便开源了出来。
:轻量级的Python Web框架
webpy的设计理念力求精简(Keep it simple and powerful),源码很简短,只提供一个框架所必须的东西,不依赖大量的第三方模块,它没有URL路由、没有模板也没有数据库的访问。
:Python的爬虫框架
Scrapy是一个使用Python编写的,轻量级的,简单轻巧,并且使用起来非常的方便。
本文作者:
本文链接:
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章python 运维平台用什么框架_百度知道
python 运维平台用什么框架
我有更好的答案
当然也有python爱好者喜欢完全用python开发的运维框架。这个真的不是必要的,也没有必要。各有所长几乎每个运维平台都支持python。python很适合做运维脚本。综合使用,不造重复的轮子。这才是正常的开发方式。配置还有其它的持续集成的方案。 监控比较常用的是nagios,这个平台就支持python,也支持perl之类的,不重复做轮子。 通常运维平台主要是指监控与配置管理。根据自己需要做功能的定制与扩展。其中监控更是主要的内容。python开发太容易了。所以做一个框架与平台也就几天的事情。 通常不会这么做
采纳率:97%
来自团队:
问题是什么。。
为您推荐:
其他类似问题
您可能关注的内容
python的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。从零构建一个简单的 Python 框架
为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点:
你有一个新奇的想法,觉得将会取代其他的框架
你想要获得一些名气
你遇到的问题很独特,以至于现有的框架不太合适
你对 web 框架是如何工作的很感兴趣,因为你想要成为一位更好的 web 开发者。
接下来的笔墨将着重于最后一点。这篇文章旨在通过对设计和实现过程一步一步的阐述告诉读者,我在完成一个小型的服务器和框架之后学到了什么。你可以在这个[1]中找到这个项目的完整代码。
我希望这篇文章可以鼓励更多的人来尝试,因为这确实很有趣。它让我知道了 web 应用是如何工作的,而且这比我想的要容易的多!
框架可以处理请求-响应周期、身份认证、数据库访问、模板生成等部分工作。Web 开发者使用框架是因为,大多数的 web 应用拥有大量相同的功能,而对每个项目都重新实现同样的功能意义不大。
比较大的的框架如 Rails 和 Django 实现了高层次的抽象,或者说“自备电池”(“batteries-included”,这是 Python 的口号之一,意即所有功能都自足。)。而实现所有的这些功能可能要花费数千小时,因此在这个项目上,我们重点完成其中的一小部分。在开始写代码前,我先列举一下所需的功能以及限制。
处理 HTTP 的 GET 和 POST 请求。你可以在[2]中对 HTTP 有个大致的了解。
实现异步操作(我喜欢Python 3 的 asyncio 模块)。
简单的路由逻辑以及参数撷取。
像其他微型框架一样,提供一个简单的用户级 API 。
支持身份认证,因为学会这个很酷啊(微笑)。
将只支持 HTTP 1.1 的一个小子集,不支持传输编码transfer-encoding、HTTP 认证http-auth、内容编码content-encoding(如 gzip)以及[3]等功能。
不支持对响应内容的 MIME 判断 - 用户需要手动指定。
不支持 WSGI - 仅能处理简单的 TCP 连接。
不支持数据库。
我觉得一个小的用例可以让上述内容更加具体,也可以用来演示这个框架的 API:
fromdiy_framework importApp,Router
fromdiy_framework.http_utils importResponse
#GET simple route
async defhome(r):
rsp =Response()
rsp.set_header('Content-Type','text/html')
rsp.body ='&html&&body&&b&test&/b&&/body&&/html&'
#GET route+params
async defwelcome(r,name):
return"Welcome {}".format(name)
#POST route+body param
async defparse_form(r):
ifr.method =='GET':
return'form'
name =r.body.get('name','')[0]
password =r.body.get('password','')[0]
return"{0}:{1}".format(name,password)
#application =router +http server
router =Router()
router.add_routes({
r'/welcome/{name}':welcome,
r'/':home,
r'/login':parse_form,})
app =App(router)
app.start_server()
' 用户需要定义一些能够返回字符串或Response对象的异步函数,然后将这些函数与表示路由的字符串配对,最后通过一个函数调用(start_server)开始处理请求。
完成设计之后,我将它抽象为几个我需要编码的部分:
接受 TCP 连接以及调度一个异步函数来处理这些连接的部分
将原始文本解析成某种抽象容器的部分
对于每个请求,用来决定调用哪个函数的部分
将上述部分集中到一起,并为开发者提供一个简单接口的部分
我先编写一些测试,这些测试被用来描述每个部分的功能。几次重构后,整个设计被分成若干部分,每个部分之间是相对解耦的。这样就非常好,因为每个部分可以被独立地研究学习。以下是我上文列出的抽象的具体体现:
一个 HTTPServer 对象,需要一个 Router 对象和一个 http_parser 模块,并使用它们来初始化。
HTTPConnection 对象,每一个对象表示一个单独的客户端 HTTP 连接,并且处理其请求-响应周期:使用 http_parser 模块将收到的字节流解析为一个 Request 对象;使用一个 Router 实例寻找并调用正确的函数来生成一个响应;最后将这个响应发送回客户端。
一对 Request 和 Response 对象为用户提供了一种友好的方式,来处理实质上是字节流的字符串。用户不需要知道正确的消息格式和分隔符是怎样的。
一个包含“路由:函数”对应关系的 Router 对象。它提供一个添加配对的方法,可以根据 URL 路径查找到相应的函数。
最后,一个 App 对象。它包含配置信息,并使用它们实例化一个 HTTPServer 实例。
让我们从HTTPConnection开始来讲解各个部分。
模拟异步连接
为了满足上述约束条件,每一个 HTTP 请求都是一个单独的 TCP 连接。这使得处理请求的速度变慢了,因为建立多个 TCP 连接需要相对高的花销(DNS 查询,TCP 三次握手,[4]等等的花销),不过这样更加容易模拟。对于这一任务,我选择相对高级的 [5]模块,它建立在 [6]的基础之上。我强烈推荐你读一读标准库中的相应代码,很有意思!
中。HTTPConnection的实例能够处理多个任务。首先,它使用asyncio.StreamReader对象以增量的方式从 TCP 连接中读取数据,并存储在缓存中。每一个读取操作完成后,它会尝试解析缓存中的数据,并生成一个Request对象。一旦收到了这个完整的请求,它就生成一个回复,并通过asyncio.StreamWriter对象发送回客户端。当然,它还有两个任务:超时连接以及错误处理。
你可以在[7]浏览这个类的完整代码。我将分别介绍代码的每一部分。为了简单起见,我移除了代码文档。
classHTTPConnection(object):
definit(self,http_server,reader,writer):
self.router =http_server.router
self.http_parser =http_server.http_parser
self.loop =http_server.loop
self._reader =reader
self._writer =writer
self._buffer =bytearray()
self._conn_timeout =None
self.request =Request()
这个init方法没啥意思,它仅仅是收集了一些对象以供后面使用。它存储了一个router对象、一个http_parser对象以及loop对象,分别用来生成响应、解析请求以及在事件循环中调度任务。
然后,它存储了代表一个 TCP 连接的读写对,和一个充当原始字节缓冲区的空[8]。_conn_timeout存储了一个 [9]的实例,用来管理超时逻辑。最后,它还存储了Request对象的一个单一实例。
下面的代码是用来接受和发送数据的核心功能:
async defhandle_request(self):
whilenotself.request.finished andnotself._reader.at_eof():
data =await self._reader.read(1024)
self._reset_conn_timeout()
await self.process_data(data)
ifself.request.finished:
await self.reply()
elifself._reader.at_eof():
raiseBadRequestException()
except(NotFoundException,
BadRequestException)ase:
self.error_reply(e.code,body=Response.reason_phrases[e.code])
exceptExceptionase:
self.error_reply(500,body=Response.reason_phrases[500])
self.close_connection()
所有内容被包含在try-except代码块中,这样在解析请求或响应期间抛出的异常可以被捕获到,然后一个错误响应会发送回客户端。
在while循环中不断读取请求,直到解析器将self.request.finished设置为 True ,或者客户端关闭连接所触发的信号使得self._reader_at_eof()函数返回值为 True 为止。这段代码尝试在每次循环迭代中从StreamReader中读取数据,并通过调用self.process_data(data)函数以增量方式生成self.request。每次循环读取数据时,连接超时计数器被重置。
这儿有个错误,你发现了吗?稍后我们会再讨论这个。需要注意的是,这个循环可能会耗尽 CPU 资源,因为如果没有读取到东西self._reader.read()函数将会返回一个空的字节对象b''。这就意味着循环将会不断运行,却什么也不做。一个可能的解决方法是,用非阻塞的方式等待一小段时间:await asyncio.sleep(0.1)。我们暂且不对它做优化。
还记得上一段我提到的那个错误吗?只有从StreamReader读取数据时,self._reset_conn_timeout()函数才会被调用。这就意味着,直到第一个字节到达时,timeout才被初始化。如果有一个客户端建立了与服务器的连接却不发送任何数据,那就永远不会超时。这可能被用来消耗系统资源,从而导致拒绝服务式攻击(DoS)。修复方法就是在init函数中调用self._reset_conn_timeout()函数。
当请求接受完成或连接中断时,程序将运行到if-else代码块。这部分代码会判断解析器收到完整的数据后是否完成了解析。如果是,好,生成一个回复并发送回客户端。如果不是,那么请求信息可能有错误,抛出一个异常!最后,我们调用self.close_connection执行清理工作。
解析请求的部分在self.process_data方法中。这个方法非常简短,也易于测试:
async defprocess_data(self,data):
self._buffer.extend(data)
self._buffer =self.http_parser.parse_into(
self.request,self._buffer)
每一次调用都将数据累积到self._buffer中,然后试着用self.http_parser来解析已经收集的数据。这里需要指出的是,这段代码展示了一种称为[10]的模式。如果你还记得init函数的话,应该知道我们传入了一个包含http_parser对象的http_server对象。在这个例子里,http_parser对象是diy_framework包中的一个模块。不过它也可以是任何含有parse_into函数的类,这个parse_into函数接受一个Request对象以及字节数组作为参数。这很有用,原因有二:一是,这意味着这段代码更易扩展。如果有人想通过一个不同的解析器来使用HTTPConnection,没问题,只需将它作为参数传入即可。二是,这使得测试更加容易,因为http_parser不是硬编码的,所以使用虚假数据或者 [11]对象来替代是很容易的。
下一段有趣的部分就是reply方法了:
async defreply(self):
request =self.request
handler =self.router.get_handler(request.path)
response =await handler.handle(request)
ifnotisinstance(response,Response):
response =Response(code=200,body=response)
self._writer.write(response.to_bytes())
await self._writer.drain()
这里,一个HTTPConnection的实例使用了HTTPServer中的router对象来得到一个生成响应的对象。一个路由可以是任何一个拥有get_handler方法的对象,这个方法接收一个字符串作为参数,返回一个可调用的对象或者抛出NotFoundException异常。而这个可调用的对象被用来处理请求以及生成响应。处理程序由框架的使用者编写,如上文所说的那样,应该返回字符串或者Response对象。Response对象提供了一个友好的接口,因此这个简单的 if 语句保证了无论处理程序返回什么,代码最终都得到一个统一的Response对象。
接下来,被赋值给self._writer的StreamWriter实例被调用,将字节字符串发送回客户端。函数返回前,程序在await self._writer.drain()处等待,以确保所有的数据被发送给客户端。只要缓存中还有未发送的数据,self._writer.close()方法就不会执行。
HTTPConnection类还有两个更加有趣的部分:一个用于关闭连接的方法,以及一组用来处理超时机制的方法。首先,关闭一条连接由下面这个小函数完成:
defclose_connection(self):
self._cancel_conn_timeout()
self._writer.close()
每当一条连接将被关闭时,这段代码首先取消超时,然后把连接从事件循环中清除。
超时机制由三个相关的函数组成:第一个函数在超时后给客户端发送错误消息并关闭连接;第二个函数用于取消当前的超时;第三个函数调度超时功能。前两个函数比较简单,我将详细解释第三个函数_reset_cpmm_timeout()。
def_conn_timeout_close(self):
self.error_reply(500,'timeout')
self.close_connection()
def_cancel_conn_timeout(self):
ifself._conn_timeout:
self._conn_timeout.cancel()
def_reset_conn_timeout(self,timeout=TIMEOUT):
self._cancel_conn_timeout()
self._conn_timeout =self.loop.call_later(
timeout,self._conn_timeout_close)
每当_reset_conn_timeout函数被调用时,它会先取消之前所有赋值给self._conn_timeout的asyncio.Handle对象。然后,使用 [12]函数。_conn_timeout_close函数在超时数秒(timeout)后执行。如果你还记得handle_request函数的内容,就知道每当接收到数据时,这个函数就会被调用。这就取消了当前的超时并且重新安排_conn_timeout_close函数在超时数秒(timeout)后执行。只要接收到数据,这个循环就会不断地重置超时回调。如果在超时时间内没有接收到数据,最后函数_conn_timeout_close就会被调用。
我们需要创建HTTPConnection对象,并且正确地使用它们。这一任务由HTTPServer类完成。HTTPServer类是一个简单的容器,可以存储着一些配置信息(解析器,路由和事件循环实例),并使用这些配置来创建HTTPConnection实例:
classHTTPServer(object):
definit(self,router,http_parser,loop):
self.router =router
self.http_parser =http_parser
self.loop =loop
async defhandle_connection(self,reader,writer):
connection =HTTPConnection(self,reader,writer)
asyncio.ensure_future(connection.handle_request(),loop=self.loop)
HTTPServer的每一个实例能够监听一个端口。它有一个handle_connection的异步方法来创建HTTPConnection的实例,并安排它们在事件循环中运行。这个方法被传递给 [13]作为一个回调函数。也就是说,每当一个 TCP 连接初始化时(以StreamReader和StreamWriter为参数),它就会被调用。
self._server =HTTPServer(self.router,self.http_parser,self.loop)
self._connection_handler =asyncio.start_server(
self._server.handle_connection,
host=self.host,
port=self.port,
reuse_address=True,
reuse_port=True,
loop=self.loop)
这就是构成整个应用程序工作原理的核心:asyncio.start_server接受 TCP 连接,然后在一个预配置的HTTPServer对象上调用一个方法。这个方法将处理一条 TCP 连接的所有逻辑:读取、解析、生成响应并发送回客户端、以及关闭连接。它的重点是 IO 逻辑、解析和生成响应。
讲解了核心的 IO 部分,让我们继续。
这个微型框架的使用者被宠坏了,不愿意和字节打交道。它们想要一个更高层次的抽象 —— 一种更加简单的方法来处理请求。这个微型框架就包含了一个简单的 HTTP 解析器,能够将字节流转化为 Request 对象。
这些 Request 对象是像这样的容器:
classRequest(object):
definit(self):
self.method =None
self.path =None
self.query_params ={}
self.path_params ={}
self.headers ={}
self.body =None
self.body_raw =None
self.finished =False
它包含了所有需要的数据,可以用一种容易理解的方法从客户端接受数据。哦,不包括 cookie ,它对身份认证是非常重要的,我会将它留在第二部分。
每一个 HTTP 请求都包含了一些必需的内容,如请求路径和请求方法。它们也包含了一些可选的内容,如请求体、请求头,或是 URL 参数。随着 REST 的流行,除了 URL 参数,URL 本身会包含一些信息。比如,"/user/1/edit" 包含了用户的 id 。
一个请求的每个部分都必须被识别、解析,并正确地赋值给 Request 对象的对应属性。HTTP/1.1 是一个文本协议,事实上这简化了很多东西。(HTTP/2 是一个二进制协议,这又是另一种乐趣了)
解析器不需要跟踪状态,因此http_parser模块其实就是一组函数。调用函数需要用到Request对象,并将它连同一个包含原始请求信息的字节数组传递给parse_into函数。然后解析器会修改Request对象以及充当缓存的字节数组。字节数组的信息被逐渐地解析到 request 对象中。
http_parser模块的核心功能就是下面这个parse_into函数:
defparse_into(request,buffer):
_buffer =buffer[:]
ifnotrequest.method andcan_parse_request_line(_buffer):
(request.method,request.path,
request.query_params)=parse_request_line(_buffer)
remove_request_line(_buffer)
ifnotrequest.headers andcan_parse_headers(_buffer):
request.headers =parse_headers(_buffer)
ifnothas_body(request.headers):
request.finished =True
remove_intro(_buffer)
ifnotrequest.finished andcan_parse_body(request.headers,_buffer):
request.body_raw,request.body =parse_body(request.headers,_buffer)
clear_buffer(_buffer)
request.finished =True
return_buffer
从上面的代码中可以看到,我把解析的过程分为三个部分:解析请求行(这行像这样:GET /resource HTTP/1.1),解析请求头以及解析请求体。
请求行包含了 HTTP 请求方法以及 URL 地址。而 URL 地址则包含了更多的信息:路径、url 参数和开发者自定义的 url 参数。解析请求方法和 URL 还是很容易的 - 合适地分割字符串就好了。函数urlparse.parse可以用来解析 URL 参数。开发者自定义的 URL 参数可以通过正则表达式来解析。
接下来是 HTTP 头部。它们是一行行由键值对组成的简单文本。问题在于,可能有多个 HTTP 头有相同的名字,却有不同的值。一个值得关注的 HTTP 头部是Content-Length,它描述了请求体的字节长度(不是整个请求,仅仅是请求体)。这对于决定是否解析请求体有很重要的作用。
最后,解析器根据 HTTP 方法和头部来决定是否解析请求体。
在某种意义上,路由就像是连接框架和用户的桥梁,用户用合适的方法创建Router对象并为其设置路径/函数对,然后将它赋值给 App 对象。而 App 对象依次调用get_handler函数生成相应的回调函数。简单来说,路由就负责两件事,一是存储路径/函数对,二是返回需要的路径/函数对
Router类中有两个允许最终开发者添加路由的方法,分别是add_routes和add_route。因为add_routes就是add_route函数的一层封装,我们将主要讲解add_route函数:
defadd_route(self,path,handler):
compiled_route =self.class.build_route_regexp(path)
ifcompiled_route notinself.routes:
self.routes[compiled_route]=handler
raiseDuplicateRoute
首先,这个函数使用Router.build_router_regexp的类方法,将一条路由规则(如 '/cars/{id}' 这样的字符串),“编译”到一个已编译的正则表达式对象。这些已编译的正则表达式用来匹配请求路径,以及解析开发者自定义的 URL 参数。如果已经存在一个相同的路由,程序就会抛出一个异常。最后,这个路由/处理程序对被添加到一个简单的字典self.routes中。
下面展示 Router 是如何“编译”路由的:
@classmethod
defbuild_route_regexp(cls,regexp_str):
Turns a string into a compiled regular expression. Parses '{}' into
named groups ie. '/path/{variable}' is turned into
'/path/(?P&variable&[a-zA-Z0-9_-]+)'.
:param regexp_str: a string representing a URL path.
:return: a compiled regular expression.
defnamed_groups(matchobj):
return'(?P&{0}&[a-zA-Z0-9_-]+)'.format(matchobj.group(1))
re_str =re.sub(r'{([a-zA-Z0-9_-]+)}',named_groups,regexp_str)
re_str =''.join(('^',re_str,'$',))
returnre.compile(re_str)
这个方法使用正则表达式将所有出现的{variable}替换为(?P&variable&)。然后在字符串头尾分别添加^和$标记,最后编译正则表达式对象。
完成了路由存储仅成功了一半,下面是如何得到路由对应的函数:
defget_handler(self,path):
logger.debug('Getting handler for: {0}'.format(path))
forroute,handler inself.routes.items():
path_params =self.class.match_path(route,path)
ifpath_params isnotNone:
logger.debug('Got handler for: {0}'.format(path))
wrapped_handler =HandlerWrapper(handler,path_params)
returnwrapped_handler
raiseNotFoundException()
一旦App对象获得一个Request对象,也就获得了 URL 的路径部分(如 /users/15/edit)。然后,我们需要匹配函数来生成一个响应或者 404 错误。get_handler函数将路径作为参数,循环遍历路由,对每条路由调用Router.match_path类方法检查是否有已编译的正则对象与这个请求路径匹配。如果存在,我们就调用HandleWrapper来包装路由对应的函数。path_params字典包含了路径变量(如 '/users/15/edit' 中的 '15'),若路由没有指定变量,字典就为空。最后,我们将包装好的函数返回给App对象。
如果遍历了所有的路由都找不到与路径匹配的,函数就会抛出NotFoundException异常。
这个Route.match类方法挺简单:
defmatch_path(cls,route,path):
match =route.match(path)
returnmatch.groupdict()
exceptAttributeError:
returnNone
它使用正则对象的 [14]来检查路由是否与路径匹配。若果不匹配,则返回 None 。
最后,我们有HandleWraapper类。它的唯一任务就是封装一个异步函数,存储path_params字典,并通过handle方法对外提供一个统一的接口。
classHandlerWrapper(object):
definit(self,handler,path_params):
self.handler =handler
self.path_params =path_params
self.request =None
async defhandle(self,request):
returnawait self.handler(request,**self.path_params)
组合到一起
框架的最后部分就是用App类把所有的部分联系起来。
App类用于集中所有的配置细节。一个App对象通过其start_server方法,使用一些配置数据创建一个HTTPServer的实例,然后将它传递给 [15]。asyncio.start_server函数会对每一个 TCP 连接调用HTTPServer对象的handle_connection方法。
defstart_server(self):
ifnotself._server:
self.loop =asyncio.get_event_loop()
self._server =HTTPServer(self.router,self.http_parser,self.loop)
self._connection_handler =asyncio.start_server(
self._server.handle_connection,
host=self.host,
port=self.port,
reuse_address=True,
reuse_port=True,
loop=self.loop)
logger.info('Starting server on {0}:{1}'.format(
self.host,self.port))
self.loop.run_until_complete(self._connection_handler)
self.loop.run_forever()
exceptKeyboardInterrupt:
logger.info('Got signal, killing server')
exceptDiyFrameworkExceptionase:
logger.error('Critical framework failure:')
logger.error(e.traceback)
self.loop.close()
logger.info('Server already started - {0}'.format(self))
如果你查看源码,就会发现所有的代码仅 320 余行(包括测试代码的话共 540 余行)。这么少的代码实现了这么多的功能,让我有点惊讶。这个框架没有提供模板、身份认证以及数据库访问等功能(这些内容也很有趣哦)。这也让我知道,像 Django 和 Tornado 这样的框架是如何工作的,而且我能够快速地调试它们了。
这也是我按照测试驱动开发完成的第一个项目,整个过程有趣而有意义。先编写测试用例迫使我思考设计和架构,而不仅仅是把代码放到一起,让它们可以运行。不要误解我的意思,有很多时候,后者的方式更好。不过如果你想给确保这些不怎么维护的代码在之后的几周甚至几个月依然工作,那么测试驱动开发正是你需要的。
我研究了下[16]以及依赖注入模式,这些充分体现在Router类是如何作为一个更高层次的抽象的(实体?)。Router类是比较接近核心的,像http_parser和App的内容比较边缘化,因为它们只是完成了极小的字符串和字节流、或是中层 IO 的工作。测试驱动开发(TDD)迫使我独立思考每个小部分,这使我问自己这样的问题:方法调用的组合是否易于理解?类名是否准确地反映了我正在解决的问题?我的代码中是否很容易区分出不同的抽象层?
来吧,写个小框架,真的很有趣:)
作者:[17]译者:[18]校对:[19]
本文由 [20]原创编译,[21]荣誉推出
(题图来自:[22])
[1]: https://github.com/sirMackk/diy_framework
[2]: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
[3]: https://en.wikipedia.org/wiki/HTTP_persistent_connection
[4]: https://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm#Slow_start
[5]: https://docs.python.org/3/library/asyncio-stream.html
[6]: https://docs.python.org/3/library/asyncio-protocol.html
[7]: https://github.com/sirMackk/diy_framework/blob/ec7cd80abe88f51adb79/diy_framework/http_server.py#L46
[8]: https://docs.python.org/3/library/functions.html#bytearray
[9]: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Handle
[10]: https://en.wikipedia.org/wiki/Dependency_injection
[11]: https://docs.python.org/3/library/unittest.mock.html
[12]: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.BaseEventLoop.call_later
[13]: https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server
[14]: https://docs.python.org/3/library/re.html#re.match
[15]: https://docs.python.org/3/library/asyncio-stream.html?highlight=start_server#asyncio.start_server
[16]: https://blog.8thlight.com/uncle-bob//the-clean-architecture.html
[17]: http://mattscodecave.com/hire-me.html
[18]: https://github.com/Cathon
[19]: https://github.com/wxy
[20]: https://github.com/LCTT/TranslateProject
[21]: https://linux.cn/
点击标题或输入文章 ID 直达该文章
将文章分享给朋友是对我们最好的赞赏!
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
今日搜狐热点

我要回帖

更多关于 python自动化测试框架 的文章

 

随机推荐