abstract class shouldn't be considered Callable
Bug Report
An abstract class fits the typing for Callable.
To Reproduce
import abc
from typing import Callable
class A(abc.ABC):
@abc.abstractmethod
def m(self) -> None:
...
class C(A):
def m(self) -> None:
pass
def f() -> A:
return C()
def t(x: Callable[[], A]) -> None:
x()
def main() -> None:
t(f)
t(C)
t(A) # should give typing error - A is not callable because it can't be instantiated
A() # mypy correctly reports that A can't be instantiated
if __name__ == "__main__":
main()
Expected Behavior
mypy should report a typing error if an abstract class is put where a Callable should go
Actual Behavior
mypy doesn't report any error if an abstract class is put where a Callable should go
Your Environment
- Mypy version used: 0.910
- Python version used: 3.8.10
The built-in callable function returns true on the abstract class. I guess the typing error is not raised due to ABC appears to be callable but it's not callable as it can't be instantiated.
I strongly disagree with this. AnyIO has a number of abstract classes where their __new__() methods return an instance of a subclass.
I strongly disagree with this. AnyIO has a number of abstract classes where their
__new__()methods return an instance of a subclass.
Can you provide an example to show that this is a pattern worth supporting?
If a Python API user calls a class, they expect to get an instance of that specific class, not an instance of a subclass.
AnyIO is an async "meta-framework" that delegates actual I/O to underlying frameworks like asyncio or Trio. Therefore it has classes like Lock and Semaphore which, when instantiated, look up the current event loop implementation (using sniffio) and then use the appropriate back-end to instantiate the actual implementation (which would be a subclass of this abstract class). Implementing this in __new__() is far better than the alternative of having separate factory methods like create_lock() which AnyIO had in the past.
Implementing this in
__new__()is far better than the alternative of having separate factory methods likecreate_lock()
I disagree with this. Having a separate factory method like create_lock() would be better.
The reason it would be better is that if a Python API user calls a class, they expect to get an instance of that specific class, not an instance of a subclass.
That ship has sailed, and nobody has complained about this design change. It makes it much easier to shift from asyncio to AnyIO when these classes essentially work the same way in both libraries. I dislike that MyPy enforces certain design choices on its users, without them even being mandated by any PEP whatsoever.