Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-43453: Update and re-add example to typing runtime_checkable #27013

Merged

Conversation

akulakov
Copy link
Contributor

@akulakov akulakov commented Jul 4, 2021

This example was outdated and removed in 3.10, I've found a similar example that can be used to clarify this function.

https://bugs.python.org/issue43453

Copy link
Member

@Fidget-Spinner Fidget-Spinner left a comment

Choose a reason for hiding this comment

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

Thanks for the docs improvement. The example looks good, I just have a few questions below.

Doc/library/typing.rst Outdated Show resolved Hide resolved
not their type signatures.
:func:`runtime_checkable` will check only the presence of the required
methods, not their type signatures. For example, :class:`ssl.SSLObject`
implements :func:`__init__`, therefore it passes an :func:`issubclass`
Copy link
Member

Choose a reason for hiding this comment

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

Runtime protocol checking is an area which I'm not too familiar with so I may be wrong here, but isn't the requirement for Callable to define __call__, not __init__?

https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

__call__ makes the instances callable, not the class itself. In my testing, a class meets the Callable check if it doesn't have __call__ defined, and it works if init is either explicitly defined or implicitly inherited from object. However, this class -- SSLObject -- cannot be called because init raises an exception. So this example illustrates that check for callable will tell you SSLObject is callable when in fact it's not callable in practice due to the dunder method raising an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

[ins] In [89]: class A:
          ...:     def __init__(self):0
          ...:

[ins] In [90]: isinstance(A,Callable)
Out[90]: True

[ins] In [91]: class B:pass

[ins] In [92]: isinstance(B,Callable)
Out[92]: True

Copy link
Member

Choose a reason for hiding this comment

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

Wow, interesting sleuthing. Thanks for investigating!

Copy link
Member

@Fidget-Spinner Fidget-Spinner Jul 4, 2021

Choose a reason for hiding this comment

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

I wonder if this has anything to play in it, but a class implicitly has __call__ from the type object:

>>> class A: ...
>>> A.__call__
<method-wrapper '__call__' of type object at 0x0000028F6E186DE0>
>>> type(A).__call__
<slot wrapper '__call__' of 'type' objects>

>>> type(A).__call__(A)
<__main__.A object at 0x0000028F6DE94280>
>>> A()
<__main__.A object at 0x0000028F6DE8BED0>

As you can see, the last two are equivalent. This corresponds to what the docs say about callables https://docs.python.org/3/reference/datamodel.html#object.__call__. Also mentioned here for callable() https://docs.python.org/3/library/functions.html#callable

The part I'm nitpicky about is saying ssl.SSLObject passes the issubclass check because it defines __init__. I think that's untrue -- it passes the check because all classes are callable (as mentioned in the docs for callable() above). I completely agree with the part about __init__ being used for raising exceptions.
I think the phrasing would be better if it became this:

        :func:`runtime_checkable` will check only the presence of the required
        methods, not their type signatures. For example, :class:`ssl.SSLObject`
        is a class, therefore it passes an :func:`issubclass`
        check against :data:`Callable`.  However, the
        :meth:`ssl.SSLObject.__init__` method exists only to raise a
        :exc:`TypeError` with a more informative message.

Sorry about the bikshedding!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, good catch! I think your edit is good, but I want to highlight the fact that init as implemented makes this class uncallable, maybe something like this:

        :func:`runtime_checkable` will check only the presence of the required
        methods, not their type signatures. For example, :class:`ssl.SSLObject`
        is a class, therefore it passes an :func:`issubclass`
        check against :data:`Callable`.  However, the
        :meth:`ssl.SSLObject.__init__` method exists only to raise a
        :exc:`TypeError` with a more informative message, therefore making
        it impossible to call (instantiate) :class:`ssl.SSLObject`.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

Great!

akulakov and others added 2 commits July 4, 2021 10:32
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Copy link
Member

@Fidget-Spinner Fidget-Spinner left a comment

Choose a reason for hiding this comment

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

LGTM!

@gvanrossum gvanrossum merged commit 17f94e2 into python:main Jul 5, 2021
@bedevere-bot
Copy link

@gvanrossum: Please replace # with GH- in the commit message next time. Thanks!

@gvanrossum
Copy link
Member

Does this need backports?

@akulakov
Copy link
Contributor Author

akulakov commented Jul 5, 2021

Does this need backports?

I believe it does - but only to 3.10, not to 3.9.

@akulakov
Copy link
Contributor Author

akulakov commented Jul 5, 2021

@Fidget-Spinner thanks for the review :)

@Fidget-Spinner Fidget-Spinner added the needs backport to 3.10 only security fixes label Jul 5, 2021
@miss-islington
Copy link
Contributor

Thanks @akulakov for the PR, and @gvanrossum for merging it 🌮🎉.. I'm working now to backport this PR to: 3.10.
🐍🍒⛏🤖

@bedevere-bot bedevere-bot removed the needs backport to 3.10 only security fixes label Jul 5, 2021
@bedevere-bot
Copy link

GH-27036 is a backport of this pull request to the 3.10 branch.

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Jul 5, 2021
…honGH-27013)

Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
(cherry picked from commit 17f94e2)

Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
@akulakov akulakov deleted the 43453-Readd-example-typing-runtime_checkable branch July 5, 2021 16:46
miss-islington added a commit that referenced this pull request Jul 5, 2021
…27013)

Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
(cherry picked from commit 17f94e2)

Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir skip news
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants