Repository: python/mypy

Issue:
Error message for "cannot instantiate abstract class" should indicate how many methods
E.g. I got this message:

```
test-data/samples/crawl.py:824: error: Cannot instantiate abstract class 'AbstractEventLoop' with abstract attributes 'add_reader', 'add_signal_handler', 'add_writer', 'call_at' and 'call_exception_handler'
```

But in fact there are a total of 38 abstract methods in that class. It would be nice if `format_string_list()` could replace some part of a large list with an ellipsis and perhaps an indication of how many items it suppressed.

Error message for "cannot instantiate abstract class" should indicate how many methods
E.g. I got this message:

```
test-data/samples/crawl.py:824: error: Cannot instantiate abstract class 'AbstractEventLoop' with abstract attributes 'add_reader', 'add_signal_handler', 'add_writer', 'call_at' and 'call_exception_handler'
```

But in fact there are a total of 38 abstract methods in that class. It would be nice if `format_string_list()` could replace some part of a large list with an ellipsis and perhaps an indication of how many items it suppressed.



Discussion:

Resolving patch:
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -767,7 +767,7 @@ def incompatible_conditional_function_def(self, defn: FuncDef) -> None:
     def cannot_instantiate_abstract_class(self, class_name: str,
                                           abstract_attributes: List[str],
                                           context: Context) -> None:
-        attrs = format_string_list("'%s'" % a for a in abstract_attributes[:5])
+        attrs = format_string_list("'%s'" % a for a in abstract_attributes)
         self.fail("Cannot instantiate abstract class '%s' with abstract "
                   "attribute%s %s" % (class_name, plural_s(abstract_attributes),
                                    attrs),
@@ -881,8 +881,10 @@ def format_string_list(s: Iterable[str]) -> str:
     assert len(l) > 0
     if len(l) == 1:
         return l[0]
-    else:
+    elif len(l) <= 5:
         return '%s and %s' % (', '.join(l[:-1]), l[-1])
+    else:
+        return '%s, ... and %s (%i methods suppressed)' % (', '.join(l[:2]), l[-1], len(l) - 3)
 
 
 def callable_name(type: CallableType) -> str:


Test patch:
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -157,6 +157,33 @@ class B(A): pass
 B()# E: Cannot instantiate abstract class 'B' with abstract attributes 'f' and 'g'
 [out]
 
+[case testInstantiatingClassWithInheritedAbstractMethodAndSuppression]
+from abc import abstractmethod, ABCMeta
+import typing
+class A(metaclass=ABCMeta):
+    @abstractmethod
+    def a(self): pass
+    @abstractmethod
+    def b(self): pass
+    @abstractmethod
+    def c(self): pass
+    @abstractmethod
+    def d(self): pass
+    @abstractmethod
+    def e(self): pass
+    @abstractmethod
+    def f(self): pass
+    @abstractmethod
+    def g(self): pass
+    @abstractmethod
+    def h(self): pass
+    @abstractmethod
+    def i(self): pass
+    @abstractmethod
+    def j(self): pass
+a = A() # E: Cannot instantiate abstract class 'A' with abstract attributes 'a', 'b', ... and 'j' (7 methods suppressed)
+[out]
+
 
 -- Implementing abstract methods
 -- -----------------------------