this seems related to #5374 but only in the error message (?)
I'm trying to port a section of code that was previously using a metaclass to just using __subclasses__. Here's a minimal example of what I'm trying to do:
import abc
import inspect
from typing import Dict
from typing import List
from typing import Type
class C(metaclass=abc.ABCMeta):
@abc.abstractmethod
def foo(self):
raise NotImplementedError(foo.__name__)
def _make_reg() -> Dict[str, Type[C]]:
ret: Dict[str, Type[C]] = {}
#subclasses: List[Type[C]] = C.__subclasses__()
subclasses = C.__subclasses__()
reveal_type(C.__subclasses__())
reveal_type(subclasses)
while subclasses:
cls = subclasses.pop()
subclasses.extend(cls.__subclasses__())
if inspect.isabstract(cls):
continue
if cls.__name__ in ret:
raise AssertionError(cls.__name__, ret[cls.__name__], cls)
else:
ret[cls.__name__] = cls
return ret
REG = _make_reg()
This, as written, fails with:
$ mypy --version
mypy 0.650
$ mypy t.py
t.py:19: error: Revealed type is 'builtins.list[def () -> t.C]'
t.py:20: error: Revealed type is 'builtins.list[def () -> t.C]'
t.py:31: error: Only concrete class can be given where "Type[C]" is expected
If I replace the assignment of subclasses with the commented out line, it succeeds:
$ mypy t.py
t.py:19: error: Revealed type is 'builtins.list[def () -> t.C]'
t.py:20: error: Revealed type is 'builtins.list[Type[t.C]]'
Type[C] objects in a typesafe manner even without the inspect.isabstract call (notice I'm never even attempting to _construct_ a Type[C])This is a duplicate of #5374 and https://github.com/python/mypy/issues/4717. You can read the issues for why it may be unsafe (essentially in this case we can't exclude that dict.__setitem__() instantiates cls, but as @JukkaL mentioned in both issues, this looks like an overkill).
under what circumstance would dict.__setitem__ cause cls to be instantiated?
this also still doesn't explain why adding an annotation causes it to be ok (which is why I created a separate issue from those two)
under what circumstance would
dict.__setitem__causeclsto be instantiated?
Although in your code ret is just a dict (not a subclass), a subclass of dict can do this.
this also still doesn't explain why adding an annotation causes it to be ok
It does. Because with your annotation cls will be Type[C], and passing Type[C] where Type[C] is expected is OK.
isn't the place where I added the annotation also an assignment from () -> C to Type[C]? I would think that would also trigger this error
No, that check was added only as an additional special-case check when the types appear as "top level" in an assignment or function call, it doesn't affect subtyping, so doesn't trigger when the types appear in a nested position.
This whole abstract Type[...] was already a compromise between false positives and false negatives, and from what we see now it is probably a bit too "aggressive".
Also please stop arguing, it is not helpful.
I'm not arguing, simply curious -- thanks for the explanation that's all I wanted here <3
@asottile OK, sorry, sometimes it is hard to get the tone from written text :-)
Most helpful comment
@asottile OK, sorry, sometimes it is hard to get the tone from written text :-)