Skip to content

Comments

[ty] Detect invalid issubclass() calls against Protocol classes with non-method members#22896

Merged
AlexWaygood merged 1 commit intomainfrom
alex/proto-issubclass-method-members
Jan 27, 2026
Merged

[ty] Detect invalid issubclass() calls against Protocol classes with non-method members#22896
AlexWaygood merged 1 commit intomainfrom
alex/proto-issubclass-method-members

Conversation

@AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Jan 27, 2026

Summary

These fail at runtime, and the typing conformance suite mandates that type checkers should catch the error.

In order to provide good diagnostics, I added tracking of the Definition of protocol members. This allows us to provide subdiagnostics pointing to the declarations or definitions of specific protocol members.

Fixes astral-sh/ty#1878

Test Plan

mdtests and snapshots

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Jan 27, 2026
@AlexWaygood AlexWaygood force-pushed the alex/proto-issubclass-method-members branch from 07dfe8a to 408182e Compare January 27, 2026 18:26
@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 27, 2026

Typing conformance results improved 🎉

The percentage of diagnostics emitted that were expected errors increased from 79.63% to 79.65%. The percentage of expected errors that received a diagnostic increased from 70.86% to 70.95%.

Summary

Metric Old New Diff Outcome
True Positives 766 767 +1 ⏫ (✅)
False Positives 196 196 +0
False Negatives 315 314 -1 ⏬ (✅)
Total Diagnostics 962 963 +1
Precision 79.63% 79.65% +0.02% ⏫ (✅)
Recall 70.86% 70.95% +0.09% ⏫ (✅)

True positives added

Details
Location Name Message
protocols_runtime_checkable.py:55:8 isinstance-against-protocol DataProtocol cannot be used as the second argument to issubclass as it is a protocol with non-method members

@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 27, 2026

mypy_primer results

Changes were detected when running on open source projects
prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:94:28: error[invalid-assignment] Object of type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:99:28: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:86:21: error[invalid-assignment] Object of type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:87:21: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/deployments/runner.py:997:70: warning[possibly-missing-attribute] Attribute `__name__` may be missing on object of type `Unknown | ((...) -> Any)`
+ src/prefect/deployments/runner.py:997:70: warning[possibly-missing-attribute] Attribute `__name__` may be missing on object of type `Unknown | (((...) -> Any) & ((*args: object, **kwargs: object) -> object))`
- src/prefect/deployments/steps/core.py:137:38: error[invalid-argument-type] Argument is incorrect: Argument type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/flow_engine.py:989:32: error[invalid-await] `Unknown | R@FlowRunEngine | Coroutine[Any, Any, R@FlowRunEngine]` is not awaitable
- src/prefect/flow_engine.py:1580:24: error[invalid-await] `Unknown | R@AsyncFlowRunEngine | Coroutine[Any, Any, R@AsyncFlowRunEngine]` is not awaitable
- src/prefect/flow_engine.py:1661:43: error[invalid-argument-type] Argument to function `next` is incorrect: Expected `SupportsNext[Unknown]`, found `Unknown | R@run_generator_flow_sync`
- src/prefect/flow_engine.py:1669:21: warning[possibly-missing-attribute] Attribute `throw` may be missing on object of type `Unknown | R@run_generator_flow_sync`
- src/prefect/flow_engine.py:1703:44: warning[possibly-missing-attribute] Attribute `__anext__` may be missing on object of type `Unknown | R@run_generator_flow_async`
- src/prefect/flow_engine.py:1710:25: warning[possibly-missing-attribute] Attribute `throw` may be missing on object of type `Unknown | R@run_generator_flow_async`
- src/prefect/flows.py:285:34: error[unresolved-attribute] Object of type `(**P@Flow) -> R@Flow` has no attribute `__name__`
+ src/prefect/flows.py:285:34: error[unresolved-attribute] Object of type `((**P@Flow) -> R@Flow) & ((*args: object, **kwargs: object) -> object)` has no attribute `__name__`
- src/prefect/flows.py:403:68: error[unresolved-attribute] Object of type `(**P@Flow) -> R@Flow` has no attribute `__name__`
+ src/prefect/flows.py:403:68: error[unresolved-attribute] Object of type `((**P@Flow) -> R@Flow) & ((*args: object, **kwargs: object) -> object)` has no attribute `__name__`
- src/prefect/flows.py:1937:21: error[no-matching-overload] No overload of function `run_coro_as_sync` matches arguments
+ src/prefect/flows.py:1877:53: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `Unknown | int | dict[str, Any] | ... omitted 4 union elements` on object of type `dict[str, Any]`
+ src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `Unknown | dict[str, Any]` on object of type `dict[str, Any]`
- src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | int | dict[str, Any] | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | dict[str, Any]]`
- src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown]`
- src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown]`
+ src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `str | dict[str, Any]` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `str | int | dict[str, Any] | ... omitted 3 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:234:20: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `int | Unknown | float | ... omitted 4 union elements`
- Found 5368 diagnostics
+ Found 5356 diagnostics

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
- tests/frame/test_groupby.py:229:15: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[str | bytes | int | ... omitted 12 union elements]`
- tests/frame/test_groupby.py:625:15: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[str | bytes | int | ... omitted 12 union elements]`
- Found 4413 diagnostics
+ Found 4411 diagnostics

No memory usage changes detected ✅

@AlexWaygood AlexWaygood force-pushed the alex/proto-issubclass-method-members branch 2 times, most recently from d5c4af1 to 42cbca9 Compare January 27, 2026 18:42
@AlexWaygood AlexWaygood marked this pull request as ready for review January 27, 2026 18:42
@AlexWaygood AlexWaygood force-pushed the alex/proto-issubclass-method-members branch from 42cbca9 to 9a3cda3 Compare January 27, 2026 18:54
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks excellent, thank you!

@AlexWaygood AlexWaygood force-pushed the alex/proto-issubclass-method-members branch from 9a3cda3 to c58316f Compare January 27, 2026 19:00
@AlexWaygood AlexWaygood enabled auto-merge (squash) January 27, 2026 19:02
@AlexWaygood AlexWaygood merged commit f79ec02 into main Jan 27, 2026
48 checks passed
@AlexWaygood AlexWaygood deleted the alex/proto-issubclass-method-members branch January 27, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit diagnostic on issubclass calls against protocols with non-method members

2 participants