Description
Bug Report
If you use isinstance(item, type)
, this used to be used in conjunction with dataclasses.is_dataclass
to identify types and instances. It's the recommendation in the CPython documentation, in fact: https://docs.python.org/3/library/dataclasses.html#dataclasses.is_dataclass. However, the latests MyPy no longer narrows this correctly (from what I can tell, unless I'm doing something wrong), and still thinks I might have an instance, which then breaks later when creating an instance, etc.
Note that I can flip the order, in which case I just get builtins.type
for both versions. This isn't enough to fix it for some of the more complex usages in scikit-build-core, though.
To Reproduce
import dataclasses
raw_target: object
if isinstance(raw_target, type) and dataclasses.is_dataclass(raw_target):
reveal_type(raw_target)
Expected Behavior
This is from 1.15:
tmp.py:6: note: Revealed type is "Type[_typeshed.DataclassInstance]"
Actual Behavior
tmp.py:6: note: Revealed type is "Union[_typeshed.DataclassInstance, Type[_typeshed.DataclassInstance]]"
Your Environment
- Mypy version used:
uvx --from git+https://github.com/python/mypy mypy tmp.py
was used, so latest commit, also latest release 1.15 version for comparison. - Python version used: 3.13
Noticed in hauntsaninja/mypy_primer#174.
Activity
chore: adjust typing to be ready for next mypy release (#1088)
sterliakov commentedon May 27, 2025
This is a regression, and a rather bad one. Note also that this is somehow dependent on checks order. MRE without stdlib dependency:
https://mypy-play.net/?mypy=master&python=3.13&flags=strict&gist=6e2a57a00f5aa275ebbb2abbefa972e6
My MRE exhibits the same behaviour on 1.15.0, so it isn't the same. And this is correct -
A
andtype
can have a common LSP-compatible subclass.This issue bisects to #17678;
is_dataclass
is an overloaded function:And this looks like a bug - after first
isinstance
we should haveraw_target
already narrowed totype
, so the second overload should match.sterliakov commentedon May 27, 2025
@hauntsaninja could you please create a label for PEP 742 / TypeIs?
sterliakov commentedon May 28, 2025
I get it now. When
find_isinstance_check_helper
is applied, we check all arguments at once, which means wrong@overload
is selected - mypy only stores narrowed types when all typemaps are known. cc @sobolevn as original author of #17678 - this sounds like a painful limitation, any overload resolution here won't take preceding narrowing statements into account.Splitting the ladder works:
[-]Regression in 1.16 on DataclassInstance?[/-][+][1.16 regression] narrowing on is_dataclass[/+]Revert "Infer correct types with overloads of `Type[Guard | Is]` (#17678
Type[Guard | Is]
#19161Revert "Infer correct types with overloads of `Type[Guard | Is]` (#19161
Revert "Infer correct types with overloads of `Type[Guard | Is]` (#19161
Revert "Infer correct types with overloads of `Type[Guard | Is]` (pyt…
3 remaining items