[Python-Dev] PEP 567 v3 (original) (raw)

Yury Selivanov yselivanov.ml at gmail.com
Thu Jan 18 00🔞05 EST 2018


On Wed, Jan 17, 2018 at 8:53 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:

On Wed, Jan 17, 2018 at 2:24 PM, Guido van Rossum <gvanrossum at gmail.com> wrote:

Perhaps you can update the PEP with a summary of the rejected ideas from this thread? The Rejected Ideas section of the PEP is now updated with the below:

I've added two more subsections to Rejected Ideas:

Make Context a MutableMapping

Making the Context class implement the abc.MutableMapping interface would mean that it is possible to set and unset variables using Context[var] = value and del Context[var] operations.

This proposal was deferred to Python 3.8+ because of the following:

  1. If in Python 3.8 it is decided that generators should support context variables (see :pep:550 and :pep:568), then Context would be transformed into a chain-map of context variables mappings (as every generator would have its own mapping). That would make mutation operations like Context.__delitem__ confusing, as they would operate only on the topmost mapping of the chain.

  2. Having a single way of mutating the context (ContextVar.set() and ContextVar.reset() methods) makes the API more straightforward.

    For example, it would be non-obvious why the below code fragment does not work as expected::

    var = ContextVar('var')

    ctx = copy_context() ctx[var] = 'value' print(ctx[var]) # Prints 'value'

    print(var.get()) # Raises a LookupError

    While the following code would work::

    ctx = copy_context()

    def func(): ctx[var] = 'value' # Contrary to the previous example, this would work # because 'func()' is running within 'ctx'. print(ctx[var]) print(var.get()) ctx.run(func)

Have initial values for ContextVars

Nathaniel Smith proposed to have a required initial_value keyword-only argument for the ContextVar constructor.

The main argument against this proposal is that for some types there is simply no sensible "initial value" except None. E.g. consider a web framework that stores the current HTTP request object in a context variable. With the current semantics it is possible to create a context variable without a default value::

# Framework:
current_request: ContextVar[Request] = \
    ContextVar('current_request')


# Later, while handling an HTTP request:
request: Request = current_request.get()

# Work with the 'request' object:
return request.method

Note that in the above example there is no need to check if request is None. It is simply expected that the framework always sets the current_request variable, or it is a bug (in which case current_request.get() would raise a LookupError).

If, however, we had a required initial value, we would have to guard against None values explicitly::

# Framework:
current_request: ContextVar[Optional[Request]] = \
    ContextVar('current_request', initial_value=None)


# Later, while handling an HTTP request:
request: Optional[Request] = current_request.get()

# Check if the current request object was set:
if request is None:
    raise RuntimeError

# Work with the 'request' object:
return request.method

Moreover, we can loosely compare context variables to regular Python variables and to threading.local() objects. Both of them raise errors on failed lookups (NameError and AttributeError respectively).

Yury



More information about the Python-Dev mailing list