Re: [Python-Dev] PEP 550 leak-in vs leak-out, why not just a ChainMap

2017-08-23 Thread Yury Selivanov
Hi Jim,

Sorry, I don't answer all questions/points directly.  We are working
on a new version of the PEP that will hopefully address most of them.

Some comments inlined below:

On Thu, Aug 24, 2017 at 12:32 AM, Jim J. Jewett  wrote:
[..]
> I still don't see how this is different from a ChainMap.

Because conceptually it's not different.  In the next version of the
PEP it will become more apparent.

[..]
>
>> HAMT is a way to efficiently implement immutable mappings ...
>> using regular dicts and copy, set() would be O(log N)

The key requirement for using immutable datastructures is to make
"get_execution_context" operation fast.  Currently, the PEP doesn't do
a good job at explaining why we need that operation and why it will be
used by asyncio.Task and call_soon, so I understand the confusion.
This will be fixed in the next PEP version.

>
> Using a ChainMap, set affects only the localmost map and is therefore
> O(1).  get could require stacksize lookups, but ...

Again, the PEP essentially is implemented through a very specialized
version of ChainMap, although written in C with a few extra
optimizations and properties.

>
> (A)  How many values do you expect a typical generator to use?  The
> django survey suggested mostly 0, sometimes 1, occasionally 2.  So
> caching the values of all possible keys probably won't pay off.

Not many, but caching is still as important, because some API users
want the "get()" operation to be as fast as possible under all
conditions.

>
> (B)  Other than the truly global context and thread-level context, how
> many of these maps do you expect to be non-empty?

It's likely that most of them will be empty in most cases. Although we
can imagine a degenerate case of a recursive generator that modifies
EC on each iteration.

