[Python-Dev] Inheritance vs composition in backcompat (PEP521)

Koos Zevenhoven k7hoven at gmail.com
Mon Oct 2 13:13:54 EDT 2017


Hi all, It was suggested that I start a new thread, because the other
thread drifted away from its original topic. So here, in case someone is
interested:

On Oct 2, 2017 17:03, "Koos Zevenhoven <k7hoven at gmail.com> wrote:

On Mon, Oct 2, 2017 at 6:42 AM, Guido van Rossum <guido at python.org> wrote:

On Sun, Oct 1, 2017 at 1:52 PM, Koos Zevenhoven <k7hoven at gmail.com> wrote:

On Oct 1, 2017 19:26, "Guido van Rossum" <guido at python.org> wrote:

Your PEP is currently incomplete. If you don't finish it, it is not even a
contender. But TBH it's not my favorite anyway, so you could also just
withdraw it.


I can withdraw it if you ask me to, but I don't want to withdraw it without
any reason. I haven't changed my mind about the big picture. OTOH, PEP 521
is elegant and could be used to implement PEP 555, but 521 is almost
certainly less performant and has some problems regarding context manager
wrappers that use composition instead of inheritance.


It is my understanding that PEP 521 (which proposes to add optional
__suspend__ and __resume__ methods to the context manager protocol, to be
called whenever a frame is suspended or resumed inside a `with` block) is
no longer a contender because it would be way too slow. I haven't read it
recently or thought about it, so I don't know what the second issue you
mention is about (though it's presumably about the `yield` in a context
manager implemented using a generator decorated with
`@contextlib.contextmanager`).


​Well, it's not completely unrelated to that. The problem I'm talking about
is perhaps most easily seen from a simple context manager wrapper that uses
composition instead of inheritance:

class Wrapper:
    def __init__(self):
        self._wrapped = SomeContextManager()

    def __enter__(self):
        print("Entering context")
        return self._wrapped.__enter__()

    def __exit__(self):
        self._wrapped.__exit__()
        print("Exited context")


Now, if the wrapped contextmanager becomes a PEP 521 one with __suspend__
and __resume__, the Wrapper class is broken, because it does not respect
__suspend__ and __resume__. So actually this is a backwards compatiblity
issue.

But if the wrapper is made using inheritance, the problem goes away:


class Wrapper(SomeContextManager):
    def __enter__(self):
        print("Entering context")
        return super().__enter__()

    def __exit__(self):
        super().__exit__()
        print("Exited context")


Now the wrapper cleanly inherits the new optional __suspend__ and
__resume__ from the wrapped context manager type.


––Koos




-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20171002/4afb3fe9/attachment.html>


More information about the Python-Dev mailing list