Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
is called on all objects that define `__set_name__` and exist in the values
of the `NamedTuple` class's class dictionary. Patch by Alex Waygood,
backporting https://github.com/python/cpython/pull/111876.
- Improve the error message when trying to call `issubclass()` against a
`Protocol` that has non-method members. Patch by Alex Waygood (backporting
https://github.com/python/cpython/pull/112344, by Randolph Scholz).

# Release 4.8.0 (September 17, 2023)

Expand Down
17 changes: 17 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3446,6 +3446,23 @@ def method(self) -> None: ...
self.assertIsInstance(Foo(), ProtocolWithMixedMembers)
self.assertNotIsInstance(42, ProtocolWithMixedMembers)

@skip_if_early_py313_alpha
def test_protocol_issubclass_error_message(self):
class Vec2D(Protocol):
x: float
y: float

def square_norm(self) -> float:
return self.x ** 2 + self.y ** 2

self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'})
expected_error_message = (
"Protocols with non-method members don't support issubclass()."
" Non-method members: 'x', 'y'."
)
with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)):
issubclass(int, Vec2D)


class Point2DGeneric(Generic[T], TypedDict):
a: T
Expand Down
7 changes: 6 additions & 1 deletion src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,13 @@ def __subclasscheck__(cls, other):
not cls.__callable_proto_members_only__
and cls.__dict__.get("__subclasshook__") is _proto_hook
):
non_method_attrs = sorted(
attr for attr in cls.__protocol_attrs__
if not callable(getattr(cls, attr, None))
)
raise TypeError(
"Protocols with non-method members don't support issubclass()"
"Protocols with non-method members don't support issubclass()."
f" Non-method members: {str(non_method_attrs)[1:-1]}."
)
if not getattr(cls, '_is_runtime_protocol', False):
raise TypeError(
Expand Down