Repository: marshmallow-code/marshmallow

Issue:
2.x: Nested(many=True) eats first element from generator value when dumping
As reproduced in Python 3.6.8:

```py
from marshmallow import Schema, fields

class O(Schema):
    i = fields.Int()

class P(Schema):
    os = fields.Nested(O, many=True)

def gen():
    yield {'i': 1}
    yield {'i': 0}

p = P()
p.dump({'os': gen()})
# MarshalResult(data={'os': [{'i': 0}]}, errors={})
```

Problematic code is here:

https://github.com/marshmallow-code/marshmallow/blob/2.x-line/src/marshmallow/fields.py#L447

And here:

https://github.com/marshmallow-code/marshmallow/blob/2.x-line/src/marshmallow/schema.py#L832

The easiest solution would be to cast `nested_obj` to list before calling `schema._update_fields`, just like a normal Schema with `many=True` does.


Discussion:
I confirmed that this is no longer an issue in marshmallow 3. I was able to reproduce this with python 2 and 3 using the latest version of marshmallow 2.
`next(iter(...))` is not a safe operation for generators.

```py
def gen():
    yield 1
    yield 2

x = gen()
next(iter(x))
# 1
list(x)
# [2]
```

I suspect `list` would be an acceptable solution. If it was a performance concern we could use `itertools.tee` to copy the generator before peeking at the first item.
`next(iter(...))` is apparently fine because `obj` is guaranteed to be a list here:

https://github.com/marshmallow-code/marshmallow/blob/2.x-line/src/marshmallow/schema.py#L489

It's just that usage of `Schema._update_fileds` in `Nested` ignores the requirement.


Patch:
diff --git a/src/marshmallow/fields.py b/src/marshmallow/fields.py
--- a/src/marshmallow/fields.py
+++ b/src/marshmallow/fields.py
@@ -443,6 +443,8 @@ def _serialize(self, nested_obj, attr, obj):
         schema = self.schema
         if nested_obj is None:
             return None
+        if self.many and utils.is_iterable_but_not_string(nested_obj):
+            nested_obj = list(nested_obj)
         if not self.__updated_fields:
             schema._update_fields(obj=nested_obj, many=self.many)
             self.__updated_fields = True
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -816,23 +816,14 @@ def __filter_fields(self, field_names, obj, many=False):
 
         :param set field_names: Field names to include in the final
             return dictionary.
+        :param object|Mapping|list obj The object to base filtered fields on.
         :returns: An dict of field_name:field_obj pairs.
         """
         if obj and many:
-            try:  # Homogeneous collection
-                # Prefer getitem over iter to prevent breaking serialization
-                # of objects for which iter will modify position in the collection
-                # e.g. Pymongo cursors
-                if hasattr(obj, '__getitem__') and callable(getattr(obj, '__getitem__')):
-                    try:
-                        obj_prototype = obj[0]
-                    except KeyError:
-                        obj_prototype = next(iter(obj))
-                else:
-                    obj_prototype = next(iter(obj))
-            except (StopIteration, IndexError):  # Nothing to serialize
+            try:  # list
+                obj = obj[0]
+            except IndexError:  # Nothing to serialize
                 return dict((k, v) for k, v in self.declared_fields.items() if k in field_names)
-            obj = obj_prototype
         ret = self.dict_class()
         for key in field_names:
             if key in self.declared_fields:


Test patch:
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -782,3 +782,21 @@ class ValueSchema(Schema):
 
     serialized = ValueSchema(many=True).dump(slice).data
     assert serialized == values
+
+
+# https://github.com/marshmallow-code/marshmallow/issues/1163
+def test_nested_field_many_serializing_generator():
+    class MySchema(Schema):
+        name = fields.Str()
+
+    class OtherSchema(Schema):
+        objects = fields.Nested(MySchema, many=True)
+
+    def gen():
+        yield {'name': 'foo'}
+        yield {'name': 'bar'}
+
+    obj = {'objects': gen()}
+    data, _ = OtherSchema().dump(obj)
+
+    assert data.get('objects') == [{'name': 'foo'}, {'name': 'bar'}]