[Python-ideas] Re: request for new Executor in `concurrent.futures`

2021-11-08 Thread Mark Gordon
Are you looking for

ProcessPoolExecutor(mp_context=multiprocessing.get_context("fork"))
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5QZXOUWTCRFZN4YUWYQQKSVPZDEEXIJU/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Context managers in expressions

2021-10-25 Thread Mark Gordon
What should happen if the context manager attempts to suppress a raised 
exception? In cases where you applied the context manager to an entire line, 
e.g.

data = fail() with contextlib.suppress(Exception)

Then it would make sense to treat it like

with contextlib.suppress(Exception):
data = fail()

Where `data` remains unassigned after the block executes assuming `fail` raises 
an exception. However, with the initial proposal you run into trouble when you 
apply this to sub-expressions that are expected to themselves have a value. For 
example, what should happen here?

more_work(fail() with contextlib.suppress(Exception))

We have no value to pass as an argument to `more_work` so there's no way we can 
call it. Yet it would be odd to not call it if there's no exception being 
raised since it exists outside of any context manager itself.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/Y7WZDD2AFGUX3ND2OX3EUN2VUK27O4E5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: weakref.link: Keep A alive while B is alive (and/or update WeakKeyDictionary)

2021-07-27 Thread Mark Gordon
Looks like a separate issue to me. That issue can be fixed by updating the 
Python implementation of WeakKeyDictioary.

The fix for the WeakKeyDictionary issue I mentioned and the Ephemeron primitive 
(as Sebastian identified it) are equivalant (you can implement one from the 
other) and cannot be reduced to any existing primitive as far as I can tell. 
Such a change would need to happen within the Python engine itself.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/KJUFOKVTU2YY32CS6TCRAN3P3XLBCJ42/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] weakref.link: Keep A alive while B is alive (and/or update WeakKeyDictionary)

2021-07-19 Thread Mark Gordon
Proposal:

Have a weakref.link (not at all attached to the naming) primitive that allows 
one to keep object A alive while object B is alive. This would be functionally 
similar to adding A as an attribute of B from the GC's perspective without 
making any assumptions about what attributes are available in B. Here's a 
really course example of how this might be used:

# Associate "extra_data" with "obj" where "extra_data" will lose its reference 
when "obj" is no longer reachable through strong refs.
my_link = weakref.link(obj, extra_data)

...

obj, extra_data = my_link()
if obj is not None:
  do_something(obj, extra_data)


