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

2017-10-18 Thread Nick Coghlan
On 19 October 2017 at 04:53, Guido van Rossum  wrote:

> Actually after recent debate  I think
> this PEP should *not* be provisional.
>

+1 from me - "contextvars._set_ctx()" is the only part I think we're really
unsure about in your latest API design, and marking that specific function
as private will cover the fact that its semantics aren't guaranteed yet.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Guido van Rossum
Actually after recent debate  I think
this PEP should *not* be provisional.

On Wed, Oct 18, 2017 at 11:45 AM, Yury Selivanov 
wrote:

> On Wed, Oct 18, 2017 at 2:21 PM, Guido van Rossum 
> wrote:
> > On Wed, Oct 18, 2017 at 10:50 AM, Yury Selivanov <
> yselivanov...@gmail.com>
> > wrote:
> >>
> >> The main reason why I don't like 'set_ctx()' is because it would make
> >> it harder for us to adopt PEP 550-like design later in the future
> >> (*if* we need that.)
> >>
> >> PEP 550 is designed in such a way, that 'generator.send()' is the only
> >> thing that can control the actual stack of LCs.  If users can call
> >> 'set_ctx' themselves, it means that it's no longer safe for
> >> 'generator.send()' to simply pop the topmost LC from the stack.  This
> >> can be worked around, potentially, but the we don't actually need
> >> 'set_ctx' in asyncio or in any other async framework.  There is simply
> >> no hard motivation to have it.  That's why I'd like to have just
> >> Context.run(), because it's sufficient, and it doesn't burn the bridge
> >> to PEP 550-like design.
> >
> >
> > Honestly that stack-popping in send() always felt fragile to me, so I'd
> be
> > happy if we didn't need to depend on it.
> >
> > That said I'm okay with presenting set_ctx() *primarily* as an
> educational
> > tool for showing how Context.run() works. We could name it _set_ctx() and
> > add a similar note as we have for sys._getframe(), basically keeping the
> > door open for future changes that may render it non-functional without
> > worries about backward compatibility (and without invoking the notion of
> > "provisional" API).
>
> '_set_ctx()' + documentation bits work for me.  I also assume that if
> you accept the PEP, you do it provisionally, right?  That should make
> it possible for us to *slightly* tweak the
> implementation/API/semantics in 3.8 if needed.
>
> > There's no problem with get_ctx() right?
>
> Yes, 'get_ctx()' is absolutely fine.  We still need it for async
> tasks/callbacks.
>
> Yury
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Yury Selivanov
On Wed, Oct 18, 2017 at 2:21 PM, Guido van Rossum  wrote:
> On Wed, Oct 18, 2017 at 10:50 AM, Yury Selivanov 
> wrote:
>>
>> The main reason why I don't like 'set_ctx()' is because it would make
>> it harder for us to adopt PEP 550-like design later in the future
>> (*if* we need that.)
>>
>> PEP 550 is designed in such a way, that 'generator.send()' is the only
>> thing that can control the actual stack of LCs.  If users can call
>> 'set_ctx' themselves, it means that it's no longer safe for
>> 'generator.send()' to simply pop the topmost LC from the stack.  This
>> can be worked around, potentially, but the we don't actually need
>> 'set_ctx' in asyncio or in any other async framework.  There is simply
>> no hard motivation to have it.  That's why I'd like to have just
>> Context.run(), because it's sufficient, and it doesn't burn the bridge
>> to PEP 550-like design.
>
>
> Honestly that stack-popping in send() always felt fragile to me, so I'd be
> happy if we didn't need to depend on it.
>
> That said I'm okay with presenting set_ctx() *primarily* as an educational
> tool for showing how Context.run() works. We could name it _set_ctx() and
> add a similar note as we have for sys._getframe(), basically keeping the
> door open for future changes that may render it non-functional without
> worries about backward compatibility (and without invoking the notion of
> "provisional" API).

'_set_ctx()' + documentation bits work for me.  I also assume that if
you accept the PEP, you do it provisionally, right?  That should make
it possible for us to *slightly* tweak the
implementation/API/semantics in 3.8 if needed.

> There's no problem with get_ctx() right?

Yes, 'get_ctx()' is absolutely fine.  We still need it for async
tasks/callbacks.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Guido van Rossum
On Wed, Oct 18, 2017 at 10:50 AM, Yury Selivanov 
wrote:

> The main reason why I don't like 'set_ctx()' is because it would make
> it harder for us to adopt PEP 550-like design later in the future
> (*if* we need that.)
>
> PEP 550 is designed in such a way, that 'generator.send()' is the only
> thing that can control the actual stack of LCs.  If users can call
> 'set_ctx' themselves, it means that it's no longer safe for
> 'generator.send()' to simply pop the topmost LC from the stack.  This
> can be worked around, potentially, but the we don't actually need
> 'set_ctx' in asyncio or in any other async framework.  There is simply
> no hard motivation to have it.  That's why I'd like to have just
> Context.run(), because it's sufficient, and it doesn't burn the bridge
> to PEP 550-like design.
>

Honestly that stack-popping in send() always felt fragile to me, so I'd be
happy if we didn't need to depend on it.

That said I'm okay with presenting set_ctx() *primarily* as an educational
tool for showing how Context.run() works. We could name it _set_ctx() and
add a similar note as we have for sys._getframe(), basically keeping the
door open for future changes that may render it non-functional without
worries about backward compatibility (and without invoking the notion of
"provisional" API).

There's no problem with get_ctx() right?

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Yury Selivanov
On Wed, Oct 18, 2017 at 2:10 PM, Ethan Furman  wrote:
[..]
> Unless it's extremely difficult to not seg-fault in such a situation I don't
> think this is a valid argument.

Well, you don't think so, but I do, after writing a few
implementations of this PEP family.  It would complicate the design,
but the function isn't even needed, strictly speaking.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Ethan Furman

On 10/18/2017 10:50 AM, Yury Selivanov wrote:

On Wed, Oct 18, 2017 at 1:06 PM, Guido van Rossum  wrote:

On Tue, Oct 17, 2017 at 9:40 PM, Nick Coghlan  wrote:

[..]

By contrast, "contextvars.set_ctx" would need various wrappers to handle
correctly reverting the context change, and would hence be prone to "changed
the active context without changing it back" bugs (which can be especially
fun when you're dealing with a shared pool of worker threads or processes).



So let's have both.


The main reason why I don't like 'set_ctx()' is because it would make
it harder for us to adopt PEP 550-like design later in the future
(*if* we need that.)

PEP 550 is designed in such a way, that 'generator.send()' is the only
thing that can control the actual stack of LCs.  If users can call
'set_ctx' themselves, it means that it's no longer safe for
'generator.send()' to simply pop the topmost LC from the stack.


I don't see why this is a concern -- Python is a "consenting adults" language.  If users decide to start mucking around 
with advanced behavior and something breaks, well, they own all the pieces! ;)


Unless it's extremely difficult to not seg-fault in such a situation I don't 
think this is a valid argument.

--
~Ethan~
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Yury Selivanov
On Wed, Oct 18, 2017 at 1:06 PM, Guido van Rossum  wrote:
> On Tue, Oct 17, 2017 at 9:40 PM, Nick Coghlan  wrote:
[..]
>> By contrast, "contextvars.set_ctx" would need various wrappers to handle
>> correctly reverting the context change, and would hence be prone to "changed
>> the active context without changing it back" bugs (which can be especially
>> fun when you're dealing with a shared pool of worker threads or processes).
>
>
> So let's have both.

The main reason why I don't like 'set_ctx()' is because it would make
it harder for us to adopt PEP 550-like design later in the future
(*if* we need that.)

