[Python-Dev] PEP 550 V5 (original) (raw)

Eric Snow ericsnowcurrently at gmail.com
Fri Sep 1 23:29:28 EDT 2017


Nice working staying on top of this! Keeping up with discussion is arguably much harder than actually writing the PEP. :) I have some comments in-line below.

-eric

On Fri, Sep 1, 2017 at 5:02 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:

[snip]

Abstract ======== [snip] Rationale ========= [snip] Goals =====

I still think that the Abstract, Rationale, and Goals sections should be clear that a major component of this proposal is lookup via chained contexts. Without such clarity it may not be apparent that chained lookup is not strictly necessary to achieve the stated goals (i.e. an async-compatible TLS replacement). This matters because the chaining introduces extra non-trivial complexity.

The goal of this PEP is to provide a more reliable threading.local() alternative, which:

[snip] * implements TLS-like semantics for synchronous code, so that users like decimal and numpy can switch to the new mechanism with minimal risk of breaking backwards compatibility; [snip] Replication of threading.local() interface ------------------------------------------ Choosing the threading.local()-like interface for context variables was considered and rejected for the following reasons: * A survery of the standard library and Django has shown that the vast majority of threading.local() uses involve a single attribute, which indicates that the namespace approach is not as helpful in the field. * Using _getattr_() instead of .get() for value lookup does not provide any way to specify the depth of the lookup (i.e. search only the top logical context). * Single-value ContextVar is easier to reason about in terms of visibility. Suppose ContextVar() is a namespace, and the consider the following:: ns = contextvars.ContextVar('ns') def gen(): ns.a = 2 yield assert ns.b == 'bar' # ?? def main(): ns.a = 1 ns.b = 'foo' g = gen() next(g) # should not see the ns.a modification in gen() assert ns.a == 1 # but should gen() see the ns.b modification made here? ns.b = 'bar' yield The above example demonstrates that reasoning about the visibility of different attributes of the same context var is not trivial. * Single-value ContextVar allows straightforward implementation of the lookup cache; * Single-value ContextVar interface allows the C-API to be simple and essentially the same as the Python API. See also the mailing list discussion: [26], [27]. [snip]

On the one hand the first three sections imply that the PEP is intended as a replacement for the current TLS mechanism; threading.local. On the other hand, the PEP (and related discussion) clearly says that the feature works differently than threading.local and hence is not a (drop-in) replacement. I'd prefer it to be a drop-in replacement but recognize I'm on the losing side of that argument. :P Regardless, having a consistent message in the PEP would help folks looking to switch over.

Speaking of which, I have plans for the near-to-middle future that involve making use of the PEP 550 functionality in a way that is quite similar to decimal. However, it sounds like the implementation of such (namespace) contexts under PEP 550 is much more complex than it is with threading.local (where subclassing made it easy). It would be helpful to have some direction in the PEP on how to port to PEP 550 from threading.local. It would be even better if the PEP included the addition of a contextlib.Context or contextvars.Context class (or NamespaceContext or ContextNamespace or ...). :) However, I recognize that may be out of scope for this PEP.



More information about the Python-Dev mailing list