[Python-Dev] Timeout for PEP 550 / Execution Context discussion

Nick Coghlan ncoghlan at gmail.com
Tue Oct 17 00:09:27 EDT 2017

On 17 October 2017 at 03:00, Guido van Rossum <guido at python.org> wrote:

> On Mon, Oct 16, 2017 at 9:11 AM, Yury Selivanov <yselivanov.ml at gmail.com>
> wrote:
>> > I agree, but I don't see how making the type a subtype (or duck type) of
>> > MutableMapping prevents any of those strategies. (Maybe you were
>> equating
>> > MutableMapping with "subtype of dict"?)
>> Question: why do we want EC objects to be mappings?  I'd rather make
>> them opaque, which will result in less code and make it more
>> future-proof.
> I'd rather have them mappings, since that's what they represent. It helps
> users understand what's going on behind the scenes, just like modules,
> classes and (most) instances have a `__dict__` that you can look at and (in
> most cases) manipulate.

Perhaps rather than requiring that EC's *be* mappings, we could instead
require that they expose a mapping API as their __dict__ attribute, similar
to the way class dictionaries work?

Then the latter could return a proxy that translated mapping operations
into the appropriate method calls on the ContextVar being used as the key.

Something like:

    class ExecutionContextProxy:
        def __init__(self, ec):
            self._ec = ec
            # Omitted from the methods below: checking if this EC is the
            # active EC, and implicitly switching to it if it isn't (for
read ops)
            # or complaining (for write ops)

        # Individual operations call methods on the key itself
        def __getitem__(self, key):
            return key.get()
        def __setitem__(self, key, value):
            if not isinstance(key, ContextVar):
                raise TypeError("Execution context keys must be context
        def __delitem__(self, key):

        # The key set would be the context vars assigned in the active
        def __contains__(self, key):
            # Note: PEP 550 currently calls the below method ec.vars(),
            # but I just realised that's confusing, given that the vars()
            # returns a mapping
            return key in self._ec.assigned_vars()
        def __iter__(self):
            return iter(self._ec.assigned_vars())
        def keys(self):
            return self._ec.assigned_vars()

        # These are the simple iterator versions of values() and items()
        # but they could be enhanced to return dynamic views instead
        def values(self):
            for k in self._ec.assigned_vars():
                yield k.get()
        def items(self):
            for k in self._ec.assigned_vars():
                yield (k, k.get())

The nice thing about defining the mapping API as a wrapper around otherwise
opaque interpreter internals is that it makes it clearer which operations
are expected to matter for runtime performance (i.e. the ones handled by
the ExecutionContext itself), and which are mainly being provided as
intuition pumps for humans attempting to understand how execution contexts
actually work (whether for debugging purposes, or simply out of curiosity)

If there's a part of the mapping proxy API where we don't have a strong
intuition about how it should work, then instead of attempting to guess
suitable semantics, we can instead define it as raising RuntimeError for
now, and then wait and see if the appropriate semantics become clearer over


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20171017/ab38582b/attachment.html>

More information about the Python-Dev mailing list