PEP 550 is designed in such a way, that 'generator.send()' is the only
thing that can control the actual stack of LCs.  If users can call
'set_ctx' themselves, it means that it's no longer safe for
'generator.send()' to simply pop the topmost LC from the stack.  This
can be worked around, potentially, but the we don't actually need
'set_ctx' in asyncio or in any other async framework.  There is simply
no hard motivation to have it.  That's why I'd like to have just
Context.run(), because it's sufficient, and it doesn't burn the bridge
to PEP 550-like design.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Guido van Rossum
On Tue, Oct 17, 2017 at 9:40 PM, Nick Coghlan  wrote:

> On 18 October 2017 at 05:55, Yury Selivanov 
> wrote:
>
>> I actually like what you did in
>> https://github.com/gvanrossum/pep550/blob/master/simpler.py, it seems
>> reasonable.  The only thing that I'd change is to remove "set_ctx"
>> from the public API and add "Context.run(callable)".  This makes the
>> API more flexible to potential future changes and amendments.
>>
>
> Yep, with that tweak, I like Guido's suggested API as well.
>

I've added the suggested Context.run() method.

>
> Attempting to explain why I think we want "Context.run(callable)" rather
> "context_vars.set_ctx()" by drawing an analogy to thread local storage:
>
> 1. In C, the compiler & CPU work together to ensure you can't access
> another thread's thread locals.
>

But why is that so important? I wouldn't recommend doing it, but it might
be handy for a debugger to be able to inspect a thread's thread-locals. As
it is, it seems a debugger can only access thread-locals for the thread in
which the debugger itself runs. It has better access to the real locals on
the thread's stack of frames!


> 2. In Python's thread locals API, we do the same thing: you can only get
> access to the running thread's thread locals, not anyone else's
>

But there's no real benefit in this. In C, I could imagine a compiler
optimizing access to thread-locals, but in Python that's moot.


> At the Python API layer, we don't expose the ability to switch explicitly
> to another thread state while remaining within the current function.
> Instead, we only offer two options: starting a new thread, and waiting for
> a thread to finish execution. The lifecycle of the thread local storage is
> then intrinsically linked to the lifecycle of the thread it belongs to.
>

