Discuss / Python / day5的课程真的有毒!!!

day5的课程真的有毒!!!

Topic source

屌炸天,太科幻了。跟着教程轻松愉快地攒出来一个web框架。 一看源码就停不下来,连续沉迷了两天,中了廖大的毒。 我现在没有用middleware和RequestHandler ,就用最朴素的方式来响应请求。 走通了一遍流程。简直不要太开心。

其中发现廖大代码里的一些小瑕疵 add_routes()

廖大的版本
def add_routes(app, module_name):
    n = module_name.rfind('.')
    if n == (-1):
        mod = __import__(module_name, globals(), locals())
    else:
        name = module_name[n+1:]
        mod = getattr(__import__(module_name[:n], globals(), locals(), [name]), name)
    for attr in dir(mod):
        if attr.startswith('_'):
            continue
        fn = getattr(mod, attr)
        if callable(fn):
        #这里用callable区分函数和普通成员变量是不合适地。
        #总是有一些奇怪的东西混进来,例如handler里边引入的User 用callable判断它总是True,然而扫描User纯粹是浪费电。
            method = getattr(fn, '__method__', None)
            path = getattr(fn, '__route__', None)
            if method and path:
            #这里method和path 判断完就丢了,有点可惜,add_route()里边还要再费二遍事,最好能修改add_route的签名,add_route(app,fn,method,path)一套传过去。
                add_route(app, fn)


我改过的
import types
def add_routes(app,module_name):
    n = module_name.rfind('.')
    if n == (-1):
        mod = __import__(module_name,globals(),locals())
    else:
        name = module_name[n+1:]
        mod = getattr(__import__(module_name[:n],globals(),locals(),[name]),name)

    for attr in dir(mod):
        if attr.startswith('_'):
            continue
        fn = getattr(mod , attr)
        if isinstance(fn,types.FunctionType):
            has_method = hasattr(fn,"__method__")
            has_path = hasattr(fn,"__route__")
            if has_method and has_path:
                add_route(app,fn)

就这些了,后边我再把middleware和RequestHandler 部分补上。

灰_手

#2 Created at ... [Delete] [Delete and Lock User]

middlewareRequestHandler是一定要实现的,不然耦合度太高很容易混乱。 相反,add_route可以省略,如果只加一个的话app.router.add_route就足以应付了。

这是我重构过的版本, __import__有一个黑魔法,不用写得这么复杂的:

mod = __import__(module_name, fromlist=[''])

加上了middlewareRequestHandler 后,走通了整个流程。

aiohttp的文档里说,middleware是一种自定义请求处理的强大机制。 貌似和JavaEE的过滤器,struts2的拦截器机制是差不多的意思。 都是责任链模式在项目中的实践。

async def logger_factory(app, handler): #这就是个装饰器呀
    async def logger(request):
        logging.info('Request: %s %s Begin>>>' % (request.method, request.path))
        # await asyncio.sleep(0.3)
        rst = (await handler(request))
        return rst
    return logger

async def response_factory(app, handler): #这个middleware比较不错,大家在handler里边可以返回各种类型的结果,在这里统一由廖大的response_factory给大家擦屁股。
    。。。

middleware的代码示意:
m1( m2( m3( doFoo())))

middleware调用流程:
req -> m1 -> m2 -> m3 -> doFoo()- 一路return 原路返回
<- m1 <- m2 <- m2 <- resq - <-

知道了__call__方法和__init__的区别,RequestHandler就是个普通类,没有什么特别的技巧。inspect模块涨姿势了,把函数查了一遍户口本,这就是自省吧。

通过这几天的学习,粗粒度的掌握了框架的结构,闹懂了请求到来及响应的过程。以后学习的其他的web框架的难度会大大降低。

廖大屌爆了,在下服了。

@灰_手 把项目都做完了呀。

__import__ 指定fromList参数这里涨姿势了

你的add_routes()很机智,廖大版的method 和 path 查到了不用实在是浪费。 RequestHandler看起来写得和廖大的结构上不一样,粗略的搂了两眼,两份我都没仔细看具体实现。

在这里我得黑一下廖大,廖大版的RequestHandler 貌似把handler函数的参数名写死了,handler的参数必须得叫request。我感觉这样对用户不太友好,这是对用户赤裸裸地强J。 最好能用判断类型的方法来代替判断参数名的方法,来保证传入了一个request类型的参数。这样用户写handler的时候就r,req,request 什么的随意写了。用户用得爽了,框架才有市场。

灰_手

#5 Created at ... [Delete] [Delete and Lock User]

@匿名sina网友V 我的已经是做完了,而且重构了不少地方,比如RequestHandler,重构的理由在这里

你黑的并不是地方,不是handler函数的参数写死了,而是app.router.add_route方法的第三个参数写死了,这里必需是一个只有一个参数request的函数,所以才用__call__统一封装成RequestHandler(handler)(request)的形式,这样使得handler的参数可以多样化。

然而RequestHandler是整个项目中最值得黑的地方,你可以看看我和别人在楼下的评论就会知道真正值得黑的地方在哪里了XD

前边给RequestHandler的评价过低了。 我说它是个普通类的前提是: app.router.add_route('GET', '/', handler) 想要的handler只不过就是一个可以接收Request 类型实例,可以返回Response实例的一个callable而已,世界上可以满足这个需求的callable就像我的腿毛那么多,而RequestHandler 的实例恰好也满足这一条件。这就是个巧合,人为制造的巧合。 RequestHandler本身还有其他的支持它存在的意义。

A request handler can be any callable that accepts a Request instance as its only argument and returns a StreamResponse derived (e.g. Response) instance:

def handler(request):
    return web.Response()

这个RequestHandler ,其实应该叫 RequestHandlerAdapter才对。

它是个适配器,它把用户自定义参数的各种奇形怪状handler适配成了标准的 handler(request)调用。

goodboy_xiaoq

#8 Created at ... [Delete] [Delete and Lock User]
add_routes最后直接用一个if更简洁
if isinstance(fn, types.FunctionType) and hasattr(fn, '__method__') and hasattr(fn, '__route__'):
    add_route(app, fn)

goodboy_xiaoq

#9 Created at ... [Delete] [Delete and Lock User]

我觉得add_route还是很有存在的必要,add_route方法里面还有有蛮多逻辑的,还需要将fn包装成协程,因为在RequestHandler中是await fn()

灰_手

#10 Created at ... [Delete] [Delete and Lock User]

看上去逻辑很多,但实际上是一种冗余

你没读过asyncio的源码才会这样说,func = asyncio.coroutine(func)这方法会内部处理func是协程或生成器,根本无需在外先判断func的类型,最终出来的就是标准的协程函数。

add_routes都用了method = getattr(func, '__method__', None)了,还要在add_route进行hasattr(fn, '__method__')判断是没有意义的,在add_routes中没有__method____route__属性时根本不会调用add_route

add_route这些判断是为单独调用时存在的,然而并没有单独调用的场景。有单独调用的场景我倒不如直接用pp.router.add_route了。


  • 1
  • 2

Reply