今又在丰富mock http接口了,由于我的开发速度太快,同事们的开发速度太慢,哈哈别打我, 他们在一定程度上拖慢了哥的大腿. 但又因为哥的接口要大量耦合调用他们的rest api,又为了kpi稳定的推进. 哥直接用tornado自己写一套完整的mock server…
文章写的不是很严谨,欢迎来喷,另外该文后续有更新的,请到原文地址查看更新。
这个是其中遇到的问题,以前都是用msgpack来序列化数据,但因为很多时候不好调试数据,你用json序列化数据还是能看懂的. 下面是我序列化headers头部信息报错.
#blog: xiaorui.cc
In [92]: import requests
In [93]: r = requests.get('http://www.xiaorui.cc')
In [94]: import json
In [95]: json.dumps(r.headers)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-95-435fbd17ffc1> in <module>()
----> 1 json.dumps(r.headers)
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.pyc in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, encoding, default, sort_keys, **kw)
241 cls is None and indent is None and separators is None and
242 encoding == 'utf-8' and default is None and not sort_keys and not kw):
--> 243 return _default_encoder.encode(obj)
244 if cls is None:
245 cls = JSONEncoder
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in encode(self, o)
205 # exceptions aren't as detailed. The list call should be roughly
206 # equivalent to the PySequence_Fast that ''.join() would do.
--> 207 chunks = self.iterencode(o, _one_shot=True)
208 if not isinstance(chunks, (list, tuple)):
209 chunks = list(chunks)
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in iterencode(self, o, _one_shot)
268 self.key_separator, self.item_separator, self.sort_keys,
269 self.skipkeys, _one_shot)
--> 270 return _iterencode(o, 0)
271
272 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in default(self, o)
182
183 """
--> 184 raise TypeError(repr(o) + " is not JSON serializable")
185
186 def encode(self, o):
TypeError: {'x-powered-by': 'PHP/5.3.3', 'transfer-encoding': 'chunked', 'content-encoding': 'gzip', 'server': 'nginx/1.8.0', 'connection': 'keep-alive', 'date': 'Mon, 25 Jan 2016 09:56:54 GMT', 'content-type': 'text/html; charset=UTF-8', 'x-pingback': 'http://xiaorui.cc/xmlrpc.php'} is not JSON serializabled
raise TypeError(repr(o) + ” is not JSON serializable”) ,还真没看到过着问题. 一顿狂搜出来的结果跟我遇到的问题都不一样,最后通过看requests structures类解决的.
我们看看headers的type,是CaseInsensitiveDict类.
In [99]: type(r.headers) Out[99]: requests.structures.CaseInsensitiveDict
这是解决方法.
#blog: xiaorui.cc
In [96]: json.dumps(dict(r.headers))
Out[96]: '{"x-powered-by": "PHP/5.3.3", "transfer-encoding": "chunked", "content-encoding": "gzip", "server": "nginx/1.8.0", "connection": "keep-alive", "date": "Mon, 25 Jan 2016 09:56:54 GMT", "content-type": "text/html; charset=UTF-8", "x-pingback": "http://xiaorui.cc/xmlrpc.php"}'
In [97]: json.loads(json.dumps(dict(r.headers)))
Out[97]:
{u'connection': u'keep-alive',
u'content-encoding': u'gzip',
u'content-type': u'text/html; charset=UTF-8',
u'date': u'Mon, 25 Jan 2016 09:56:54 GMT',
u'server': u'nginx/1.8.0',
u'transfer-encoding': u'chunked',
u'x-pingback': u'http://xiaorui.cc/xmlrpc.php',
u'x-powered-by': u'PHP/5.3.3'}
我们为什么要调用dict函数呢? 因为CaseInsensitiveDict类不能直接使用,他跟原装的dict字典微微有些差异的.
#blog: xiaorui.cc
In [100]: r.headers.items()
Out[100]:
[('content-encoding', 'gzip'),
('transfer-encoding', 'chunked'),
('x-powered-by', 'PHP/5.3.3'),
('server', 'nginx/1.8.0'),
('connection', 'keep-alive'),
('date', 'Mon, 25 Jan 2016 09:56:54 GMT'),
('content-type', 'text/html; charset=UTF-8'),
('x-pingback', 'http://xiaorui.cc/xmlrpc.php')]
In [101]: dict(r.headers.items())
Out[101]:
{'connection': 'keep-alive',
'content-encoding': 'gzip',
'content-type': 'text/html; charset=UTF-8',
'date': 'Mon, 25 Jan 2016 09:56:54 GMT',
'server': 'nginx/1.8.0',
'transfer-encoding': 'chunked',
'x-pingback': 'http://xiaorui.cc/xmlrpc.php',
'x-powered-by': 'PHP/5.3.3'}
既然搜不出解决方法,那就直接看下requests的源码,看看为什么会有出现那问题…
vim /Library/Python/2.7/site-packages/requests/structures.py
#blog: xiaorui.cc
继承了collections父类.
class CaseInsensitiveDict(collections.MutableMapping):
""" CaseInsensitiveDict里用了各种的魔法函数,他的目的是为了抽象自带的dict字典."""
def __init__(self, data=None, **kwargs):
self._store = dict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
def __getitem__(self, key):
return self._store[key.lower()][1]
def __delitem__(self, key):
del self._store[key.lower()]
def __iter__(self):
return (casedkey for casedkey, mappedvalue in self._store.
def __len__(self):
return len(self._store)
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
return (
(lowerkey, keyval[1])
for (lowerkey, keyval)
in self._store.items()
)
def __eq__(self, other):
if isinstance(other, collections.Mapping):
other = CaseInsensitiveDict(other)
else:
return NotImplemented
return dict(self.lower_items()) == dict(other.lower_items())
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
#repr是返回一个可以用来表示对象的可打印字符串,简单说,str()出来的值是给人看的 ... repr()出来的值是给python看的,可以通过eval()重新变回一个Python对象...
def __repr__(self):
return str(dict(self.items()))
通过看了requests的相关源码,我们还可以这么理解,所谓是一通百通 … repr跟str的区别就在于此.
In [111]: str(r.headers)
Out[111]: “{‘x-powered-by’: ‘PHP/5.3.3’, ‘transfer-encoding’: ‘chunked’, ‘content-encoding’: ‘gzip’, ‘server’: ‘nginx/1.8.0’, ‘connection’: ‘keep-alive’, ‘date’: ‘Mon, 25 Jan 2016 09:56:54 GMT’, ‘content-type’: ‘text/html; charset=UTF-8’, ‘x-pingback’: ‘http://xiaorui.cc/xmlrpc.php’}”
In [112]: eval(str(r.headers))
Out[112]:
{‘connection’: ‘keep-alive’,
‘content-encoding’: ‘gzip’,
‘content-type’: ‘text/html; charset=UTF-8’,
‘date’: ‘Mon, 25 Jan 2016 09:56:54 GMT’,
‘server’: ‘nginx/1.8.0’,
‘transfer-encoding’: ‘chunked’,
‘x-pingback’: ‘http://xiaorui.cc/xmlrpc.php’,
‘x-powered-by’: ‘PHP/5.3.3’}
遇到这个python requests不是坑的坑. 现在也不是很理解为什么要那么设计. 就这样吧,本来不大的问题,扯这么多… 见谅…