To me this feels more a side-effect of the implementation (perhaps
inherited from C's implementation) than an intentional design.

To be clear, I think it's totally fine for *clients* of the ContextVar API
-- e.g. numpy or decimal -- to assume that their context doesn't change
arbitrarily while they're happily executing in a single frame or calling
stuff they trust not to change the context. (IOW all changes to a
particular ContextVar would be through that ContextVar object, not through
behind-the-scenes manipulation of the thread's current context).

But for *frameworks* (e.g. asyncio or Twisted) I find it simpler to think
about the context in terms of `set_ctx` and `get_ctx`, and I worry that
*hiding* these might block off certain API design patterns that some
framework might want to use -- who knows, maybe Nathaniel (who is fond of
`with`
)
might come up with a context manager to run a block of code in a different
context (perhaps cloned from the current one).


> That intrinsic link makes various aspects of thread local storage easier
> to reason about, since the active thread state can't change in the middle
> of a running function - even if the current thread gets suspended by the
> OS, resuming the function also implies resuming the original thread.
>

I don't feel reasoning would be much impaired. When reasoning about code we
make assumptions that are theoretically unsafe all the time (e.g. "nobody
will move the clock back").


> Including a "contextvars.set_ctx" API would be akin to making
> PyThreadState_Swap a public Python-level API, rather than only exposing
> _thread.start_new_thread the way we do now.
>

It's different for threads, because they are the bedrock of execution, and
nobody is interested in implementing their own threading framework that
doesn't build on this same bedrock.


> One reason we *don't* do that is because it would make thread locals much
> harder to reason about - every function call could have an implicit side
> effect of changing the active thread state, which would mean the thread
> locals at the start of the function could differ from those at the end of
> the function, even if the function itself didn't do anything to change them.
>

Hm. Threads are still hard to reason about, because for everything *but*
thread-locals there is always the possibility that it's being mutated by
another thread... So I don't think we should get our knickers twisted over
thread-local variables.


> Only offering Context.run(callable) provides a similar "the only changes
> to the execution context will be those this function, or a function it
> called, explicitly initiates" protection for context variables, and Guido's
> requested API simplifications make this aspect even easier to reason about:
> after any given function call, you can be certain of being back in the
> context you started in, because we wouldn't expose any Python level API
> that allowed an execution context switch to persist beyond the frame that
> initiated it.
>

And as long as you're not calling something that's a specific 

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

2017-10-18 Thread Nick Coghlan
On 18 October 2017 at 16:25, Ethan Furman  wrote:

> On 10/17/2017 09:40 PM, Nick Coghlan wrote:
>
>> At the Python API layer, we don't expose the ability to switch explicitly
>> to another thread state while remaining within
>> the current function. Instead, we only offer two options: starting a new
>> thread, and waiting for a thread to finish
>> execution. The lifecycle of the thread local storage is then
>> intrinsically linked to the lifecycle of the thread it
>> belongs to.
>>
>
> I seem to remember mention about frameworks being able to modify contexts
> for various tasks/coroutines; if the framework cannot create and switch to
> a new context how will it set them up?  Or did I misunderstand?


That's what Context.run() will handle.

>From a usage perspective, what Yury and I are suggesting is that switching
execution to a new context should always implicitly switch back when the
called operation returns:

def run_in_isolated_ec(operation):
ec = contextvars.new_context()
return ec.run(operation)

That's safe and simple to use, and integrates nicely with the APIs for
resuming coroutines (next(cr), cr.__next__(), cr.send(), cr.throw()).

By contrast, exposing set_ctx() directly puts the responsibility for
reverting the active context back to the initial one on the caller:

def run_in_isolated_ec(operation):
ec = contextvars.new_context()
initial_ec = contextvar.get_ctx()
contextvars.set_ctx(ec)
try:
return operation()
finally:
# What happens if we forget to revert back to the previous
context here?
contextvars.set_ctx(initial_ec)

While the contextvars implementation is going to need a context switching
capability like that internally in order to implement ec.run() (just as the
threading implementation's C API includes PyThreadState_Swap), we don't
currently have a use case for exposing it as a Python level API.

And if we don't expose an API that allows it, then we can delay specifying
what effect the following code should have all the calling function or
coroutine (or whether it's a runtime error):

def buggy_run_in_isolated_ec(operation):
ec = contextvars.new_context()
contextvars.set_ctx(ec)
return operation()
# Oops, forgot to revert the active context to the one we were
called with

So if we start out only exposing "Context.run()" at the Python layer
(covering all currently known use cases), then any subsequent addition of
an in-place context switching API can be guided by specific examples of
situations where Context.run() isn't sufficient.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-18 Thread Ethan Furman

On 10/17/2017 09:40 PM, Nick Coghlan wrote:

On 18 October 2017 at 05:55, Yury Selivanov wrote:



I actually like what you did in
https://github.com/gvanrossum/pep550/blob/master/simpler.py
, it seems
reasonable.  The only thing that I'd change is to remove "set_ctx"
from the public API and add "Context.run(callable)".  This makes the
API more flexible to potential future changes and amendments.


Yep, with that tweak, I like Guido's suggested API as well.


Attempting to explain why I think we want "Context.run(callable)" rather 
"context_vars.set_ctx()" by drawing an analogy
to thread local storage:

1. In C, the compiler & CPU work together to ensure you can't access another 
thread's thread locals.
2. In Python's thread locals API, we do the same thing: you can only get access 
to the running thread's thread locals,
not anyone else's

At the Python API layer, we don't expose the ability to switch explicitly to 
another thread state while remaining within
the current function. Instead, we only offer two options: starting a new 
thread, and waiting for a thread to finish
execution. The lifecycle of the thread local storage is then intrinsically 
linked to the lifecycle of the thread it
belongs to.


I seem to remember mention about frameworks being able to modify contexts for various tasks/coroutines; if the framework 
cannot create and switch to a new context how will it set them up?  Or did I misunderstand?


--
~Ethan~
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Nick Coghlan
On 18 October 2017 at 05:55, Yury Selivanov  wrote:

> On Tue, Oct 17, 2017 at 2:25 PM, Guido van Rossum 
> wrote:
> > On Tue, Oct 17, 2017 at 8:54 AM, Yury Selivanov  >
> [..]
> >> My way of thinking about this: "get_execution_context()" returns you a
> >> shallow copy of the current EC (at least conceptually).  So making any
> >> modifications on it won't affect the current environment.  The only
> >> way to actually apply the modified EC object to the environment will
> >> be its 'run(callable)' method.
> >
> >
> > I understand that you don't want to throw away the implementation work
> > you've already done. But I find that the abstractions you've introduced
> are
> > getting in the way of helping people understand what they can do with
> > context variables, and I really want to go back to a model that is *much*
> > closer to understanding how instance variables are just self.__dict__.
> (Even
> > though there are possible complications due to __slots__ and @property.)
>
> I don't really care about the implementation work that has already
> been done, it's OK if I write it from scratch again.
>
> I actually like what you did in
> https://github.com/gvanrossum/pep550/blob/master/simpler.py, it seems
> reasonable.  The only thing that I'd change is to remove "set_ctx"
> from the public API and add "Context.run(callable)".  This makes the
> API more flexible to potential future changes and amendments.
>

Yep, with that tweak, I like Guido's suggested API as well.


Attempting to explain why I think we want "Context.run(callable)" rather
"context_vars.set_ctx()" by drawing an analogy to thread local storage:

1. In C, the compiler & CPU work together to ensure you can't access
another thread's thread locals.
2. In Python's thread locals API, we do the same thing: you can only get
access to the running thread's thread locals, not anyone else's

At the Python API layer, we don't expose the ability to switch explicitly
to another thread state while remaining within the current function.
Instead, we only offer two options: starting a new thread, and waiting for
a thread to finish execution. The lifecycle of the thread local storage is
then intrinsically linked to the lifecycle of the thread it belongs to.

That intrinsic link makes various aspects of thread local storage easier to
reason about, since the active thread state can't change in the middle of a
running function - even if the current thread gets suspended by the OS,
resuming the function also implies resuming the original thread.

Including a "contextvars.set_ctx" API would be akin to making
PyThreadState_Swap a public Python-level API, rather than only exposing
_thread.start_new_thread the way we do now.

One reason we *don't* do that is because it would make thread locals much
harder to reason about - every function call could have an implicit side
effect of changing the active thread state, which would mean the thread
locals at the start of the function could differ from those at the end of
the function, even if the function itself didn't do anything to change them.

Only offering Context.run(callable) provides a similar "the only changes to
the execution context will be those this function, or a function it called,
explicitly initiates" protection for context variables, and Guido's
requested API simplifications make this aspect even easier to reason about:
after any given function call, you can be certain of being back in the
context you started in, because we wouldn't expose any Python level API
that allowed an execution context switch to persist beyond the frame that
initiated it.



The above is my main rationale for preferring contextvars.Context.run() to
contextvars.set_ctx(), but it's not the only reason I prefer it.

At a more abstract design philosophy level, I think the distinction between
symmetric and asymmetric coroutines is relevant here [2]:

* in symmetric coroutines, there's a single operation that says "switch to
running this other coroutine"
* in asymmetric coroutines, there are separate operations for starting or
resuming coroutine and for suspending the currently running one

Python's native coroutines are asymmetric - we don't provide a "switch to
this coroutine" primitive, we instead provide an API for starting or
resuming a coroutine (via cr.__next__(), cr.send() & cr.throw()), and an
API for suspending one (via await).

The contextvars.set_ctx() API would be suitable for symmetric coroutines,
as there's no implied notion of parent context/child context, just a notion
of switching which context is active.

The Context.run() API aligns better with asymmetric coroutines, as there's
a clear distinction between the parent frame (the one initiating the
context switch) and the child frame (the one running in the designated
context).

As a practical matter, Context.run also composes nicely (in combination
with functools.partial) for use with any existing 

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

2017-10-17 Thread Guido van Rossum
On Tue, Oct 17, 2017 at 12:51 PM, Nathaniel Smith  wrote:

> On Oct 17, 2017 11:25 AM, "Guido van Rossum"  wrote:
>
>
> In short, I really don't think there's a need for context variables to be
> faster than instance variables.
>
>
> There really is: currently the cost of looking up a thread local through
> the C API is a dict lookup, which is faster than instance variable lookup,
> and decimal and numpy have both found that that's already too expensive.
>

(At first I found this hard to believe, but then I realized that decimal
and numpy presumably access these from C code where a typical operation
like Decimal.__add__ is much faster than a dict lookup. So point taken.)


> Or maybe you're just talking about the speed when the cache misses, in
> which case never mind :-).
>

I'm happy to support caching the snot out of this, but it seems we agree
that the "semantics" can be specified without taking the caching into
account, and that's what I'm after. I presume that each ContextVar object
will have one cached value? Because that's what PEP 550 currently
specifies. Surely it wouldn't be hard for a direct __setitem__ (or
__delitem__) call to cause the cache to be invalidated, regardless of
whether the affected context is a *current* context or not (a bunch of
different approaches suggest themselves).Oother mutating methods can be
implemented in terms of __setitem__ (or __delitem__), and I don't care for
them to be as fast as a cached lookup.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Yury Selivanov
On Tue, Oct 17, 2017 at 2:25 PM, Guido van Rossum  wrote:
> On Tue, Oct 17, 2017 at 8:54 AM, Yury Selivanov 
[..]
>> My way of thinking about this: "get_execution_context()" returns you a
>> shallow copy of the current EC (at least conceptually).  So making any
>> modifications on it won't affect the current environment.  The only
>> way to actually apply the modified EC object to the environment will
>> be its 'run(callable)' method.
>
>
> I understand that you don't want to throw away the implementation work
> you've already done. But I find that the abstractions you've introduced are
> getting in the way of helping people understand what they can do with
> context variables, and I really want to go back to a model that is *much*
> closer to understanding how instance variables are just self.__dict__. (Even
> though there are possible complications due to __slots__ and @property.)

I don't really care about the implementation work that has already
been done, it's OK if I write it from scratch again.

I actually like what you did in
https://github.com/gvanrossum/pep550/blob/master/simpler.py, it seems
reasonable.  The only thing that I'd change is to remove "set_ctx"
from the public API and add "Context.run(callable)".  This makes the
API more flexible to potential future changes and amendments.


> In short, I really don't think there's a need for context variables to be
> faster than instance variables.

Well, even our current idea about the API, doesn't really prohibit us
from adding a cache to ContextVar.get().  That would be an
implementation detail, right?  Same as our latest optimization for
creating bound methods (CALL_METHOD & LOAD_METHOD opcodes), which
avoids creating bound method instances when it's OK not to.


Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Nathaniel Smith
On Oct 17, 2017 11:25 AM, "Guido van Rossum"  wrote:


In short, I really don't think there's a need for context variables to be
faster than instance variables.


There really is: currently the cost of looking up a thread local through
the C API is a dict lookup, which is faster than instance variable lookup,
and decimal and numpy have both found that that's already too expensive.

Or maybe you're just talking about the speed when the cache misses, in
which case never mind :-).

-n
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Guido van Rossum
Also, IMO this is all the interface we should need to explain to users
(even framework authors):
https://github.com/gvanrossum/pep550/blob/master/simpler.py

On Tue, Oct 17, 2017 at 11:25 AM, Guido van Rossum  wrote:

> On Tue, Oct 17, 2017 at 8:54 AM, Yury Selivanov 
> wrote:
>
>> On Tue, Oct 17, 2017 at 1:02 AM, Nick Coghlan  wrote:
>> > 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:
>> >
>> > ec = get_execution_context()
>> > # Change to a different execution context
>> > ec[key] = new_value
>>
>> I *really* don't want to make ECs behave like 'locals()'.  That will
>> make everything way more complicated.
>>
>
> At least some of the problems with locals() have more to do with the
> legacy of that function than with inherent difficulties. And local
> variables might be optimized by a JIT in a way that context vars never will
> be (or at least if we ever get to that point we will be able to redesign
> the API first).
>
>
>> My way of thinking about this: "get_execution_context()" returns you a
>> shallow copy of the current EC (at least conceptually).  So making any
>> modifications on it won't affect the current environment.  The only
>> way to actually apply the modified EC object to the environment will
>> be its 'run(callable)' method.
>>
>
> I understand that you don't want to throw away the implementation work
> you've already done. But I find that the abstractions you've introduced are
> getting in the way of helping people understand what they can do with
> context variables, and I really want to go back to a model that is *much*
> closer to understanding how instance variables are just self.__dict__.
> (Even though there are possible complications due to __slots__ and
> @property.)
>
> In short, I really don't think there's a need for context variables to be
> faster than instance variables.
>
> --
> --Guido van Rossum (python.org/~guido)
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Guido van Rossum
On Tue, Oct 17, 2017 at 8:54 AM, Yury Selivanov 
wrote:

> On Tue, Oct 17, 2017 at 1:02 AM, Nick Coghlan  wrote:
> > 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:
> >
> > ec = get_execution_context()
> > # Change to a different execution context
> > ec[key] = new_value
>
> I *really* don't want to make ECs behave like 'locals()'.  That will
> make everything way more complicated.
>

At least some of the problems with locals() have more to do with the legacy
of that function than with inherent difficulties. And local variables might
be optimized by a JIT in a way that context vars never will be (or at least
if we ever get to that point we will be able to redesign the API first).


> My way of thinking about this: "get_execution_context()" returns you a
> shallow copy of the current EC (at least conceptually).  So making any
> modifications on it won't affect the current environment.  The only
> way to actually apply the modified EC object to the environment will
> be its 'run(callable)' method.
>

I understand that you don't want to throw away the implementation work
you've already done. But I find that the abstractions you've introduced are
getting in the way of helping people understand what they can do with
context variables, and I really want to go back to a model that is *much*
closer to understanding how instance variables are just self.__dict__.
(Even though there are possible complications due to __slots__ and
@property.)

In short, I really don't think there's a need for context variables to be
faster than instance variables.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Yury Selivanov
On Tue, Oct 17, 2017 at 1:02 AM, Nick Coghlan  wrote:
> On 17 October 2017 at 14:31, Guido van Rossum  wrote:
>>
>> 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!).
>
>
> 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:
>
> class ContextKey:
> ...
> class ExecutionContext:
> ...
>
> class ContextVar:
> def __init__(self, name):
> self._key = ContextKey(name)
>
> def get(self):
> return get_execution_context()[self._key]
>
> def set(self, value):
> get_execution_context()[self._key] = value
>
> def delete(self, value):
> del get_execution_context()[self._key]

ContextVar itself will be hashable, we don't need ContextKeys.

>
> 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.
>
> 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:
>
> ec = get_execution_context()
> # Change to a different execution context
> ec[key] = new_value

I *really* don't want to make ECs behave like 'locals()'.  That will
make everything way more complicated.

My way of thinking about this: "get_execution_context()" returns you a
shallow copy of the current EC (at least conceptually).  So making any
modifications on it won't affect the current environment.  The only
way to actually apply the modified EC object to the environment will
be its 'run(callable)' method.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-17 Thread Guido van Rossum
On Mon, Oct 16, 2017 at 10:02 PM, Nick Coghlan  wrote:

> On 17 October 2017 at 14:31, Guido van Rossum  wrote:
>
>> 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!).
>>
>
> 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:
>
> class ContextKey:
> ...
> class ExecutionContext:
> ...
>
> class ContextVar:
> def __init__(self, name):
> self._key = ContextKey(name)
>
> def get(self):
> return get_execution_context()[self._key]
>
> def set(self, value):
> get_execution_context()[self._key] = value
>
> def delete(self, value):
> del get_execution_context()[self._key]
>