>
> (C)  How deep do you expect the stack to get?  Are we talking about
> 100 layers of mappings to check between the current generator and the
> thread-wide defaults?  Even if we are, verifying that there hasn't
> been a change in some mid-level layer requires tracking the versions
> of each mid-level layer.  (If version is globally unique, that would
> also ensure that the stack hasn't changed.)  Is that really faster
> than checking that the map is empty?

The stack can be as deep as recursion limit.

>
> And, of course, using a ChainMap means that the keys do NOT have to be
> predefined ... so the Key class really can be skipped.

The first version of the PEP had no ContextKey object and the most
popular complaint about it was that the key names will clash.

The ContextKey design avoids clashing issue entirely and enables fast
Python and C APIs (because of the cache).

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


[Python-Dev] PEP 550 leak-in vs leak-out, why not just a ChainMap

2017-08-23 Thread Jim J. Jewett
In https://mail.python.org/pipermail/python-dev/2017-August/148869.html
Nick Coghlan wrote:

> * what we want to capture at generator creation time is
>   the context where writes will happen, and we also
>   want that to be the innermost context used for lookups

So when creating a generator, we want a new (empty) ImplicitContext
map to be the head of the ChainMap.  Each generator should have one of
its own, just as each generator has its own frame.  And the ChainMap
delegation goes up the call stack, just as an exception would.
Eventually, it hits the event loop (or other Executor) which is
responsible for ensuring that the ChainMap eventually defers to the
proper (Chain)Map for this thread or Task.

> While the context is defined conceptually as a nested chain of
> key:value mappings, we avoid using the mapping syntax because of the
> way the values can shift dynamically out from under you based on who
> called you
...
> instead of having the problem of changes inside the
> generator leaking out, we instead had the problem of
> changes outside the generator *not* making their way in

I still don't see how this is different from a ChainMap.

If you are using a stack(chain) of [d_global, d_thread, d_A, d_B, d_C,
d_mine]  maps as your implicit context, then a change to d_thread map
(that some other code could make) will be visible unless it is masked.

Similarly, if the fallback for d_C changes from d_B to d_B1 (which
points directly to d_thread), that will be visible for any keys that
were previously resolved in d_A or d_B, or are now resolved in dB1.

Those seem like exactly the cases that would (and should) cause
"shifting values".

This does mean that you can't cache everything in the localmost map,
but that is a problem with the optimization regardless of how the
implementation is done.

In https://mail.python.org/pipermail/python-dev/2017-August/148873.html
Yury Selivanov wrote:

> Any code that uses EC will not see any difference
[between mutable vs immutable but replaced LC maps],
> because it can only work with the top LC.

> Back to generators. Generators have their own empty LCs when created
> to store their *local* EC modifications.

OK, so just as they have their own frame, they have their own
ChainMap, and the event loop is responsible for resetting the fallback
when it schedules them.

> When a generator is *being* iterated, it pushes its LC to the EC. When
> the iteration step is finished, it pops its LC from the EC.

I'm not sure it helps to think of a single stack.  When the generator
is active, it starts with its own map.  When it is in the call chain
of the active generator, its map will be in the chain of delegations.
When neither it nor a descendant are active, no code will end up
delegating to it.

If the delegation graph has 543 generators delegating directly to the
thread-wide map, there is no reason to pop/push an execution stack
every time a different generator is scheduled, since only that
generator itself (and code it calls) will even care.

> HAMT is a way to efficiently implement immutable mappings ...
> using regular dicts and copy, set() would be O(log N)

Using a ChainMap, set affects only the localmost map and is therefore
O(1).  get could require stacksize lookups, but ...

(A)  How many values do you expect a typical generator to use?  The
django survey suggested mostly 0, sometimes 1, occasionally 2.  So
caching the values of all possible keys probably won't pay off.

(B)  Other than the truly global context and thread-level context, how
many of these maps do you expect to be non-empty?

(C)  How deep do you expect the stack to get?  Are we talking about
100 layers of mappings to check between the current generator and the
thread-wide defaults?  Even if we are, verifying that there hasn't
been a change in some mid-level layer requires tracking the versions
of each mid-level layer.  (If version is globally unique, that would
also ensure that the stack hasn't changed.)  Is that really faster
than checking that the map is empty?

And, of course, using a ChainMap means that the keys do NOT have to be
predefined ... so the Key class really can be skipped.

-jJ
___
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] PEP 550 v3 naming

2017-08-23 Thread Ethan Furman

On 08/23/2017 12:17 PM, Yury Selivanov wrote:

How about ExecutionContext and ContextVars ?



We are already used to different levels of variables: global, local, non-local, 
class.  I think having variables tied to a Context, and having search flow back 
to previous Contexts, would be easy to understand.


Yeah, I had this idea about ContextVars and so far I like it more than
Context Keys and Context Values.

In the next version of the PEP (will post it soon), we use Execution
Context, Logical Context, and Context Variable terminology (the debate
if we should use other names is, of course, still open).


ContextVars is actually a different name for LogicalContext.  So it would be:

  ExecutionContext = [ContextVars()[, ContextVars()[ ...]]]

and you get the (thread.local similar) ContextVars by

  context_vars = sys.get_context_vars()   # or whatever
  context_vars.verbose = ...

--
~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] PEP 550 v3 naming

2017-08-23 Thread Simon Cross
What about "CoroutineScope" or "CoroutineContext"?
___
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] PEP 550 v3 naming

2017-08-23 Thread Yury Selivanov
> How about ExecutionContext and ContextVars ?

> We are already used to different levels of variables: global, local, 
> non-local, class.  I think having variables tied to a Context, and having 
> search flow back to previous Contexts, would be easy to understand.

Yeah, I had this idea about ContextVars and so far I like it more than
Context Keys and Context Values.

In the next version of the PEP (will post it soon), we use Execution
Context, Logical Context, and Context Variable terminology (the debate
if we should use other names is, of course, still open).

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] PEP 550 v3 naming

2017-08-23 Thread Yury Selivanov
On Wed, Aug 23, 2017 at 2:47 PM, Guido van Rossum  wrote:
> In favor of ImplicitContext is one point: it is indeed "implicit" if you
> compare it with the "explicit" way of passing state around, which would
> require an extra argument containing the state for any function that uses
> the state *or calls a function that uses the state* (recursively).

