【发布时间】:2013-12-24 03:54:20
【问题描述】:
我在使用 Openstack Swift 客户端库时遇到了 Python 生成器问题。
手头的问题是我正在尝试从特定的 url(大约 7MB)检索一大串数据,将字符串分块成更小的位,然后发回一个生成器类,每次迭代都保存一个分块位字符串。在测试套件中,这只是一个字符串,它被发送到 swift 客户端的一个猴子补丁类进行处理。
monkeypatched 类中的代码如下所示:
def monkeypatch_class(name, bases, namespace):
'''Guido's monkeypatch metaclass.'''
assert len(bases) == 1, "Exactly one base class required"
base = bases[0]
for name, value in namespace.iteritems():
if name != "__metaclass__":
setattr(base, name, value)
return base
在测试套件中:
from swiftclient import client
import StringIO
import utils
class Connection(client.Connection):
__metaclass__ = monkeypatch_class
def get_object(self, path, obj, resp_chunk_size=None, ...):
contents = None
headers = {}
# retrieve content from path and store it in 'contents'
...
if resp_chunk_size is not None:
# stream the string into chunks
def _object_body():
stream = StringIO.StringIO(contents)
buf = stream.read(resp_chunk_size)
while buf:
yield buf
buf = stream.read(resp_chunk_size)
contents = _object_body()
return headers, contents
生成器对象返回后,被存储类中的流函数调用:
class SwiftStorage(Storage):
def get_content(self, path, chunk_size=None):
path = self._init_path(path)
try:
_, obj = self._connection.get_object(
self._container,
path,
resp_chunk_size=chunk_size)
return obj
except Exception:
raise IOError("Could not get content: {}".format(path))
def stream_read(self, path):
try:
return self.get_content(path, chunk_size=self.buffer_size)
except Exception:
raise OSError(
"Could not read content from stream: {}".format(path))
最后,在我的测试套件中:
def test_stream(self):
filename = self.gen_random_string()
# test 7MB
content = self.gen_random_string(7 * 1024 * 1024)
self._storage.stream_write(filename, io)
io.close()
# test read / write
data = ''
for buf in self._storage.stream_read(filename):
data += buf
self.assertEqual(content,
data,
"stream read failed. output: {}".format(data))
输出结果如下:
======================================================================
FAIL: test_stream (test_swift_storage.TestSwiftStorage)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/bacongobbler/git/github.com/bacongobbler/docker-registry/test/test_local_storage.py", line 46, in test_stream
"stream read failed. output: {}".format(data))
AssertionError: stream read failed. output: <generator object _object_body at 0x2a6bd20>
我尝试使用一个简单的 python 脚本来隔离它,该脚本遵循与上面代码相同的流程,没有问题通过:
def gen_num():
def _object_body():
for i in range(10000000):
yield i
return _object_body()
def get_num():
return gen_num()
def stream_read():
return get_num()
def main():
num = 0
for i in stream_read():
num += i
print num
if __name__ == '__main__':
main()
非常感谢您对此问题的任何帮助:)
【问题讨论】:
-
你应该在函数 gen_num 中返回 _object_body 吗?没有括号?
-
有些奇怪。是
test_stream中的最后一行抛出错误消息吗?如果是这样,似乎data是<generator object _object_body at 0x2a6bd20>,但我不明白这是怎么回事。它是什么时候从字符串改变的? -
在您的代码中添加一些打印,以确保您正在执行您认为正在执行的内容。正如@DSM 所说,
data在 您在此处显示 的代码中以字符串的形式开始生活,而data += buf无法改变它 -data不可能神奇地变成生成器对象代码你展示了。因此我得出结论 ;-) 你实际上并没有执行你展示的代码。 -
@DSM,是的,这是
test_stream中的最后一行抛出此错误消息。 -
将
path视为 S3 存储桶中文件的路径,而contents是该文件中包含的数据。我认为问题可能必须处理找到不返回任何内容的路径的代码,然后contents只会将 None 返回到stream_read。但是,我仍然不明白为什么在这种情况下会返回生成器类。也许是一个空文件?
标签: python openstack openstack-swift