Repository: marshmallow-code/apispec

Issue:
Marshmallow 3.12 schema with `fields` property produce error
Marshmallow added Fields be accessed by name as Schema attributes 

I have a schema like this 

```python
import marshmallow as ma

class Schema(ma.Schema):
    fields = ma.fields.List(ma.fields.Int)
```

Produces error 

```
 File "/venv/lib/python3.9/site-packages/apispec/ext/marshmallow/openapi.py", line 127, in schema2parameters
    fields = get_fields(schema, exclude_dump_only=True)
  File "/venv/lib/python3.9/site-packages/apispec/ext/marshmallow/common.py", line 56, in get_fields
    return filter_excluded_fields(fields, Meta, exclude_dump_only=exclude_dump_only)
  File "/venv/lib/python3.9/site-packages/apispec/ext/marshmallow/common.py", line 90, in filter_excluded_fields
    for key, value in fields.items()
AttributeError: 'List' object has no attribute 'items'
```

https://marshmallow.readthedocs.io/en/stable/changelog.html#id2

`get_fields` function gets `fields` schema attribute:
https://github.com/marshmallow-code/apispec/blob/dev/src/apispec/ext/marshmallow/common.py#L46


Can we eliminate this?






Discussion:

Patch:
diff --git a/src/apispec/ext/marshmallow/common.py b/src/apispec/ext/marshmallow/common.py
index 9f773a8..7f757f1 100644
--- a/src/apispec/ext/marshmallow/common.py
+++ b/src/apispec/ext/marshmallow/common.py
@@ -43,14 +43,12 @@ def get_fields(schema, *, exclude_dump_only=False):
     :param bool exclude_dump_only: whether to filter fields in Meta.dump_only
     :rtype: dict, of field name field object pairs
     """
-    if hasattr(schema, "fields"):
+    if isinstance(schema, marshmallow.Schema):
         fields = schema.fields
-    elif hasattr(schema, "_declared_fields"):
+    elif isinstance(schema, type) and issubclass(schema, marshmallow.Schema):
         fields = copy.deepcopy(schema._declared_fields)
     else:
-        raise ValueError(
-            f"{schema!r} doesn't have either `fields` or `_declared_fields`."
-        )
+        raise ValueError(f"{schema!r} is neither a Schema class nor a Schema instance.")
     Meta = getattr(schema, "Meta", None)
     warn_if_fields_defined_in_meta(fields, Meta)
     return filter_excluded_fields(fields, Meta, exclude_dump_only=exclude_dump_only)


Test patch:
diff --git a/tests/test_ext_marshmallow_common.py b/tests/test_ext_marshmallow_common.py
index 8b1f192..5ce03d1 100644
--- a/tests/test_ext_marshmallow_common.py
+++ b/tests/test_ext_marshmallow_common.py
@@ -79,3 +79,12 @@ class TestGetFields:
         assert list(get_fields(ExcludeSchema, exclude_dump_only=True).keys()) == [
             "field5"
         ]
+
+    # regression test for https://github.com/marshmallow-code/apispec/issues/673
+    def test_schema_with_field_named_fields(self):
+        class TestSchema(Schema):
+            fields = fields.Int()
+
+        schema_fields = get_fields(TestSchema)
+        assert list(schema_fields.keys()) == ["fields"]
+        assert isinstance(schema_fields["fields"], fields.Int)
diff --git a/tests/test_ext_marshmallow_openapi.py b/tests/test_ext_marshmallow_openapi.py
index 7e27845..121401c 100644
--- a/tests/test_ext_marshmallow_openapi.py
+++ b/tests/test_ext_marshmallow_openapi.py
@@ -200,9 +200,7 @@ class TestMarshmallowSchemaToModelDefinition:
             pass
 
         expected_error = (
-            "{!r} doesn't have either `fields` or `_declared_fields`.".format(
-                NotASchema
-            )
+            f"{NotASchema!r} is neither a Schema class nor a Schema instance."
         )
         with pytest.raises(ValueError, match=expected_error):
             openapi.schema2jsonschema(NotASchema)
@@ -346,7 +344,7 @@ class TestMarshmallowSchemaToParameters:
             pass
 
         expected_error = (
-            f"{NotASchema!r} doesn't have either `fields` or `_declared_fields`"
+            f"{NotASchema!r} is neither a Schema class nor a Schema instance."
         )
         with pytest.raises(ValueError, match=expected_error):
             openapi.schema2jsonschema(NotASchema)