This is a good point, now I understand the reasoning better.

I guess I'm looking at this from a perspective of PEP 550 users.  They
will use either ContextKey-like API described in the PEP now, or
something similar to threading.local() to store values in the context
of a generator or an async task.  From the standpoint of the user,
they work with some sort of of a "global" variable.  To me, renaming
LC to ImplicitContext sounds similar to saying that using global
variables is implicit, or calling "global" scope "implicit".

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] PEP 550 v3 naming

2017-08-23 Thread Ethan Furman

On 08/23/2017 11:27 AM, Yury Selivanov wrote:


Out of what was proposed so far to replace Logical Context:

[...]

I don't think that replacing LogicalContext with any name in this list
will make any improvement.


How about ExecutionContext and ContextVars ?

We are already used to different levels of variables: global, local, non-local, class.  I think having variables tied to 
a Context, and having search flow back to previous Contexts, would be easy to understand.


--
~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] PEP 550 v3 naming

2017-08-23 Thread Guido van Rossum
In favor of ImplicitContext is one point: it is indeed "implicit" if you
compare it with the "explicit" way of passing state around, which would
require an extra argument containing the state for any function that uses
the state *or calls a function that uses the state* (recursively).

On Wed, Aug 23, 2017 at 11:27 AM, Yury Selivanov 
wrote:

> On Wed, Aug 23, 2017 at 12:56 PM, Antoine Pitrou 
> wrote:
> > On Wed, 23 Aug 2017 12:19:40 -0400
> > Yury Selivanov  wrote:
> >> PEP 550 calls generators and asynchronous tasks as "logical threads",
> >> and "logical context" stems directly from that notion.
> >
> > I wouldn't refer to a generator as a "thread" personally.  A thread
> > essentially executes without intervention from outside code, which is
> > not the case for a generator (where resumption is controlled by
> > explicit next() calls or iteration by the consumer).
>
> I agree. That's why we are rewriting the PEP without mentioning
> "logical threads".
>
> >
> > That is also why I proposed the more general "task" (and "task
> > context").  Of course, capitalized "Task" is given a specific
> > meaning by asyncio...
>
> Yeah.. I like TaskContext when it's applied to asynchronous code. It
> doesn't really work for generators because we never refer to
> generators as tasks.
>
> Out of what was proposed so far to replace Logical Context:
>
> 1. DynamicContext: close to "dynamic scoping", but tries to avoid
> mentioning "scopes". There are only a few languages where dynamic
> scoping is implemented, so people are generally not aware of it.
>
> 2. ContextFrame and all frame-related names: implies that EC is
> somehow implemented on top of frames or is frame-specific (which is
> not always true).
>
> 3. ImplicitContext: covers one specific property observed from
> specific examples.  Context in PEP 550 is managed explicitly in some
> cases. There are many use cases when API users will be working with it
> explicitly too (to wrirte/read data from it). FWIW I believe that
> "ExplicitContext" would be more accurate than "ImplicitContext".
>
> 4. LocalContext: we use "local" to describe local variables and
> scoping in Python, we want to avoid any confusion here.
>
> 5. TaskContext: works for asynchronous tasks, but not for generators.
>
> I don't think that replacing LogicalContext with any name in this list
> will make any improvement.
>
> 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/
> guido%40python.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] PEP 550 v3 naming

2017-08-23 Thread Yury Selivanov
On Wed, Aug 23, 2017 at 12:56 PM, Antoine Pitrou  wrote:
> On Wed, 23 Aug 2017 12:19:40 -0400
> Yury Selivanov  wrote:
>> PEP 550 calls generators and asynchronous tasks as "logical threads",
>> and "logical context" stems directly from that notion.
>
> I wouldn't refer to a generator as a "thread" personally.  A thread
> essentially executes without intervention from outside code, which is
> not the case for a generator (where resumption is controlled by
> explicit next() calls or iteration by the consumer).