Why would we need this extra layer? I would assume that the key can just be
the ContextVar object itself, e.g.

return get_execution_context()[self]

or

get_execution_context()[self] = value


> 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.
>
> 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:
>
> ec = get_execution_context()
> # Change to a different execution context
> ec[key] = new_value
>
> 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).
>

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?


> 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:
>
> if get_execution_context() is not self:
> raise RuntimeError("Cannot modify an inactive execution context")
>
> 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.
>

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.)

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nick Coghlan
On 17 October 2017 at 15:02, Nick Coghlan  wrote:

> On 17 October 2017 at 14:31, Guido van Rossum  wrote:
>
>> 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!).
>>
>
> 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:
>
> class ContextKey:
> ...
> class ExecutionContext:
> ...
>
> class ContextVar:
> def __init__(self, name):
> self._key = ContextKey(name)
>
> def get(self):
> return get_execution_context()[self._key]
>
> def set(self, value):
> get_execution_context()[self._key] = value
>
> def delete(self, value):
> del get_execution_context()[self._key]
>

Tangent: if we do go this way, it actually maps pretty nicely to the idea
of a "threading.ThreadVar" API that wraps threading.local():

class ThreadVar:
def __init__(self, name):
self._name = name
self._storage = threading.local()

def get(self):
return self._storage.value

def set(self, value):
self._storage.value = value

def delete(self):
del self._storage.value

(Note: real implementations of either idea would need to pay more attention
to producing clear exception messages and instance representations)

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nick Coghlan
On 17 October 2017 at 14:31, Guido van Rossum  wrote:

> 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!).
>

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:

class ContextKey:
...
class ExecutionContext:
...

class ContextVar:
def __init__(self, name):
self._key = ContextKey(name)

def get(self):
return get_execution_context()[self._key]

def set(self, value):
get_execution_context()[self._key] = value

def delete(self, value):
del get_execution_context()[self._key]

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.

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:

ec = get_execution_context()
# Change to a different execution context
ec[key] = new_value

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).

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:

if get_execution_context() is not self:
raise RuntimeError("Cannot modify an inactive execution context")

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.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Guido van Rossum
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!).

On Mon, Oct 16, 2017 at 9:09 PM, Nick Coghlan  wrote:

> On 17 October 2017 at 03:00, Guido van Rossum  wrote:
>
>> On Mon, Oct 16, 2017 at 9:11 AM, Yury Selivanov 
>> 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
> variables")
> key.set(value)
> def __delitem__(self, key):
> key.delete()
>
> # The key set would be the context vars assigned in the active
> context
> 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()
> builtin
> # 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
> time.
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nick Coghlan
On 17 October 2017 at 03:00, Guido van Rossum  wrote:

> On Mon, Oct 16, 2017 at 9:11 AM, Yury Selivanov 
> 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
variables")
key.set(value)
def __delitem__(self, key):
key.delete()

# The key set would be the context vars assigned in the active
context
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()
builtin
# 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
time.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Guido van Rossum
Hm. I really like the idea that you can implement and demonstrate all of
ContextVar by manipulating the underlying mapping. And surely compared to
the effort of implementing the HAMT itself (including all its edge cases)
surely implementing the mutable mapping API should be considered
recreational programming.

