From f35e067d3f0f4921506f70102eb2e61d2efee055 Mon Sep 17 00:00:00 2001 From: Abhi Agarwal Date: Sat, 11 Jan 2025 17:39:18 -0500 Subject: [PATCH] vault backup: 2025-01-11 17:39:18 --- content/().md | 0 ...ch-of-futures-safely.md => asyncio-gather-taskgroup.md} | 6 ++++-- ...iew-question-you-ever-need.md => interview-question.md} | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 content/().md rename content/programming/languages/python/asyncio/{running-a-bunch-of-futures-safely.md => asyncio-gather-taskgroup.md} (77%) rename content/programming/languages/python/asyncio/{the-only-interview-question-you-ever-need.md => interview-question.md} (97%) diff --git a/content/().md b/content/().md deleted file mode 100644 index e69de29b..00000000 diff --git a/content/programming/languages/python/asyncio/running-a-bunch-of-futures-safely.md b/content/programming/languages/python/asyncio/asyncio-gather-taskgroup.md similarity index 77% rename from content/programming/languages/python/asyncio/running-a-bunch-of-futures-safely.md rename to content/programming/languages/python/asyncio/asyncio-gather-taskgroup.md index b514c4bc..a3c3fbd1 100644 --- a/content/programming/languages/python/asyncio/running-a-bunch-of-futures-safely.md +++ b/content/programming/languages/python/asyncio/asyncio-gather-taskgroup.md @@ -24,13 +24,15 @@ if __name__ == "__main__": asyncio.run(main()) ``` -is actually _deeply_ unsafe. Let me cite the (in)famous article that alerted me to this problem, [the heisenberg lurking in your async code](https://textual.textualize.io/blog/2023/02/11/the-heisenbug-lurking-in-your-async-code/). Here's also an excellent [stack overflow](https://stackoverflow.com/a/76823668/21551208) answer that goes a bit more in depth. In short, due to python's garbage collector, those `task_*` objects we created are weak references. Python's garbage collector doesn't understand that those `task_*` objects have a life after the `asyncio.gather`, and they may just be arbitrarily garbage collected by python, and never run. +is _deeply_ unsafe. Let me cite the (in)famous article that alerted me to this problem, [the Heisenbug lurking in your async code](https://textual.textualize.io/blog/2023/02/11/the-heisenbug-lurking-in-your-async-code/). Here's also an excellent [stack overflow](https://stackoverflow.com/a/76823668/21551208) answer that goes a bit more in depth. In short, due to python's garbage collector, those `task_*` objects we created are weak references. Python's garbage collector doesn't understand that those `task_*` objects have a life after the `asyncio.gather`, and they may just be arbitrarily garbage collected by python, and never run. Why does python do this? ¯\\\_(ツ)\_/¯. I would like to have a cordial conversation to whoever designed it this way. In fact, the [asyncio docs for `create_task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) have a warning for this: -![[running-a-bunch-of-futures-asyncio-docs.png]]Alright, fair, but I, like hundreds of millions developers, will skip text that's in a grey box. It needs to have red scary text, maybe outlined in red, and it should also have a popup in the browser. Guido Van Rossum should mail each IP address that has ever downloaded python a hand-written letter warning them of this. That's how serious this problem is. +![[running-a-bunch-of-futures-asyncio-docs.png]] + +Alright, fair, but I, like hundreds of millions developers, will skip text that's in a grey box. It needs to have red scary text, maybe outlined in red, and it should also have a popup in the browser. Guido Van Rossum should mail each IP address that has ever downloaded python a hand-written letter warning them of this. That's how serious this problem is. The alternative solution, as the docs mention, is to use `asyncio.TaskGroup`. I actually _love_ the `asyncio.TaskGroup()` abstraction, and it serves its purpose well. diff --git a/content/programming/languages/python/asyncio/the-only-interview-question-you-ever-need.md b/content/programming/languages/python/asyncio/interview-question.md similarity index 97% rename from content/programming/languages/python/asyncio/the-only-interview-question-you-ever-need.md rename to content/programming/languages/python/asyncio/interview-question.md index 3aa4c363..7a1e7985 100644 --- a/content/programming/languages/python/asyncio/the-only-interview-question-you-ever-need.md +++ b/content/programming/languages/python/asyncio/interview-question.md @@ -3,7 +3,7 @@ title: The only python interview question you will ever need tags: - thoughts --- -I've been thinking recently about Python API design (as one does, in their mid 20s). I'm someone who cares deeply writing performant code, so I often turn to [[threading|`threading, multiprocessing`]] or `asyncio` when dealing with IO-bound work (which is the majority of python applications). +I've been thinking recently about Python API design (as one does, in their mid 20s). I'm someone who cares deeply writing performant code, so I often turn to [[threading|threading, multiprocessing]] or asyncio when dealing with IO-bound work (which is the majority of python applications). I was looking at the API design for `concurrent.futures.ProcessPoolExecutor/ThreadPoolExecutor.submit()`, which looks something like: @@ -16,7 +16,7 @@ with ProcessPoolExecutor() as executor: task = executor.submit(cpu_bound_function, 5, keyword_arg=10) ``` -Versus `asyncio.TaskGroup.create_task()`, which looks something like: +And `asyncio.TaskGroup.create_task()`, which looks something like: ```python async def io_bound_function(val, *, keyword_arg): @@ -34,7 +34,7 @@ If it isn't obvious to you: time to study :) # Appendix: What LLMs say -This isn't a particularly esoteric question or anything, but I wanted to see what LLMs think about it. Here's what a few of them say. +This isn't a particularly esoteric question or anything, but I wanted to see what LLMs think about it. The prompt was exactly the text above, with the first and last paragraphs excluded. Here's what a few of them say. > [!example]- Claude 3.5 Sonnet > @@ -152,6 +152,7 @@ This isn't a particularly esoteric question or anything, but I wanted to see wha > > [!info] Verdict > > A bit wordy, but overall, the right answer. o1 in general is a bit wordy (not sure if this is intentional behavior), but it could be cut in half and still be right. + > [!example]- Gemini 1206 Exp > You've hit upon a subtle but important distinction between synchronous and asynchronous programming models, and how that impacts API design. Here's a breakdown of why `concurrent.futures` and `asyncio` differ in their task submission approaches: >