Skip to content

gh-112319: Allow special protocol members#112340

Draft
randolf-scholz wants to merge 11 commits intopython:mainfrom
randolf-scholz:rework_protocol_attrs
Draft

gh-112319: Allow special protocol members#112340
randolf-scholz wants to merge 11 commits intopython:mainfrom
randolf-scholz:rework_protocol_attrs

Conversation

@randolf-scholz
Copy link
Copy Markdown
Contributor

@randolf-scholz randolf-scholz commented Nov 23, 2023

@bedevere-app

This comment was marked as duplicate.

@bedevere-app

This comment was marked as duplicate.

Comment thread Lib/typing.py
Comment on lines +1864 to +1868
cls.__protocol_attrs__ = (
_get_local_protocol_members(namespace) | _get_parent_members(cls)
)
# local_attrs = _get_local_protocol_members(namespace)
# cls.__protocol_attrs__ = local_attrs.union(*map(_get_cls_members, bases))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure which variant is better...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The only difference between _get_cls_members and _get_parent_members is that the former checks cls.__mro__[:-1] and the latter only cls.__mro__[1:-1].

I would expect the _get_parent_members to be better for deeply inherited classes, and the _get_cls_members to be better for classes with many disjoint parents.

Comment thread Lib/typing.py Outdated
Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

haven't looked deeply yet, just a few comments from an initial skim:

Comment thread Lib/typing.py
Comment on lines -1678 to +1686
'__init__', '__module__', '__new__', '__slots__',
'__subclasshook__', '__weakref__', '__class_getitem__',
'__match_args__',
'__module__', '__slots__', '__match_args__', '__qualname__',
})
_SPECIAL_CALLABLE_NAMES = frozenset({
'__init__', '__new__', '__subclasshook__','__class_getitem__', '__weakref__',
})

# These special attributes will be not collected as protocol members.
EXCLUDED_ATTRIBUTES = _TYPING_INTERNALS | _SPECIAL_NAMES | {'_MutableMapping__marker'}
EXCLUDED_MEMBERS = EXCLUDED_ATTRIBUTES | _SPECIAL_CALLABLE_NAMES
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please keep the diff as minimal as possible. As far as I can tell, splitting the _SPECIAL_NAMES frozenset into a _SPECIAL_NAMES set and a _SPECIAL_CALLABLE_NAMES set shouldn't make any difference here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For my solution to work, I need both, because for things defined directly inside the protocol body, I only exclude EXCLUDED_ATTRIBUTES, but not _SPECIAL_CALLABLE_NAMES. (Hence, we can test for __class_getitem__). However, for general classes we need to exclude it (otherwise test.test_typing.ProtocolTests.test_collections_protocols_allowed fails).

See the difference in function body between _get_local_members and _get_local_protocol_members.

Comment thread Lib/typing.py Outdated
Comment thread Lib/typing.py Outdated
Comment thread Lib/typing.py
Comment thread Lib/typing.py Outdated
Comment thread Lib/typing.py Outdated
Co-authored-by: Alex Waygood <[email protected]>
@bedevere-app

This comment was marked as duplicate.

Co-authored-by: Alex Waygood <[email protected]>
@bedevere-app

This comment was marked as duplicate.

Comment thread Lib/test/test_typing.py Outdated
Comment thread Lib/test/test_typing.py
Comment thread Lib/test/test_typing.py Outdated
Comment thread Lib/test/test_typing.py Outdated
@bedevere-app

This comment was marked as duplicate.

@bedevere-app

This comment was marked as duplicate.

@bedevere-app

This comment was marked as duplicate.

Co-authored-by: Alex Waygood <[email protected]>
@bedevere-app

This comment was marked as duplicate.

Co-authored-by: Alex Waygood <[email protected]>
@bedevere-app

This comment was marked as resolved.

Comment thread Lib/typing.py
return attrs


def _get_protocol_attrs(cls):
Copy link
Copy Markdown
Contributor Author

@randolf-scholz randolf-scholz Nov 23, 2023

Choose a reason for hiding this comment

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

After the modifications here, _get_protocol_attrs is not used anymore, and it will even give some wrong results (e.g. if the Protocol defines __class_getitem__). So maybe just delete it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If it's not used anywhere anymore, then yes, it should be deleted. It's an undocumented, private function; if anybody's using it and their code is broken by us deleting it, that's on them. I can't find any uses of it on grep.app anyway, so I don't think anybody will have their code broken by us deleting it.

@github-actions
Copy link
Copy Markdown

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale Stale PR or inactive for long period of time.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@runtime_checkable Protocol doesn't check if __class_getitem__ exists.

2 participants