On Mon, Oct 16, 2017 at 5:57 PM, Nathaniel Smith  wrote:

> On Mon, Oct 16, 2017 at 8:49 AM, Guido van Rossum 
> wrote:
> > On Sun, Oct 15, 2017 at 10:26 PM, Nathaniel Smith  wrote:
> >>
> >> On Sun, Oct 15, 2017 at 10:10 PM, Guido van Rossum 
> >> wrote:
> >> > Yes, that's what I meant by "ignoring generators". And I'd like there
> to
> >> > be
> >> > a "current context" that's a per-thread MutableMapping with ContextVar
> >> > keys.
> >> > Maybe there's not much more to it apart from naming the APIs for
> getting
> >> > and
> >> > setting it? To be clear, I am fine with this being a specific subtype
> of
> >> > MutableMapping. But I don't see much benefit in making it more
> abstract
> >> > than
> >> > that.
> >>
> >> We don't need it to be abstract (it's fine to have a single concrete
> >> mapping type that we always use internally), but I think we do want it
> >> to be opaque (instead of exposing the MutableMapping interface, the
> >> only way to get/set specific values should be through the ContextVar
> >> interface). The advantages are:
> >>
> >> - This allows C level caching of values in ContextVar objects (in
> >> particular, funneling mutations through a limited API makes cache
> >> invalidation *much* easier)
> >
> >
> > Well the MutableMapping could still be a proxy or something that
> invalidates
> > the cache when mutated. That's why I said it should be a single concrete
> > mapping type. (It also doesn't have to derive from MutableMapping -- it's
> > sufficient for it to be a duck type for one, or perhaps some Python-level
> > code could `register()` it.
>
> MutableMapping is just a really complicated interface -- you have to
> deal with iterator invalidation and popitem and implementing view
> classes and all that. It seems like a lot of code for a feature that
> no-one seems to worry about missing right now. (In fact, I suspect the
> extra code required to implement the full MutableMapping interface on
> top of a basic HAMT type is larger than the extra code to implement
> the current PEP 550 draft's chaining semantics on top of this proposal
> for a minimal PEP 550.)
>
> What do you think of something like:
>
> class Context:
> def __init__(self, /, init: MutableMapping[ContextVar,object] = {}):
> ...
>
> def as_dict(self) -> Dict[ContextVar, object]:
> "Returns a snapshot of the internal state."
>
> def copy(self) -> Context:
> "Equivalent to (but maybe faster than) Context(self.as_dict())."
>
> I like the idea of making it possible to set up arbitrary Contexts and
> introspect them, because sometimes you do need to debug weird issues
> or do some wacky stuff deep in the guts of a coroutine scheduler, but
> this would give us that without implementing MutableMapping's 17
> methods and 7 helper classes.
>
> -n
>
> --
> Nathaniel J. Smith -- https://vorpus.org
>



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Ethan Furman

On 10/16/2017 05:29 PM, Nathaniel Smith wrote:

On Mon, Oct 16, 2017 at 11:12 AM, Ethan Furman wrote:



What would be really nice is to have attribute access like thread locals.
Instead of working with individual ContextVars you grab the LocalContext and
access the vars as attributes.  I don't recall reading in the PEP why this
is a bad idea.


You're mixing up levels -- the way threading.local objects work is
that there's one big dict that's hidden inside the interpreter (in the
ThreadState), and it holds a separate little dict for each
threading.local. The dict holding ContextVars is similar to the big
dict; a threading.local itself is like a ContextVar that holds a dict.
(And the reason it's this way is that it's easy to build either
version on top of the other, and we did some survey of threading.local
usage and the ContextVar style usage was simpler in the majority of
cases.)

For threading.local there's no way to get at the big dict at all from
Python; it's hidden inside the C APIs and threading internals. I'm
guessing you've never missed this :-). For ContextVars we can't hide
it that much, because async frameworks need to be able to swap the
current dict when switching tasks and clone it when starting a new
task, but those are the only absolutely necessary operations.


Ah, thank you.

--
~Ethan~

___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nathaniel Smith
On Mon, Oct 16, 2017 at 8:49 AM, Guido van Rossum  wrote:
> On Sun, Oct 15, 2017 at 10:26 PM, Nathaniel Smith  wrote:
>>
>> On Sun, Oct 15, 2017 at 10:10 PM, Guido van Rossum 
>> wrote:
>> > Yes, that's what I meant by "ignoring generators". And I'd like there to
>> > be
>> > a "current context" that's a per-thread MutableMapping with ContextVar
>> > keys.
>> > Maybe there's not much more to it apart from naming the APIs for getting
>> > and
>> > setting it? To be clear, I am fine with this being a specific subtype of
>> > MutableMapping. But I don't see much benefit in making it more abstract
>> > than
>> > that.
>>
>> We don't need it to be abstract (it's fine to have a single concrete
>> mapping type that we always use internally), but I think we do want it
>> to be opaque (instead of exposing the MutableMapping interface, the
>> only way to get/set specific values should be through the ContextVar
>> interface). The advantages are:
>>
>> - This allows C level caching of values in ContextVar objects (in
>> particular, funneling mutations through a limited API makes cache
>> invalidation *much* easier)
>
>
> Well the MutableMapping could still be a proxy or something that invalidates
> the cache when mutated. That's why I said it should be a single concrete
> mapping type. (It also doesn't have to derive from MutableMapping -- it's
> sufficient for it to be a duck type for one, or perhaps some Python-level
> code could `register()` it.

MutableMapping is just a really complicated interface -- you have to
deal with iterator invalidation and popitem and implementing view
classes and all that. It seems like a lot of code for a feature that
no-one seems to worry about missing right now. (In fact, I suspect the
extra code required to implement the full MutableMapping interface on
top of a basic HAMT type is larger than the extra code to implement
the current PEP 550 draft's chaining semantics on top of this proposal
for a minimal PEP 550.)

What do you think of something like:

class Context:
def __init__(self, /, init: MutableMapping[ContextVar,object] = {}):
...

def as_dict(self) -> Dict[ContextVar, object]:
"Returns a snapshot of the internal state."

def copy(self) -> Context:
"Equivalent to (but maybe faster than) Context(self.as_dict())."

I like the idea of making it possible to set up arbitrary Contexts and
introspect them, because sometimes you do need to debug weird issues
or do some wacky stuff deep in the guts of a coroutine scheduler, but
this would give us that without implementing MutableMapping's 17
methods and 7 helper classes.

-n

-- 
Nathaniel J. Smith -- https://vorpus.org
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nathaniel Smith
On Mon, Oct 16, 2017 at 11:12 AM, Ethan Furman  wrote:
> What would be really nice is to have attribute access like thread locals.
> Instead of working with individual ContextVars you grab the LocalContext and
> access the vars as attributes.  I don't recall reading in the PEP why this
> is a bad idea.

You're mixing up levels -- the way threading.local objects work is
that there's one big dict that's hidden inside the interpreter (in the
ThreadState), and it holds a separate little dict for each
threading.local. The dict holding ContextVars is similar to the big
dict; a threading.local itself is like a ContextVar that holds a dict.
(And the reason it's this way is that it's easy to build either
version on top of the other, and we did some survey of threading.local
usage and the ContextVar style usage was simpler in the majority of
cases.)

For threading.local there's no way to get at the big dict at all from
Python; it's hidden inside the C APIs and threading internals. I'm
guessing you've never missed this :-). For ContextVars we can't hide
it that much, because async frameworks need to be able to swap the
current dict when switching tasks and clone it when starting a new
task, but those are the only absolutely necessary operations.

-n

-- 
Nathaniel J. Smith -- https://vorpus.org
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Ethan Furman

On 10/16/2017 09:11 AM, Yury Selivanov wrote:


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.

The key arguments for keeping ContextVar abstraction:

* Naturally avoids name clashes.

* Allows to implement efficient caching.  This is important if we want
libraries like decimal/numpy to start using it.

* Abstracts away the actual implementation of the EC.  This is a
future-proof solution, with which we can enable EC support for
generators in the future.  We already know two possible solutions (PEP
550 v1, PEP 550 current), and ContextVar is a good enough abstraction
to support both of them.

IMO ContextVar.set() and ContextVar.get() is a simple and nice API to
work with the EC.  Most people (aside framework authors) won't even
need to work with EC objects directly anyways.


Framework/library authors are users too.  Please don't make the interface 
unpleasant to use.

What would be really nice is to have attribute access like thread locals.  Instead of working with individual 
ContextVars you grab the LocalContext and access the vars as attributes.  I don't recall reading in the PEP why this is 
a bad idea.


--
~Ethan~
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Guido van Rossum
On Mon, Oct 16, 2017 at 9:53 AM, Yury Selivanov 
wrote:

> I think we can still implement context isolation in generators in
> later versions for ContextVars.  In 3.7, ContextVars will only support
> async tasks and threads.  Using them in generators will be
> *documented* as unsafe, as the context will "leak out".  Fixing
> generators in some later version of Python will then be a feature/bug
> fix.  I expect almost no backwards compatibility issue, same as I
> wouldn't expect them if we switched decimal to PEP 550 in 3.7.
>

Context also leaks into a generator. That's a feature too. Basically a
generator does not have its own context; in that respect it's no different
from a regular function call. The apparent difference is that it's possible
to call next() on a generator object from different contexts (that's always
been possible, in today's Python you can do this from multiple threads and
there's even protection against re-entering a generator frame that's
already active in another thread -- the GIL helps here of course).

I expect that any future (post-3.7) changes to how context work in
generators will have to support this as the default behavior, and to get
other behavior the generator will have to be marked or wrapped somehow.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Guido van Rossum
On Mon, Oct 16, 2017 at 9:11 AM, Yury Selivanov 
wrote:

> On Mon, Oct 16, 2017 at 11:49 AM, Guido van Rossum 
> wrote:
> > On Sun, Oct 15, 2017 at 10:26 PM, Nathaniel Smith  wrote:
> >> We don't need it to be abstract (it's fine to have a single concrete
> >> mapping type that we always use internally), but I think we do want it
> >> to be opaque (instead of exposing the MutableMapping interface, the
> >> only way to get/set specific values should be through the ContextVar
> >> interface). The advantages are:
> >>
> >> - This allows C level caching of values in ContextVar objects (in
> >> particular, funneling mutations through a limited API makes cache
> >> invalidation *much* easier)
>
> > Well the MutableMapping could still be a proxy or something that
> invalidates
> > the cache when mutated. That's why I said it should be a single concrete
> > mapping type. (It also doesn't have to derive from MutableMapping -- it's
> > sufficient for it to be a duck type for one, or perhaps some Python-level
> > code could `register()` it.
>
> Yeah, we can do a proxy.
>
> >> - It gives us flexibility to change the underlying data structure
> >> without breaking API, or for different implementations to make
> >> different choices -- in particular, it's not clear whether a dict or
> >> HAMT is better, and it's not clear whether a regular dict or
> >> WeakKeyDict is better.
>
> > I would keep it simple and supid, but WeakKeyDict is a subtype of
> > MutableMapping, and I'm sure we can find a way to implement the full
> > MutableMapping interface on top of HAMT as well.
>
> Correct.
>
> >> The first point (caching) I think is the really compelling one: in
> >> practice decimal and numpy are already using tricky caching code to
> >> reduce the overhead of accessing the ThreadState dict, and this gets
> >> even trickier with context-local state which has more cache
> >> invalidation points, so if we don't do this in the interpreter then it
> >> could actually become a blocker for adoption. OTOH it's easy for the
> >> interpreter itself to do this caching, and it makes everyone faster.
>
> > 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.


> The key arguments for keeping ContextVar abstraction:
>

To be clear, I do want to keep ContextVar!


> * Naturally avoids name clashes.
>
> * Allows to implement efficient caching.  This is important if we want
> libraries like decimal/numpy to start using it.
>
> * Abstracts away the actual implementation of the EC.  This is a
> future-proof solution, with which we can enable EC support for
> generators in the future.  We already know two possible solutions (PEP
> 550 v1, PEP 550 current), and ContextVar is a good enough abstraction
> to support both of them.
>
> IMO ContextVar.set() and ContextVar.get() is a simple and nice API to
> work with the EC.  Most people (aside framework authors) won't even
> need to work with EC objects directly anyways.
>

Sure. But (unlike you, it seems) I find it important that users can
understand their actions in terms of operations on the mapping representing
the context. Its type should be a specific class that inherits from
`MutableMapping[ContextVar, object]`.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Yury Selivanov
On Mon, Oct 16, 2017 at 7:44 AM, Nick Coghlan  wrote:
[..]
> So going down this path would lock in the *default* semantics for the
> interaction between context variables and generators as being the same as
> the interaction between thread locals and generators, but would still leave
> the door open to subsequently introducing an opt-in API like the
> "contextvars.iter_in_context" idea for cases where folks decided they wanted
> to do something different (like capturing the context at the point where
> iterator was created and then temporarily switching back to that on each
> iteration).

I think we can still implement context isolation in generators in
later versions for ContextVars.  In 3.7, ContextVars will only support
async tasks and threads.  Using them in generators will be
*documented* as unsafe, as the context will "leak out".  Fixing
generators in some later version of Python will then be a feature/bug
fix.  I expect almost no backwards compatibility issue, same as I
wouldn't expect them if we switched decimal to PEP 550 in 3.7.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Yury Selivanov
On Mon, Oct 16, 2017 at 11:49 AM, Guido van Rossum  wrote:
> On Sun, Oct 15, 2017 at 10:26 PM, Nathaniel Smith  wrote:
>>
>> On Sun, Oct 15, 2017 at 10:10 PM, Guido van Rossum 
>> wrote:
>> > Yes, that's what I meant by "ignoring generators". And I'd like there to
>> > be
>> > a "current context" that's a per-thread MutableMapping with ContextVar
>> > keys.
>> > Maybe there's not much more to it apart from naming the APIs for getting
>> > and
>> > setting it? To be clear, I am fine with this being a specific subtype of
>> > MutableMapping. But I don't see much benefit in making it more abstract
>> > than
>> > that.
>>
>> We don't need it to be abstract (it's fine to have a single concrete
>> mapping type that we always use internally), but I think we do want it
>> to be opaque (instead of exposing the MutableMapping interface, the
>> only way to get/set specific values should be through the ContextVar
>> interface). The advantages are:
>>
>> - This allows C level caching of values in ContextVar objects (in
>> particular, funneling mutations through a limited API makes cache
>> invalidation *much* easier)
>
>
> Well the MutableMapping could still be a proxy or something that invalidates
> the cache when mutated. That's why I said it should be a single concrete
> mapping type. (It also doesn't have to derive from MutableMapping -- it's
> sufficient for it to be a duck type for one, or perhaps some Python-level
> code could `register()` it.

Yeah, we can do a proxy.

>
>>
>> - It gives us flexibility to change the underlying data structure
>> without breaking API, or for different implementations to make
>> different choices -- in particular, it's not clear whether a dict or
>> HAMT is better, and it's not clear whether a regular dict or
>> WeakKeyDict is better.
>
>
> I would keep it simple and supid, but WeakKeyDict is a subtype of
> MutableMapping, and I'm sure we can find a way to implement the full
> MutableMapping interface on top of HAMT as well.

Correct.

>
>>
>> The first point (caching) I think is the really compelling one: in
>> practice decimal and numpy are already using tricky caching code to
>> reduce the overhead of accessing the ThreadState dict, and this gets
>> even trickier with context-local state which has more cache
>> invalidation points, so if we don't do this in the interpreter then it
>> could actually become a blocker for adoption. OTOH it's easy for the
>> interpreter itself to do this caching, and it makes everyone faster.
>
>
> 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.

The key arguments for keeping ContextVar abstraction:

* Naturally avoids name clashes.

* Allows to implement efficient caching.  This is important if we want
libraries like decimal/numpy to start using it.

* Abstracts away the actual implementation of the EC.  This is a
future-proof solution, with which we can enable EC support for
generators in the future.  We already know two possible solutions (PEP
550 v1, PEP 550 current), and ContextVar is a good enough abstraction
to support both of them.

IMO ContextVar.set() and ContextVar.get() is a simple and nice API to
work with the EC.  Most people (aside framework authors) won't even
need to work with EC objects directly anyways.

Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Guido van Rossum
On Sun, Oct 15, 2017 at 10:26 PM, Nathaniel Smith  wrote:

> On Sun, Oct 15, 2017 at 10:10 PM, Guido van Rossum 
> wrote:
> > Yes, that's what I meant by "ignoring generators". And I'd like there to
> be
> > a "current context" that's a per-thread MutableMapping with ContextVar
> keys.
> > Maybe there's not much more to it apart from naming the APIs for getting
> and
> > setting it? To be clear, I am fine with this being a specific subtype of
> > MutableMapping. But I don't see much benefit in making it more abstract
> than
> > that.
>
> We don't need it to be abstract (it's fine to have a single concrete
> mapping type that we always use internally), but I think we do want it
> to be opaque (instead of exposing the MutableMapping interface, the
> only way to get/set specific values should be through the ContextVar
> interface). The advantages are:
>
> - This allows C level caching of values in ContextVar objects (in
> particular, funneling mutations through a limited API makes cache
> invalidation *much* easier)
>