I agree. That's why we are rewriting the PEP without mentioning
"logical threads".

>
> That is also why I proposed the more general "task" (and "task
> context").  Of course, capitalized "Task" is given a specific
> meaning by asyncio...

Yeah.. I like TaskContext when it's applied to asynchronous code. It
doesn't really work for generators because we never refer to
generators as tasks.

Out of what was proposed so far to replace Logical Context:

1. DynamicContext: close to "dynamic scoping", but tries to avoid
mentioning "scopes". There are only a few languages where dynamic
scoping is implemented, so people are generally not aware of it.

2. ContextFrame and all frame-related names: implies that EC is
somehow implemented on top of frames or is frame-specific (which is
not always true).

3. ImplicitContext: covers one specific property observed from
specific examples.  Context in PEP 550 is managed explicitly in some
cases. There are many use cases when API users will be working with it
explicitly too (to wrirte/read data from it). FWIW I believe that
"ExplicitContext" would be more accurate than "ImplicitContext".

4. LocalContext: we use "local" to describe local variables and
scoping in Python, we want to avoid any confusion here.

5. TaskContext: works for asynchronous tasks, but not for generators.

I don't think that replacing LogicalContext with any name in this list
will make any improvement.

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] PEP 550 v3 naming

2017-08-23 Thread Antoine Pitrou
On Wed, 23 Aug 2017 12:19:40 -0400
Yury Selivanov  wrote:
> PEP 550 calls generators and asynchronous tasks as "logical threads",
> and "logical context" stems directly from that notion.

I wouldn't refer to a generator as a "thread" personally.  A thread
essentially executes without intervention from outside code, which is
not the case for a generator (where resumption is controlled by
explicit next() calls or iteration by the consumer).

That is also why I proposed the more general "task" (and "task
context").  Of course, capitalized "Task" is given a specific
meaning by asyncio...

Regards

Antoine.
___
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] PEP 550 v3 naming

2017-08-23 Thread Yury Selivanov
I think that "implicit context" is not an accurate description of what
LogicalContext is.

"implicit context" only makes sense when we talk about decimal
context.  For instance, in:

Decimal(1) + Decimal(2)

decimal context is implicit.  But this is "implicit" from the
standpoint of that code.  Decimal will manage its context
*explicitly*.

Fixing decimal context is only one part of the PEP though.  EC will
also allow to implement asynchronous task locals:

current_request = new_context_key()

async def handle_http_request(request):
 current_request.set(request)

Here we explicitly set and will explicitly get values from the EC.  We
will explicitly manage the EC in asyncio.Task and when we schedule
callbacks.

Values stored in the EC are essentially globals (or TLS), which we
don't call "implicit" in Python.

PEP 550 calls generators and asynchronous tasks as "logical threads",
and "logical context" stems directly from that notion.

"implicit" only covers one part of what LC really is. It implies that
all EC/LC operations are implicit, which they are not.

Logical Context is a neutral abstract term.  Implicit Context bears a
specific meaning that I don't think is accurate.

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] PEP 550 v3 naming

2017-08-23 Thread Antoine Pitrou

For the record, I'm starting to think that "implicit context" is a
reasonable name.

(in case anyone is interested in those 2 cents of mine :-))

Regards

Antoine.


___
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] PEP 550 v3 naming

2017-08-23 Thread Guido van Rossum
On Tue, Aug 22, 2017 at 11:21 PM, Nick Coghlan  wrote:
>
> As the original proponent of a Dynamic* naming scheme, I'll note that
> I eventually decided I didn't like it for the same reason I already
> didn't like naming schemes using either the word "local" or the word
> "frame": they all suggest a coupling to the synchronous call stack
> that deliberately isn't part of the proposal.
>

However, there is still *some* connection with the frame, though it's
subtle. In particular the LC (using PEP v3 terms) for a generator is tied
to that generator's frame (it becomes the top of the EC stack whenever that
generator is resumed). I don't think we should call this out in the naming
scheme though.

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