python openstack openstack-swift

Problemas al trabajar con generadores de Python y OpenStack Swift Client.



openstack-swift (1)

En su método get_object , está asignando el valor de retorno de _object_body() a la variable de contents . Sin embargo, esa variable también es la que contiene sus datos reales, y se usa desde el principio en _object_body .

El problema es que _object_body es una función generadora (usa yield ). Por lo tanto, cuando lo llama, produce un objeto generador, pero el código de la función no comienza a ejecutarse hasta que usted itera sobre ese generador . Lo que significa que cuando el código de la función realmente comienza a ejecutarse (el bucle for en _test_stream ), pasa mucho tiempo después de que hayas reasignado el contents = _object_body() .

Su stream = StringIO(contents) por lo tanto, crea un objeto StringIO que contiene el objeto generador (de ahí su mensaje de error), no los datos.

Aquí hay un caso de reproducción mínima que ilustra el problema:

def foo(): contents = "Hello!" def bar(): print contents yield 1 # Only create the generator. This line runs none of the code in bar. contents = bar() print "About to start running..." for i in contents: # Now we run the code in bar, but contents is now bound to # the generator object. So this doesn''t print "Hello!" pass

Estoy teniendo un problema con los generadores de Python al trabajar con la biblioteca del cliente de Openstack Swift.

El problema en cuestión es que estoy tratando de recuperar una gran cadena de datos de una URL específica (aproximadamente 7 MB), dividir la cadena en bits más pequeños y enviar una clase de generador de vuelta, con cada iteración conteniendo un fragmento fragmentado de la cadena. en el conjunto de pruebas, esto es solo una cadena que se envía a una clase de cliente swift para el procesamiento.

El código en la clase de Monkeypatched se ve así:

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

Y en la suite de pruebas:

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

Después de devolver el objeto generador, fue llamado por una función de flujo en la clase de almacenamiento:

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))

Y finalmente, en mi suite de pruebas:

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))

La salida termina con esto:

====================================================================== 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>

Intenté aislar esto con un simple script de Python que sigue el mismo flujo que el código anterior, que pasó sin problemas:

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()

Cualquier ayuda con este problema es muy apreciada :)