Requests源码阅读v0.8.0
工作两年了,一直用 python 写一些 API 之类的东西,自动化框架也有涉及,却一直感觉对个人技能提升缓慢。决定开这个坑,是之前看到@wangshunping的read requests,生动有趣,可惜 0.8.0 之后没有更新了。待我稍稍有了一点看源码的动力,就想接着下去写。真是漫漫长路啊,4409 个 commit,1000 多个 PR,更何况还有珠玉在前,实在没有把握能把这块硬骨头给啃下来,写一点是一点吧。作为 python 的小学生,一些错误在所难免,希望大家指出,互相讨论。 下面就开始吧! <!-- more --> 目标 0.8.0 (2011-11-13) ++++++++++++++++++ * Keep-alive support! * Complete removal of Urllib2 * Complete removal of Poster * Complete removal of CookieJars * New ConnectionError raising * Safe_mode for error catching * prefetch parameter for request methods * OPTION method * Async pool size throttling * File uploads send real names 源码阅读 v0.7.1 0.7.1 (2011-10-23) ++++++++++++++++++ * Move away from urllib2 authentication handling. * Fully Remove AuthManager, AuthObject, &c. * New tuple-based auth system with handler callbacks. 移除urllib2的 authentication 处理 完全移除AuthManager, AuthObject和。。。&c? 新的元组形式的auth机制和处理器回调函数。 1. 移除urllib2的 authentication 处理 添加一个auth.py文件,加入了自己实现的 auth 处理器,包含http_basic和http_digest,分别对应 Headers 中Autohorization以Basic和Digest开头的情形。 2. 完全删除AuthManager, AuthObject和。。。&c? 由于接口改用了 session,于是就没有必要使用AuthManager储存认证信息。使用自己实现的处理器,完全删除models.py中相关的代码。 3. 新的元组形式的auth机制和处理器回调函数。 现在: self.auth = auth_dispatch(auth) if self.auth: auth_func, auth_args = self.auth r = auth_func(self, *auth_args) self.__dict__.update(r.__dict__) def dispatch(t): """Given an auth tuple, return an expanded version.""" if not t: return t else: t = list(t) # Make sure they're passing in something. assert len(t) >= 2 # If only two items are passed in, assume HTTPBasic. if (len(t) == 2): t.insert(0, 'basic') # Allow built-in string referenced auths. if isinstance(t[0], basestring): if t[0] in ('basic', 'forced_basic'): t[0] = http_basic elif t[0] in ('digest',): t[0] = http_digest # Return a custom callable. return (t[0], tuple(t[1:])) 通过dispatch函数,若传入二元元组,则默认前面加上'basic',使用http_basic处理,否则需要指定处理类型。支持自定义处理器: def pizza_auth(r, username): """Attaches HTTP Pizza Authentication to the given Request object. """ r.headers['X-Pizza'] = username return r Then, we can make a request using our Pizza Auth:: >>> requests.get('http://pizzabin.org/admin', auth=(pizza_auth, 'kenneth')) <Response [200]> v0.7.2 0.7.2 (2011-10-23) ++++++++++++++++++ * PATCH Fix. 修正 BUG(略) v0.7.3 0.7.3 (2011-10-23) ++++++++++++++++++ * Digest Auth fix. 修正 Digest Auth 的 BUG 主要是删除了一些 debug 的 print 语句,估计当时作者脑子也不清醒了,我还注意到他改了一个文件头的"~"的长度,是有够无聊的!0.7.1 到 0.7.3 都在一个多小时内完成,小伙子动力很足啊! v0.7.4 0.7.4 (2011-10-26) ++++++++++++++++++ * Sesion Hooks fix. 主要是一些代码的美化和小 BUG,给session加了一个keep_alive参数,暂时还没用上,应该是为以后做准备。 v0.7.5 0.7.5 (2001-11-04) ++++++++++++++++++ * Response.content = None if there was an invalid repsonse. * Redirection auth handling. 咦?日期穿越了 10 年?哈哈,什么时候会改呢? 如果是无效响应则content = None 重定向认证处理 1. 无效响应content = None 加入一个 Error Handling: try: self._content = self.raw.read() except AttributeError: return None 2. 重定向认证处理 一个 BUG,原来是用 dispatch 后的 auth 构造新的 Request 会导致错误,现在使用self._auth保存原始 auth 并传入新的 Request 对象。 v0.7.6 0.7.6 (2011-11-07) ++++++++++++++++++ * Digest authentication bugfix (attach query data to path) Digest 认证的 BUG 修复(在路径后附上 query) 原来: path = urlparse(r.request.url).path 现在: p_parsed = urlparse(r.request.url) path = p_parsed.path + p_parsed.query 我注意到日期问题已经修复了: Updated your 2001, to 2011... unless you went back in time ;) 这个幽默。 v0.8.0 0.8.0 (2011-11-13) ++++++++++++++++++ * Keep-alive support! * Complete removal of Urllib2 * Complete removal of Poster * Complete removal of CookieJars * New ConnectionError raising * Safe_mode for error catching * prefetch parameter for request methods * OPTION method * Async pool size throttling * File uploads send real names 支持keep_alive参数(填坑来了) 完全抛弃urllib2 完全抛弃Poster 完全抛弃CookieJars 新的ConnectionError抛出 安全的处理异常机制。 为请求方法加入prefetch参数 新的OPTION方法 节省 Async 池的大小 上传文件发送真实文件名 1. 支持keep_alive参数 作者在 v0.8.0 全面转向urllib3,这是个第三方的轮子,它相对于urllib2最大的改进是可以重用 HTTP 连接,不用每个 request 都新建一个连接了。这样大大加快了大量 request 时的响应速度。 self.poolmanager = PoolManager( num_pools=self.config.get('pool_connections'), maxsize=self.config.get('pool_maxsize') ) proxy = self.proxies.get(_p.scheme) if proxy: conn = poolmanager.proxy_from_url(url) else: # Check to see if keep_alive is allowed. if self.config.get('keep_alive'): conn = self._poolmanager.connection_from_url(url) else: conn = connectionpool.connection_from_url(url) keep_alive是默认打开的,在urllib3中维护了一个连接池,当对某个 url 进行请求时,会从连接池中取出该连接,然后发送请求时直接调用此连接的子方法。 2. 完全抛弃urllib2 删除了models.py中用来发送请求的build_opener函数,使用urllib3的conn.urlopen方法。 3.完全抛弃Poster 同上,用一个轮子换了另一个轮子。。 4. 完全抛弃CookieJars 上测试 def test_session_persistent_cookies(self): s = requests.session() # Internally dispatched cookies are sent. _c = {'kenneth': 'reitz', 'bessie': 'monke'} r = s.get(httpbin('cookies'), cookies=_c) r = s.get(httpbin('cookies')) # Those cookies persist transparently. c = json.loads(r.content).get('cookies') assert c == _c # Double check. r = s.get(httpbin('cookies'), cookies={}) c = json.loads(r.content).get('cookies') assert c == _c # Remove a cookie by setting it's value to None. r = s.get(httpbin('c
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。
登录后,加入这场讨论。
登录参与