我使用Python的请求,在我的应用程序的一个方法库。 该方法的身体看起来是这样的:
def handle_remote_file(url, **kwargs):
response = requests.get(url, ...)
buff = StringIO.StringIO()
buff.write(response.content)
...
return True
我想不过来写一些单元测试这种方法,我想要做的是通过一个虚拟的本地URL,例如:
class RemoteTest(TestCase):
def setUp(self):
self.url = 'file:///tmp/dummy.txt'
def test_handle_remote_file(self):
self.assertTrue(handle_remote_file(self.url))
当我打电话requests.get与本地URL,我得到了下面的KeyError异常例外:
requests.get('file:///tmp/dummy.txt')
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/requests/packages/urllib3/poolmanager.pyc in connection_from_host(self, host, port, scheme)
76
77 # Make a fresh ConnectionPool of the desired type
78 pool_cls = pool_classes_by_scheme[scheme]
79 pool = pool_cls(host, port, **self.connection_pool_kw)
80
KeyError: 'file'
现在的问题是我怎么能传递一个本地的URL requests.get?
PS:我提出了上述示例。 它可能包含很多错误。
作为@WooParadog解释请求库不知道如何处理本地文件。 虽然,目前的版本允许定义传输适配器 。
因此,您可以简单地定义你自己的适配器,这将能够处理本地文件,如:
from requests_testadapter import Resp
class LocalFileAdapter(requests.adapters.HTTPAdapter):
def build_response_from_file(self, request):
file_path = request.url[7:]
with open(file_path, 'rb') as file:
buff = bytearray(os.path.getsize(file_path))
file.readinto(buff)
resp = Resp(buff)
r = self.build_response(request, resp)
return r
def send(self, request, stream=False, timeout=None,
verify=True, cert=None, proxies=None):
return self.build_response_from_file(request)
requests_session = requests.session()
requests_session.mount('file://', LocalFileAdapter())
requests_session.get('file://<some_local_path>')
我使用的要求,testadapter模块在上面的例子。
这里有一个传输适配器我写这比b1r3k的更多的其他功能,并具有超越自身的要求没有额外的依赖。 我没有测试它彻底地还,但我曾尝试似乎是无缺陷。
import requests
import os, sys
if sys.version_info.major < 3:
from urllib import url2pathname
else:
from urllib.request import url2pathname
class LocalFileAdapter(requests.adapters.BaseAdapter):
"""Protocol Adapter to allow Requests to GET file:// URLs
@todo: Properly handle non-empty hostname portions.
"""
@staticmethod
def _chkpath(method, path):
"""Return an HTTP status for the given filesystem path."""
if method.lower() in ('put', 'delete'):
return 501, "Not Implemented" # TODO
elif method.lower() not in ('get', 'head'):
return 405, "Method Not Allowed"
elif os.path.isdir(path):
return 400, "Path Not A File"
elif not os.path.isfile(path):
return 404, "File Not Found"
elif not os.access(path, os.R_OK):
return 403, "Access Denied"
else:
return 200, "OK"
def send(self, req, **kwargs): # pylint: disable=unused-argument
"""Return the file specified by the given request
@type req: C{PreparedRequest}
@todo: Should I bother filling `response.headers` and processing
If-Modified-Since and friends using `os.stat`?
"""
path = os.path.normcase(os.path.normpath(url2pathname(req.path_url)))
response = requests.Response()
response.status_code, response.reason = self._chkpath(req.method, path)
if response.status_code == 200 and req.method.lower() != 'head':
try:
response.raw = open(path, 'rb')
except (OSError, IOError) as err:
response.status_code = 500
response.reason = str(err)
if isinstance(req.url, bytes):
response.url = req.url.decode('utf-8')
else:
response.url = req.url
response.request = req
response.connection = self
return response
def close(self):
pass
(尽管名称,它完全报废之前,我想检查谷歌,所以它无关b1r3k的。)至于其他的答案,按照这个有:
requests_session = requests.session()
requests_session.mount('file://', LocalFileAdapter())
r = requests_session.get('file:///path/to/your/file')
包/ urllib3 / poolmanager.py几乎解释了它。 请求不支持本地URL。
pool_classes_by_scheme = {
'http': HTTPConnectionPool,
'https': HTTPSConnectionPool,
}
在最近的一个项目,我有同样的问题。 由于请求不支持“文件”计划,我会修补我们的代码在本地加载内容。 首先,我定义一个函数来替换requests.get
:
def local_get(self, url):
"Fetch a stream from local files."
p_url = six.moves.urllib.parse.urlparse(url)
if p_url.scheme != 'file':
raise ValueError("Expected file scheme")
filename = six.moves.urllib.request.url2pathname(p_url.path)
return open(filename, 'rb')
然后,在某个测试设置或装饰的测试功能,我用mock.patch
修补的请求get函数:
@mock.patch('requests.get', local_get)
def test_handle_remote_file(self):
...
该技术是有点脆-如果底层代码调用它并不能帮助requests.request
或者构造一个Session
,并调用。 有可能是在一个较低的水平,以支持修补请求的方式file:
网址,但在我的初步调查,似乎没有成为一个明显的挂钩点,所以我这个简单的方法去。