Well the MutableMapping could still be a proxy or something that
invalidates the cache when mutated. That's why I said it should be a single
concrete mapping type. (It also doesn't have to derive from MutableMapping
-- it's sufficient for it to be a duck type for one, or perhaps some
Python-level code could `register()` it.


> - It gives us flexibility to change the underlying data structure
> without breaking API, or for different implementations to make
> different choices -- in particular, it's not clear whether a dict or
> HAMT is better, and it's not clear whether a regular dict or
> WeakKeyDict is better.
>

I would keep it simple and supid, but WeakKeyDict is a subtype of
MutableMapping, and I'm sure we can find a way to implement the full
MutableMapping interface on top of HAMT as well.


> The first point (caching) I think is the really compelling one: in
> practice decimal and numpy are already using tricky caching code to
> reduce the overhead of accessing the ThreadState dict, and this gets
> even trickier with context-local state which has more cache
> invalidation points, so if we don't do this in the interpreter then it
> could actually become a blocker for adoption. OTOH it's easy for the
> interpreter itself to do this caching, and it makes everyone faster.
>

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"?)

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Paul Moore
On 16 October 2017 at 12:44, Nick Coghlan  wrote:
> The downside is that you'll still need to explicitly revert the decimal
> context before yielding from a generator if you didn't want the context
> change to "leak", but that's not a new constraint - it's one that already
> exists for the thread-local based decimal context API.

Ah, OK.Now I follow. Thanks for clarifying.
Paul
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Nick Coghlan
On 16 October 2017 at 18:26, Paul Moore  wrote:

> On 16 October 2017 at 02:33, Yury Selivanov 
> wrote:
> > Stage 1. A new execution context PEP to solve the problem *just for
> > async code*.  The PEP will target Python 3.7 and completely ignore
> > synchronous generators and asynchronous generators.  It will be based
> > on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
> > optimization) and borrow some good API decisions from PEP 550 v3+
> > (contextvars module, ContextVar class).  The API (and C-API) will be
> > designed to be future proof and ultimately allow transition to the
> > stage 2.
>
> So would decimal contexts stick to using threading.local? If so,
> presumably they'd still have problems with async. If not, won't you
> still be stuck with having to define the new semantics they have when
> used with generators? Or would it be out of scope for the PEP to take
> a position on what decimal does?
>

Decimal could (and should) still switch over in order to make itself more
coroutine-friendly, as in this version of the proposal, the key design
parameters would be:

- for synchronous code that never changes the execution context, context
variables and thread locals are essentially equivalent (since there will be
exactly one execution context per thread)
- for asynchronous code, each task managed by the event loop will get its
own execution context (each of which is distinct from the event loop's own
execution context)

So while I was initially disappointed by the suggestion, I'm coming around
to the perspective that it's probably a good pragmatic way to improve
context variable adoption rates, since it makes it straightforward for
folks to seamlessly switch between using context variables when they're
available, and falling back to thread local variables otherwise (and
perhaps restricting their coroutine support to Python versions that offer
context variables).

The downside is that you'll still need to explicitly revert the decimal
context before yielding from a generator if you didn't want the context
change to "leak", but that's not a new constraint - it's one that already
exists for the thread-local based decimal context API.

So going down this path would lock in the *default* semantics for the
interaction between context variables and generators as being the same as
the interaction between thread locals and generators, but would still leave
the door open to subsequently introducing an opt-in API like the
"contextvars.iter_in_context" idea for cases where folks decided they
wanted to do something different (like capturing the context at the point
where iterator was created and then temporarily switching back to that on
each iteration).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-16 Thread Paul Moore
On 16 October 2017 at 02:33, Yury Selivanov  wrote:
> Stage 1. A new execution context PEP to solve the problem *just for
> async code*.  The PEP will target Python 3.7 and completely ignore
> synchronous generators and asynchronous generators.  It will be based
> on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
> optimization) and borrow some good API decisions from PEP 550 v3+
> (contextvars module, ContextVar class).  The API (and C-API) will be
> designed to be future proof and ultimately allow transition to the
> stage 2.

So would decimal contexts stick to using threading.local? If so,
presumably they'd still have problems with async. If not, won't you
still be stuck with having to define the new semantics they have when
used with generators? Or would it be out of scope for the PEP to take
a position on what decimal does?

Paul
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Nathaniel Smith
On Sun, Oct 15, 2017 at 10:10 PM, Guido van Rossum  wrote:
> On Sun, Oct 15, 2017 at 8:17 PM, Nathaniel Smith  wrote:
>>
>> On Sun, Oct 15, 2017 at 6:33 PM, Yury Selivanov 
>> wrote:
>> > Stage 1. A new execution context PEP to solve the problem *just for
>> > async code*.  The PEP will target Python 3.7 and completely ignore
>> > synchronous generators and asynchronous generators.  It will be based
>> > on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
>> > optimization) and borrow some good API decisions from PEP 550 v3+
>> > (contextvars module, ContextVar class).  The API (and C-API) will be
>> > designed to be future proof and ultimately allow transition to the
>> > stage 2.
>>
>> If you want to ignore generators/async generators, then I think you
>> don't even want PEP 550 v1, you just want something like a
>> {set,get}_context_state API that lets you access the ThreadState's
>> context dict (or rather, an opaque ContextState object that holds the
>> context dict), and then task schedulers can call them at appropriate
>> moments.
>
>
> Yes, that's what I meant by "ignoring generators". And I'd like there to be
> a "current context" that's a per-thread MutableMapping with ContextVar keys.
> Maybe there's not much more to it apart from naming the APIs for getting and
> setting it? To be clear, I am fine with this being a specific subtype of
> MutableMapping. But I don't see much benefit in making it more abstract than
> that.

