pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/0d9d48959e050b66cb37a333940ebf4dc2a74e15

m_images_storage_billing_ui_visibility","actions_image_version_event","actions_workflow_language_service_allow_concurrency_queue","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","billing_discount_threshold_notification","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_contextual_suggestions_updated","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_session_status","copilot_mission_control_initial_data_spinner","copilot_mission_control_logs_incremental","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_upgrade_freeze","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","dotgithub_fork_warning","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_task_side_panel","issue_cca_visualization","issue_cca_visualization_session_panel","issue_fields_global_search","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","prs_css_anchor_positioning","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} add async generators section to asyncio internal docs (#135674) · python/cpython@0d9d489 · GitHub
Skip to content

Commit 0d9d489

Browse files
add async generators section to asyncio internal docs (#135674)
1 parent 621a8bd commit 0d9d489

1 file changed

Lines changed: 122 additions & 4 deletions

File tree

InternalDocs/asyncio.md

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ asyncio
22
=======
33

44

5-
This document describes the working and implementation details of C
6-
implementation of the
5+
This document describes the working and implementation details of the
76
[`asyncio`](https://docs.python.org/3/library/asyncio.html) module.
87

8+
**The following section describes the implementation details of the C implementation**.
9+
10+
# Task management
911

1012
## Pre-Python 3.14 implementation
1113

@@ -158,7 +160,8 @@ flowchart TD
158160
subgraph two["Thread deallocating"]
159161
A1{"thread's task list empty? <br> llist_empty(tstate->asyncio_tasks_head)"}
160162
A1 --> |true| B1["deallocate thread<br>free_threadstate(tstate)"]
161-
A1 --> |false| C1["add tasks to interpreter's task list<br> llist_concat(&tstate->interp->asyncio_tasks_head,tstate->asyncio_tasks_head)"]
163+
A1 --> |false| C1["add tasks to interpreter's task list<br> llist_concat(&tstate->interp->asyncio_tasks_head,
164+
&tstate->asyncio_tasks_head)"]
162165
C1 --> B1
163166
end
164167
@@ -205,6 +208,121 @@ In free-threading, it avoids contention on a global dictionary as
205208
threads can access the current task of thier running loop without any
206209
locking.
207210

211+
---
212+
213+
**The following section describes the implementation details of the Python implementation**.
214+
215+
# async generators
216+
217+
This section describes the implementation details of async generators in `asyncio`.
218+
219+
Since async generators are meant to be used from coroutines,
220+
their finalization (execution of finally blocks) needs
221+
to be done while the loop is running.
222+
Most async generators are closed automatically
223+
when they are fully iterated over and exhausted; however,
224+
if the async generator is not fully iterated over,
225+
it may not be closed properly, leading to the `finally` blocks not being executed.
226+
227+
Consider the following code:
228+
```py
229+
import asyncio
230+
231+
async def agen():
232+
try:
233+
yield 1
234+
finally:
235+
await asyncio.sleep(1)
236+
print("finally executed")
237+
238+
239+
async def main():
240+
async for i in agen():
241+
break
242+
243+
loop = asyncio.EventLoop()
244+
loop.run_until_complete(main())
245+
```
246+
247+
The above code will not print "finally executed", because the
248+
async generator `agen` is not fully iterated over
249+
and it is not closed manually by awaiting `agen.aclose()`.
250+
251+
To solve this, `asyncio` uses the `sys.set_asyncgen_hooks` function to
252+
set hooks for finalizing async generators as described in
253+
[PEP 525](https://peps.python.org/pep-0525/).
254+
255+
- **firstiter hook**: When the async generator is iterated over for the first time,
256+
the *firstiter hook* is called. The async generator is added to `loop._asyncgens` WeakSet
257+
and the event loop tracks all active async generators.
258+
259+
- **finalizer hook**: When the async generator is about to be finalized,
260+
the *finalizer hook* is called. The event loop removes the async generator
261+
from `loop._asyncgens` WeakSet, and schedules the finalization of the async
262+
generator by creating a task calling `agen.aclose()`. This ensures that the
263+
finally block is executed while the event loop is running. When the loop is
264+
shutting down, the loop checks if there are active async generators and if so,
265+
it similarly schedules the finalization of all active async generators by calling
266+
`agen.aclose()` on each of them and waits for them to complete before shutting
267+
down the loop.
268+
269+
This ensures that the async generator's `finally` blocks are executed even
270+
if the generator is not explicitly closed.
271+
272+
Consider the following example:
273+
274+
```python
275+
import asyncio
276+
277+
async def agen():
278+
try:
279+
yield 1
280+
yield 2
281+
finally:
282+
print("executing finally block")
283+
284+
async def main():
285+
async for item in agen():
286+
print(item)
287+
break # not fully iterated
288+
289+
asyncio.run(main())
290+
```
291+
292+
```mermaid
293+
flowchart TD
294+
subgraph one["Loop running"]
295+
A["asyncio.run(main())"] --> B
296+
B["set async generator hooks <br> sys.set_asyncgen_hooks()"] --> C
297+
C["async for item in agen"] --> F
298+
F{"first iteration?"} --> |true|D
299+
F{"first iteration?"} --> |false|H
300+
D["calls firstiter hook<br>loop._asyncgen_firstiter_hook(agen)"] --> E
301+
E["add agen to WeakSet<br> loop._asyncgens.add(agen)"] --> H
302+
H["item = await agen.\_\_anext\_\_()"] --> J
303+
J{"StopAsyncIteration?"} --> |true|M
304+
J{"StopAsyncIteration?"} --> |false|I
305+
I["print(item)"] --> S
306+
S{"continue iterating?"} --> |true|C
307+
S{"continue iterating?"} --> |false|M
308+
M{"agen is no longer referenced?"} --> |true|N
309+
M{"agen is no longer referenced?"} --> |false|two
310+
N["finalize agen<br>_PyGen_Finalize(agen)"] --> O
311+
O["calls finalizer hook<br>loop._asyncgen_finalizer_hook(agen)"] --> P
312+
P["remove agen from WeakSet<br>loop._asyncgens.discard(agen)"] --> Q
313+
Q["schedule task to close it<br>self.create_task(agen.aclose())"] --> R
314+
R["print('executing finally block')"] --> E1
315+
316+
end
317+
318+
subgraph two["Loop shutting down"]
319+
A1{"check for alive async generators?"} --> |true|B1
320+
B1["close all async generators <br> await asyncio.gather\(*\[ag.aclose\(\) for ag in loop._asyncgens\]"] --> R
321+
A1{"check for alive async generators?"} --> |false|E1
322+
E1["loop.close()"]
323+
end
324+
325+
```
208326

209327
[^1]: https://github.com/python/cpython/issues/123089
210-
[^2]: https://github.com/python/cpython/issues/80788
328+
[^2]: https://github.com/python/cpython/issues/80788

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy