IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

urllib2源码解读三(探索OpenerDirector的add_handler)

the5fire的技术博客 2012-12-06 13:53:52 累计浏览 2,363 次
本机暂存

OpenerDirector是怎么把这些handler分类的

上篇文章说到,在build_opener中只是调用了OpenerDirector的add_handler方法,并不是直接操作的属性来完成handler的添加的。那么来看看OpenerDirector.add_handler具体做了些什么工作。

这个函数的作用其实很简单,就是传进来的Handler进行分类。既然是分类那就要有分类的依据了,那么分类的依据是什么呢?

看一下这四个属性就知道了:

       self.handle_open = {}
       self.handle_error = {}
       self.process_response = {}
       self.process_request = {}

它要把你传递来的Handler分别放到这四个字典中,显然对应的Handler就应该会包含open或者error或者resonse或者request的属性或者方法,说到这里来看下代码就了解了:

def add_handler(self, handler):
       '''
           根据协议添加handler到不同的字典中,
           所有的handler都会存放到handlers这个list中。
       '''

       if not hasattr(handler, "add_parent"):
           raise TypeError("expected BaseHandler instance, got %r" %
                           type(handler))

       added = False
       for meth in dir(handler):
           #过滤掉命名恰好和下面判断规则一致的函数。
           # 但这几个其实并不是需要的函数。
           if meth in ["redirect_request", "do_open", "proxy_open"]:
               # oops, coincidental match
               continue

           i = meth.find("_")
           protocol = meth[:i]
           condition = meth[i+1:]

           if condition.startswith("error"):
               j = condition.find("_") + i + 1
               kind = meth[j+1:]
               try:
                   kind = int(kind)
               except ValueError:
                   pass
               lookup = self.handle_error.get(protocol, {})
               self.handle_error[protocol] = lookup
           elif condition == "open":
               kind = protocol
               lookup = self.handle_open
           elif condition == "response":
               kind = protocol
               lookup = self.process_response
           elif condition == "request":
               kind = protocol
               lookup = self.process_request
           else:
               continue
           handlers = lookup.setdefault(kind, [])
           if handlers:
               bisect.insort(handlers, handler)
           else:
               handlers.append(handler)
           added = True
       if added:
           #bisect.insort(self.handlers, handler)
           handler.add_parent(self)

从这个函数我们可以看到关于分类的具体处理过程,其实就是通过遍历这个handler对象的所有方法,然后根据其中是否存在某指定方法来进行分类的。比如HttpHanlder中有http_open这个方法,那么就会被放到handle_open中。至于代码中其他的操作都是对一些基本属性和对那些会产生冲突的方法的过滤。

最后存入字典的key也需要注意一下,这个key就是对应的协议,而此事的值并不是单独的handler对象,而是一个列表,这说明,如果有两个Handler(比如AHandler和BHandler)中含有同样的http_open方法,如果是这样的话,在后面要处理对应的http open请求的话就需要通过这两个handler依次处理。

最后还有一个被我注释掉的一句代码,这个其实urllib2作者也有写注释,handlers这个列表只是为了保持兼容。
每个handler类都继承同一个BaseHandler,拥有add_parent方法,这个方面的作用是为了在handler中同OpenerDirector进行通信。

这个函数的功能也就这些了,不过从这里我看到了另外的东西,就是:约定。

所有的这些都是基于一个约定,约定handler中的关键函数一定要是网络协议加上对应的方法,约定每个handler必须有一个add_parent方法,以及其他的一些约定。

了解这些约定的目的一个是方便理解urllib2在处理url的过程,另外一个就是方便自己以后编写一些扩展handler。

不知道说得是不是够清晰,我自己觉得还不是很清晰,或许在写到最后一篇的时候才会真正清晰。

这一篇和上一篇把urlopen中的构建opener对象的过程都学习了一下,下一篇就来学习这两篇构建opener是怎么处理你给定的url的。

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. 用Hyer来进行网站的抓取 (累计阅读 158,250)
  2. 配置Nginx+uwsgi更方便地部署python应用 (累计阅读 107,164)
  3. 程序员技术练级攻略 (累计阅读 35,469)
  4. python实现自动登录discuz论坛 (累计阅读 32,833)
  5. python编程细节──遍历dict的两种方法比较 (累计阅读 20,371)
  6. 每个程序员都应该学习使用Python或Ruby (累计阅读 17,917)
  7. 浅析http协议、cookies和session机制、浏览器缓存 (累计阅读 17,446)
  8. Chrome和goagent的配置方法,你懂的 (累计阅读 16,842)
  9. 从输入 URL 到页面加载完成的过程中都发生了什么事情? (累计阅读 15,933)
  10. libcurl的使用总结(二) (累计阅读 15,083)