We don't need it to be abstract (it's fine to have a single concrete
mapping type that we always use internally), but I think we do want it
to be opaque (instead of exposing the MutableMapping interface, the
only way to get/set specific values should be through the ContextVar
interface). The advantages are:

- This allows C level caching of values in ContextVar objects (in
particular, funneling mutations through a limited API makes cache
invalidation *much* easier)

- It gives us flexibility to change the underlying data structure
without breaking API, or for different implementations to make
different choices -- in particular, it's not clear whether a dict or
HAMT is better, and it's not clear whether a regular dict or
WeakKeyDict is better.

The first point (caching) I think is the really compelling one: in
practice decimal and numpy are already using tricky caching code to
reduce the overhead of accessing the ThreadState dict, and this gets
even trickier with context-local state which has more cache
invalidation points, so if we don't do this in the interpreter then it
could actually become a blocker for adoption. OTOH it's easy for the
interpreter itself to do this caching, and it makes everyone faster.

-n

-- 
Nathaniel J. Smith -- https://vorpus.org
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Guido van Rossum
On Sun, Oct 15, 2017 at 8:17 PM, Nathaniel Smith  wrote:

> On Sun, Oct 15, 2017 at 6:33 PM, Yury Selivanov 
> wrote:
> > Stage 1. A new execution context PEP to solve the problem *just for
> > async code*.  The PEP will target Python 3.7 and completely ignore
> > synchronous generators and asynchronous generators.  It will be based
> > on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
> > optimization) and borrow some good API decisions from PEP 550 v3+
> > (contextvars module, ContextVar class).  The API (and C-API) will be
> > designed to be future proof and ultimately allow transition to the
> > stage 2.
>
> If you want to ignore generators/async generators, then I think you
> don't even want PEP 550 v1, you just want something like a
> {set,get}_context_state API that lets you access the ThreadState's
> context dict (or rather, an opaque ContextState object that holds the
> context dict), and then task schedulers can call them at appropriate
> moments.
>

Yes, that's what I meant by "ignoring generators". And I'd like there to be
a "current context" that's a per-thread MutableMapping with ContextVar
keys. Maybe there's not much more to it apart from naming the APIs for
getting and setting it? To be clear, I am fine with this being a specific
subtype of MutableMapping. But I don't see much benefit in making it more
abstract than that.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Guido van Rossum
On Sun, Oct 15, 2017 at 8:12 PM, Nick Coghlan  wrote:

> On 16 October 2017 at 11:33, Yury Selivanov 
> wrote:
>
>> Stage 2. When Python 3.7 is out, we'll see how people use execution
>> contexts for async code and collect feedback.  If we recognize that
>> Python users want execution contexts for generators/asynchronous
>> generators, we'll make a new PEP to add support for them in Python
>> 3.8.  That future discussion will be focused on generators
>> specifically, and therefore I expect it to be somewhat more focused.
>>
>
> As long as it's made clear that the interaction between context variables
> and generators is formally undefined in 3.7, I think that's reasonable -
> folks that want to ensure the current behaviour indefinitely should keep
> using thread locals rather than switching over to context variables.
>

It shouldn't be formally undefined. It should have the semantics it
acquires when you combine the existing (well-defined) formal semantics of
generators with the (to be defined) formal semantics of context variables.

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Nathaniel Smith
On Sun, Oct 15, 2017 at 6:33 PM, Yury Selivanov  wrote:
> Hi,
>
> It looks like the discussion about the execution context became
> extremely hard to follow.  There are many opinions on how the spec for
> generators should look like.  What seems to be "natural"
> behaviour/example to one, seems to be completely unreasonable to other
> people.  Recent emails from Guido indicate that he doesn't want to
> implement execution contexts for generators (at least in 3.7).
>
> In another thread Guido said this: "... Because coroutines and
> generators are similar under the covers, Yury demonstrated the issue
> with generators instead of coroutines (which are unfamiliar to many
> people). And then somehow we got hung up about fixing the problem in
> the example."
>
> And Guido is right.  My initial motivation to write PEP 550 was to
> solve my own pain point, have a solution for async code.
> 'threading.local' is completely unusable there, but complex code bases
> demand a working solution.  I thought that because coroutines and
> generators are so similar under the hood, I can design a simple
> solution that will cover all edge cases.  Turns out it is not possible
> to do it in one pass.
>
> Therefore, in order to make some progress, I propose to split the
> problem in half:
>
> Stage 1. A new execution context PEP to solve the problem *just for
> async code*.  The PEP will target Python 3.7 and completely ignore
> synchronous generators and asynchronous generators.  It will be based
> on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
> optimization) and borrow some good API decisions from PEP 550 v3+
> (contextvars module, ContextVar class).  The API (and C-API) will be
> designed to be future proof and ultimately allow transition to the
> stage 2.

If you want to ignore generators/async generators, then I think you
don't even want PEP 550 v1, you just want something like a
{set,get}_context_state API that lets you access the ThreadState's
context dict (or rather, an opaque ContextState object that holds the
context dict), and then task schedulers can call them at appropriate
moments.

-n

-- 
Nathaniel J. Smith -- https://vorpus.org
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Nick Coghlan
On 16 October 2017 at 11:33, Yury Selivanov  wrote:

> Stage 2. When Python 3.7 is out, we'll see how people use execution
> contexts for async code and collect feedback.  If we recognize that
> Python users want execution contexts for generators/asynchronous
> generators, we'll make a new PEP to add support for them in Python
> 3.8.  That future discussion will be focused on generators
> specifically, and therefore I expect it to be somewhat more focused.
>

As long as it's made clear that the interaction between context variables
and generators is formally undefined in 3.7, I think that's reasonable -
folks that want to ensure the current behaviour indefinitely should keep
using thread locals rather than switching over to context variables.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


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

2017-10-15 Thread Yury Selivanov
Hi,

It looks like the discussion about the execution context became
extremely hard to follow.  There are many opinions on how the spec for
generators should look like.  What seems to be "natural"
behaviour/example to one, seems to be completely unreasonable to other
people.  Recent emails from Guido indicate that he doesn't want to
implement execution contexts for generators (at least in 3.7).

In another thread Guido said this: "... Because coroutines and
generators are similar under the covers, Yury demonstrated the issue
with generators instead of coroutines (which are unfamiliar to many
people). And then somehow we got hung up about fixing the problem in
the example."

And Guido is right.  My initial motivation to write PEP 550 was to
solve my own pain point, have a solution for async code.
'threading.local' is completely unusable there, but complex code bases
demand a working solution.  I thought that because coroutines and
generators are so similar under the hood, I can design a simple
solution that will cover all edge cases.  Turns out it is not possible
to do it in one pass.

Therefore, in order to make some progress, I propose to split the
problem in half:

Stage 1. A new execution context PEP to solve the problem *just for
async code*.  The PEP will target Python 3.7 and completely ignore
synchronous generators and asynchronous generators.  It will be based
on PEP 550 v1 (no chained lookups, immutable mapping or CoW as an
optimization) and borrow some good API decisions from PEP 550 v3+
(contextvars module, ContextVar class).  The API (and C-API) will be
designed to be future proof and ultimately allow transition to the
stage 2.

Stage 2. When Python 3.7 is out, we'll see how people use execution
contexts for async code and collect feedback.  If we recognize that
Python users want execution contexts for generators/asynchronous
generators, we'll make a new PEP to add support for them in Python
3.8.  That future discussion will be focused on generators
specifically, and therefore I expect it to be somewhat more focused.

I will start working on the new PEP for stage 1 tomorrow.  I expect to
have a first version by the end of the week.

I will also publish PEP 550 v1 as a separate PEP (as v1 is a totally
different PEP anyways).

Thanks,
Yury
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com