This would also allow us to fix or create a new version of WeakKeyDictionary 
where values can have strong references back to their key without preventing 
their key from being reclaimed. This has been an issue for tensorflow in the 
past (see https://github.com/tensorflow/tensorflow/issues/42022) and is likely 
to be a subtle issue in many more libraries. Even the suggested use case in the 
docs "This can be especially useful with objects that override attribute 
accesses." is likely to be broken because of this issue.  While I'd be weary of 
changing the semantics of an existing data structure it seems highly unlikely 
anything depends on this behavior since it makes WeakKeyDictionary degrade to 
behaving like a normal dictionary for that key.


Notes:
* Not attached to this specific approach or API
* Mostly interesting in updating/creating new WeakKeyDictionary as mentioned
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4KBLRV7QYS36NVJNKAJIDCFTH4DF5YSY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fill in missing contextvars/asyncio task support

2021-06-24 Thread Mark Gordon
I've read the PEP and understand what's implemented. However there is pretty 
limited discussion about what the design constraints were and what 
intended/recommended usage would look like. I'll answer my own question:

1. If all we wanted was a version of TLS that worked in an analogous way 
extending (synchronous code, threads) to (async code, tasks) then you don't 
need anything fancy, a simple dictionary backing the "context" will do. This is 
all that you need to solve the "decimal formatting" problem, for instance.

2. However, the scope of PEP 567 was increased to something greater. It was 
decided that we want tasks/threads to be able to inherit an existing context. 
This is a unique feature with no analog in TLS. I believe a motivating use case 
was for a request/response server that may spawn worker tasks off the main task 
and want to store request context information in contextvars.

3. Additionally, to continue to have the "decimal formatting" solution work 
correctly it's necessary that no two tasks/threads are running on the same 
context. This means "inherting" should mean "running on a copy of".

These constraints strongly suggest an interface of:

contextvars.get_context() -> Context
Context.run_in_copy(func) -> Context
Context.async_run_in_copy(coro) -> Context
*** NoContext.run method, no copy methods needed either ***

So what was the motivation behind having a copy_context() and a non-copying 
Context.run method? It seems to break the third design constraint allowing you 
to have multiple threads try and run on the same Context.

Nonetheless, "asyncio.run_in_context()" is a direct analog of "Context.run" and 
should be a clear add to the API from a symmetry point of view. We are already 
beyond the point where constraint three is being strictly enforced. I'm not 
sure what argument against this API wouldn't apply to "Context.run()" as well.

If it's still a -1 on "asyncio.run_in_context()" what about 
"asyncio.run_in_context_copy(context, coro) -> Context" that copies the passed 
context and runs the coroutine in the task using that context? If we go this 
route maybe we would plan on deprecating Context.run and replacing it with a 
Context.run_in_copy method?
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4SYYJEVIV3ZUSVBZIL34EZTIEMBRV533/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fill in missing contextvars/asyncio task support

2021-06-23 Thread Mark Gordon
> That's a feature :) Perhaps we should add an example to the docs.

What do you view as the point of the copy semantics, then?

> There's not much wrong about this approach for simple coroutines. But 
> if a coroutine runs its own tasks or code that forks the context 
> inside, you won't see those changes in your context.

I thought you were against this usage pattern anyway. Not sure what this has to 
do with the proposed API change.

> In other words, 
> the hack you propose will work for some cases, and fail for others.

It seems like you are assuming a purpose to this API that I did not intend. It 
really is just so you can change a context that a task is running within 
without creating a new task. It's really just to be an analog of Context.run.
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JCPP6BRCIL5WLDDECIPNNDSLQAVVASFB/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Fill in missing contextvars/asyncio task support

2021-06-21 Thread Mark Gordon
Yury Selivanov wrote:
> On Mon, Jun 21, 2021 at 7:20 PM Mark Gordon msg...@gmail.com wrote:
> > Yeah, it would indeed inherit the copy. We could, theoretically, make
> > asyncio.Task accept context objects and not copy them, but what would
> > that
> > give us?
> > My main thinking was to just be similar to the closest synchronous analog
> > I'm aware of, contextvars.Context.run. I would think an explanation of why
> > the Context object API exists as it does, letting you manipulate and run in
> > contexts directly, would equally motivate the async analogs. Maybe the
> > exception would be if this API exists purely just to support async tasks
> > (then maybe it should be private?).
> > At any rate, the issue attached to the pull requests gives one example of
> > seeking to do asyncio tests with providing fixture data through an existing
> > context object. I could also imagine a use case of wanting to track the
> > number of database requests made within a logical request that may span
> > multiple tasks. Having the subtasks inherit the same context could help
> > with this.
> > To track things like database requests just put a mutable object in the
> context somewhere at the top level, referenced by a well-known contextvar
> in your code. That single object will be part of all contexts derived from
> the top one throughout the application lifecycle.

Using mutable objects in contexts seems strange as it works against their copy 
semantics. I agree it will work in this use case.

> > *Ultimately, contextvars enable implicit flow
> > of information from outer code to nested code and not vice versa. *
> > Just to clarify, are you stating an established position of the python
> > community or is this your personal view of how context vars should be used?
> > I'm stating this as the original contextvars PEP author and implementer. I

Oh very cool, thanks for taking the time to look at this.

> don't see how the reverse flow is possible to implement within all
> restrictions of the design space. To propagate information from nested
> calls to outer calls just use mutable objects, as I outlined above.
> > Additionally (obviously) it will also be running in a separate task.
> > There's no way around that, unfortunately. Even if we add some kind of
> > helper to run coroutines in a context, there still we be a task object
> > that
> > iterates the coroutine.
> > I was just pointing out that the stated work-around requires creating an
> > additional task to run the called coroutine rather than running directly in
> > the calling task.
> > Yes, and I'm saying that running a coroutine "directly on the stack" within
> some context is not technically possible, without wrapping it into a Task.

Is there something wrong with the solution of changing the context associated 
with a task before execution, yielding, run the coroutine, then swap it back 
and yield again when the coroutine exits? This is the hacky solution that 
appears to work for me on python 3.8 if I force use of _PyTask (and the linked 
PR extends this idea to work for the CTask implementation).

import asyncio.tasks
asyncio.tasks.Task = asyncio.tasks._PyTask
import asyncio
import contextvars

var = contextvars.ContextVar('var', default=123)

async def run_in_context(context, coro):
task = asyncio.current_task()
prev_context = task._context
task._context = context
await asyncio.sleep(0)

try:
return await coro
finally:
task._context = prev_context  
await asyncio.sleep(0)

async def foo(x):
old_val = var.get()
var.set(x)
return old_val

async def main():
var.set(5)

context = contextvars.Context()
print(await run_in_context(context, foo(555))) # 123
print(var.get()) # 5
print(await run_in_context(context, foo(999))) # 555
print(var.get()) # 5
print(context.run(var.get)) # 999

asyncio.run(main())


> > I guess we can add a keyword argument to asyncio.create_task() for that.
> > It
> > is an open question if the task factory would just use the passed context
> > object or would copy it first. I'm leaning towards the latter.
> > My vote would be for not a copy as mentioned above.
> > Having a asyncio.run_in_context(context, coro()) API is more important as
> > this feature is currently completely missing. So happy to table this if we
> > can't decide on if/what semantics this task kwarg API change should have.
> > Is asyncio.run_in_context() a version of asyncio.run() or a shortcut for
> Context.run(asyncio.create_task, coro)?

Yeah I mean it to be more or less the same except without creating a new task 
(which maybe is impossible?).

> Yury
_

[Python-ideas] Re: Fill in missing contextvars/asyncio task support

2021-06-21 Thread Mark Gordon
> Yeah, it would indeed inherit the copy. We could, theoretically, make
> asyncio.Task accept context objects and not copy them, but what would that
> give us?

My main thinking was to just be similar to the closest synchronous analog I'm 
aware of, contextvars.Context.run. I would think an explanation of why the 
Context object API exists as it does, letting you manipulate and run in 
contexts directly, would equally motivate the async analogs. Maybe the 
exception would be if this API exists purely just to support async tasks (then 
maybe it should be private?).

At any rate, the issue attached to the pull requests gives one example of 
seeking to do asyncio tests with providing fixture data through an existing 
context object. I could also imagine a use case of wanting to track the number 
of database requests made within a logical request that may span multiple 
tasks. Having the subtasks inherit the same context could help with this.

> *Ultimately, contextvars enable implicit flow
> of information from outer code to nested code and not vice versa. *

Just to clarify, are you stating an established position of the python 
community or is this your personal view of how context vars should be used?

> > Additionally (obviously) it will also be running in a separate task.
> There's no way around that, unfortunately. Even if we add some kind of
> helper to run coroutines in a context, there still we be a task object that
> iterates the coroutine.

I was just pointing out that the stated work-around requires creating an 
additional task to run the called coroutine rather than running directly in the 
calling task.

> I guess we can add a keyword argument to asyncio.create_task() for that. It
> is an open question if the task factory would just use the passed context
> object or would copy it first. I'm leaning towards the latter.

My vote would be for not a copy as mentioned above.

Having a asyncio.run_in_context(context, coro()) API is more important as this 
feature is currently completely missing. So happy to table this if we can't 
decide on if/what semantics this task kwarg API change should have.

-Mark
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/66P67IVADSUB425HLU55ZYLS2WLEQMQE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Fill in missing contextvars/asyncio task support

2021-06-20 Thread Mark Gordon
With normal synchronous code you can use `contextvars.Context.run()` to change 
what context code is executing within. However, there is no analagous concept 
for asyncio code. I'm proposing something similar, for example:

coro = foo()
my_context = convextvars.Context()
await asyncio.run_in_context(coro)

Currently the workaround is to run the coroutine on a separate task.

coro = foo()
my_context = convextvars.Context()
await my_context.run(asycnio.create_task, coro)

However this is not exactlly the same as the task will inherit a copy of 
my_context rather than running directly on my_context. Additionally (obviously) 
it will also be running in a separate task.

Similarly it would be nice if create_task and the Task constructor could take 
an optional context kwarg to use as the task context rather than the default of 
copying the calling context.

Pull request with sample implementation (although I think missing the change to 
create_task): https://github.com/python/cpython/pull/26664
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3EQO67J7IBAO6YSK2LBIPU4E7HU6UQJH/
Code of Conduct: http://python.org/psf/codeofconduct/