Repository: marshmallow-code/marshmallow

Issue:
Marshmallow 3 can't dump dict-likes with property
I'm trying to port [umongo](https://github.com/Scille/umongo) to marshmallow 3 and I stumbled upon this:

```py
import marshmallow as ma


class MyDict(dict):
    @property
    def prop(self):
        return 12


class MySchema(ma.Schema):
    prop = ma.fields.Int()


print(MySchema().dump(MyDict()))

# MA 2
MarshalResult(data={'prop': 12}, errors={})

# MA 3
{}
```

It is due to @taion's https://github.com/marshmallow-code/marshmallow/pull/811.

In `get_value`, we try to get the value as key and in case of `KeyError`, `default` is returned before even trying to get as attribute.

This is wrong for structures that can have keys and attributes.

I think this is an issue in marshmallow.

Discussion:

Patch:
diff --git a/marshmallow/utils.py b/marshmallow/utils.py
index 909d00ab..49b1c336 100644
--- a/marshmallow/utils.py
+++ b/marshmallow/utils.py
@@ -362,12 +362,13 @@ def _get_value_for_keys(obj, keys, default):
 
 
 def _get_value_for_key(obj, key, default):
+    if not hasattr(obj, '__getitem__'):
+        return getattr(obj, key, default)
+
     try:
         return obj[key]
-    except (TypeError, AttributeError):
+    except (KeyError, IndexError, TypeError, AttributeError):
         return getattr(obj, key, default)
-    except (KeyError, IndexError):
-        return default
 
 
 def set_value(dct, key, value):
@@ -393,6 +394,7 @@ def set_value(dct, key, value):
     else:
         dct[key] = value
 
+
 def callable_or_raise(obj):
     """Check that an object is callable, else raise a :exc:`ValueError`.
     """


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index bf10a1bb..142a6d8f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -48,18 +48,22 @@ class PointClass(object):
         self.x = x
         self.y = y
 
+class PointDict(dict):
+    def __init__(self, x, y):
+        super(PointDict, self).__init__({'x': x})
+        self.y = y
+
 @pytest.mark.parametrize(
     'obj', [
         PointNT(24, 42),
         PointClass(24, 42),
+        PointDict(24, 42),
         {'x': 24, 'y': 42},
     ],
 )
 def test_get_value_from_object(obj):
-    result = utils.get_value(obj, 'x')
-    assert result == 24
-    result2 = utils.get_value(obj, 'y')
-    assert result2 == 42
+    assert utils.get_value(obj, 'x') == 24
+    assert utils.get_value(obj, 'y') == 42
 
 def test_get_value_from_namedtuple_with_default():
     p = PointNT(x=42, y=None)