1) Yes, I was saying `isinstance(X(), P)` succeeds when it should, because X doesn't have `protocol_member` defined. 2) No, I wasn't suggesting Protocols should be ABCs at runtime. My only intent was to play back the objection of Ken Jin to point 1 and my reaction to that. Unfortunately, my playback of Ken Jins objection was too abbreviated. His objection was based on the PEP 544 statement: "At runtime, protocol classes will be simple ABCs. There is no intent to provide sophisticated runtime instance and class checks against protocol classes. This would be difficult and error-prone and will contradict the logic of PEP 484." - My answer to this is: checking if a protocol member exists, is not a sophisticated, but the most basic check, and therefore should be done if not difficult or error-prone. As shown in the code, `_ProtocolMeta.__instancecheck__` already performs such kind of checks, additionally to the checks performed by `ABCMeta.__instancecheck__`. Regarding your (Teddy) explanations regarding 1: a) After your hint, I agree that it is surprising that `isinstance(X(), P)` in my code fragment succeeds at all. PEP 544 states: "A protocol can be used as a second argument in isinstance() and issubclass() only if it is explicitly opt-in by @runtime_checkable decorator." So, I think, the code fragment given in my initial message should already fail because of the missing @runtime_checkable decorator. But even with such a decorator, I think it should fail. But it does not fail: ``` from typing import Protocol, runtime_checkable @runtime_checkable class P(Protocol): protocol_member: str # no default value, but still a protocol member class X(P): # inherits P but does NOT implement protocol_member, since P did not provide a default value pass assert isinstance(X(), P) # should fail, but it doesn't X().protocol_member # raises: AttributeError: 'X' object has no attribute 'protocol_member' ``` b) As you (Teddy) pointed out, some properties of protocols cannot be checked at runtime, because the information is not available at runtime (erasing). Indeed, `protocol_member` is never stored as an attribute of `P`, as it has no default value resulting in `hasattr(P, 'protocol_member') is False`. But nevertheless `protocol_member` is stored in `P.__annotations__`, is available at runtime, and is actually returned by the current implementation of CPython's function `_get_protocol_attrs(cls)` in typing.py: https://github.com/python/cpython/blob/3.9/Lib/typing.py#L1048 It is one of the protocol attributes iterated over in the code of `_ProtocolMeta.__instancecheck__` shown in my first message: `for attr in _get_protocol_attrs(cls)`. For reasons that are incomprehensible for me, the current check ignores all protocol attributes which are not callable. I think the check should not be restricted to callables only. -- Paul