requests源码
之前看到过一篇关于requests代码分析的文章,它是从最早版本开始的。论文笔,不及对方十之一二,论思路,十分佩服。要知道,我口口声声说看代码都是从最新代码看,收获有限。但是从初始版本看起,对自己大有裨益。作者的思路、代码的组织、技术的运用都是很值得去体会和学习的。
0.2.0
先看起头的这个类
class _Request(urllib2.Request):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
setting of HTTP methods.
"""
def __init__(self, url,
data=None, headers={}, origin_req_host=None,
unverifiable=False, method=None):
urllib2.Request.__init__( self, url, data, headers, origin_req_host,
unverifiable)
self.method = method
def get_method(self):
if self.method:
return self.method
return urllib2.Request.get_method(self)
可以看出,_Request是urllib2.Request的子类,这里重写了父类的get_method方法。如果有method,返回;如果没有,返回父类的get_method
在Request的send方法中可以看到其调用
GET:
if isinstance(self.params, dict):
params = urllib.urlencode(self.params)
else:
params = self.params
req = _Request(("%s?%s" % (self.url, params)), method=self.method)
PUT:
req = _Request(self.url, method='PUT')
POST:
req = _Request(self.url, method='POST')
# 如果设置了headers,就会把headers赋值给req.headers
if self.headers:
req.headers = self.headers
# self._get_operner对认证做了判断,如果需要认证,则会执行以下代码
# authr = urllib2.HTTPPasswordMgrWithDefaultRealm()
#
# authr.add_password(None, self.url, self.auth.username, self.auth.password)
# handler = urllib2.HTTPBasicAuthHandler(authr)
# opener = urllib2.build_opener(handler)
# return opener.open
# 实质上还是在用urllib2的open方法
opener = self._get_opener()
try:
# 将req对象传给open方法,发出请求
resp = opener(req)
# 将结果信息赋值给response对象
self.response.status_code = resp.code
self.response.headers = resp.info().dict
if self.method.lower() == 'get':
self.response.content = resp.read()
success = True
except urllib2.HTTPError, why:
# 接收异常,why?why?why?,我充其量会写except urllib2.HTTPError, e。
self.response.status_code = why.code
Response、AuthObject都是类,只能这么介绍了,因为太简单了,没什么可说的
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
:class:`Request.response <response>` attribute, which is an instance of
this class.
"""
def __init__(self):
self.content = None
self.status_code = None
self.headers = dict()
def __repr__(self):
try:
repr = '<Response [%s]>' % (self.status_code)
except:
repr = '<Response object>'
return repr
class AuthObject(object):
"""The :class:`AuthObject` is a simple HTTP Authentication token. When
given to a Requests function, it enables Basic HTTP Authentication for that
Request. You can also enable Authorization for domain realms with AutoAuth.
See AutoAuth for more details.s
:param username: Username to authenticate with.
:param password: Password for given username.
"""
def __init__(self, username, password):
self.username = username
self.password = password
关于认证的函数
def add_autoauth(url, authobject):
global AUTOAUTHS
AUTOAUTHS.append((url, authobject))
def _detect_auth(url, auth):
return _get_autoauth(url) if not auth else auth
def _get_autoauth(url):
for (autoauth_url, auth) in AUTOAUTHS:
if autoauth_url in url:
return auth
return None
一开始实在是没领会到add_autoauth的深意,在本版本中没有其他地方对其进行调用,后来发现如下注释:
>>> c_auth = requests.AuthObject('kennethreitz', 'xxxxxxx')
>>> requests.add_autoauth('https://convore.com/api/', c_auth)
>>> r = requests.get('https://convore.com/api/account/verify.json')
截取测试get的代码如下:
def get(url, params={}, headers={}, auth=None):
r = Request()
r.method = 'GET'
r.url = url
r.params = params
r.headers = headers
r.auth = _detect_auth(url, auth)
r.send()
return r.response
分析下在使用认证时的过程
实例化AuthObject对象成c_auth,并传入add_autoauth函数。这时全局变量AUTOAUTHS=[('https://convore.com/api/', c_auth)]。
在调用_detect_aut时,会比对当前的url是否在AUTOAUTHS中,如果在,就返回对应的auth。这里我感觉AUTOAUTHS是缓存了请求的url和认证信息。get请求时,如果auth函数为None,而且请求的url在AUTOAUTHS中,则会使用AUTOAUTHS的auth信息。