<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Mon, Oct 16, 2017 at 10:02 PM, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class="">On 17 October 2017 at 14:31, Guido van Rossum <span dir="ltr"><<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">No, that version just defers to magic in ContextVar.get/set, whereas what I'd like to see is that the latter are just implemented in terms of manipulating the mapping directly. The only operations for which speed matters would be __getitem__ and __setitem__; most other methods just defer to those. __delitem__ must also be a primitive, as must __iter__ and __len__ -- but those don't need to be as speedy (however __delitem__ must really work!).<br></div></blockquote><div><br></div></span><div>To have the mapping API at the base of the design, we'd want to go back to using the ContextKey version of the API as the core primitive (to ensure we don't get name conflicts between different modules and packages), and then have ContextVar be a convenience wrapper that always accesses the currently active context:</div><div><br></div><div> class ContextKey:</div><div> ...</div><div> class ExecutionContext:</div><div> ...<br></div><div><br></div><div> class ContextVar:</div><div> def __init__(self, name):</div><div> self._key = ContextKey(name)</div><div><br></div><div> def get(self):</div><div> return get_execution_context()[self._<wbr>key]<br></div><div> </div><div><div> def set(self, value):</div><div> get_execution_context()[self._<wbr>key] = value<br></div></div></div><div class="gmail_quote"><br></div><div class="gmail_quote"><div><div> def delete(self, value):</div><div> del get_execution_context()[self._<wbr>key]</div></div></div></div></div></blockquote><div><br></div><div>Why would we need this extra layer? I would assume that the key can just be the ContextVar object itself, e.g.</div><div><br></div><div> return get_execution_context()[self]</div><div><br></div><div>or</div><div><br></div><div> get_execution_context()[self] = value<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div>While I'd defer to Yury on the technical feasibility, I'd expect that version could probably be made to work *if* you were amenable to some of the mapping methods on the execution context raising RuntimeError in order to avoid locking ourselves in to particular design decisions before we're ready to make them.</div><div><br></div><div>The reason I say that is because one of the biggest future-proofing concerns when it comes to exposing a mapping as the lowest API layer is that it makes the following code pattern possible:</div><div><br></div><div> ec = get_execution_context()</div><div> # Change to a different execution context</div><div> ec[key] = new_value</div><div><br></div></div><div>The appropriate semantics for that case (modifying a context that isn't the currently active one) are *really* unclear, which is why PEP 550 structures the API to prevent it (context variables can only manipulate the active context, not arbitrary contexts).</div></div></div></div></blockquote><div><br></div><div>But why on earth would you want to prevent that? If there's some caching involved that I have overlooked (another problem with the complexity of the design, or perhaps the explanation), couldn't mutating the non-context simply set a dirty bit to ensure that if it ever gets made the current context again the cache must be considered invalidated?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>However, even with a mapping at the lowest layer, a similar API constraint could still be introduced via a runtime guard in the mutation methods:</div><div><br></div><div> if get_execution_context() is not self:</div><div> raise RuntimeError("Cannot modify an inactive execution context")</div><div><br></div><div>That way, to actually mutate a different context, you'd still have to switch contexts, just as you have to switch threads in C if you want to modify another thread's thread specific storage.</div></div></div></div></blockquote><div><br></div><div>But that sounds really perverse. If anything, modifying an EC that's not any thread's current context should be *simpler* than modifying the current context. (I'm okay with a prohibition on modifying another *thread's* current context.)<br></div></div><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div></div>