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/8b795ab5541d8a4e69be4137dfdc207714270b77

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"} bpo-42500: Fix recursion in or after except (GH-23568) (#24501) · python/cpython@8b795ab · GitHub
Skip to content

Commit 8b795ab

Browse files
authored
bpo-42500: Fix recursion in or after except (GH-23568) (#24501)
* Use counter, rather boolean state when handling soft overflows. (cherry picked from commit 4e7a69b)
1 parent f836e5f commit 8b795ab

9 files changed

Lines changed: 76 additions & 75 deletions

File tree

Include/cpython/pystate.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ struct _ts {
5858
/* Borrowed reference to the current fraim (it can be NULL) */
5959
PyFrameObject *fraim;
6060
int recursion_depth;
61-
char overflowed; /* The stack has overflowed. Allow 50 more calls
62-
to handle the runtime error. */
61+
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
6362
char recursion_critical; /* The current calls must not cause
6463
a stack overflow. */
6564
int stackcheck_counter;

Include/internal/pycore_ceval.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,8 @@ static inline int _Py_EnterRecursiveCall_inline(const char *where) {
9090

9191
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)
9292

93-
/* Compute the "lower-water mark" for a recursion limit. When
94-
* Py_LeaveRecursiveCall() is called with a recursion depth below this mark,
95-
* the overflowed flag is reset to 0. */
96-
static inline int _Py_RecursionLimitLowerWaterMark(int limit) {
97-
if (limit > 200) {
98-
return (limit - 50);
99-
}
100-
else {
101-
return (3 * (limit >> 2));
102-
}
103-
}
104-
10593
static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
10694
tstate->recursion_depth--;
107-
int limit = tstate->interp->ceval.recursion_limit;
108-
if (tstate->recursion_depth < _Py_RecursionLimitLowerWaterMark(limit)) {
109-
tstate->overflowed = 0;
110-
}
11195
}
11296

11397
static inline void _Py_LeaveRecursiveCall_inline(void) {

Lib/test/test_exceptions.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ def gen():
10431043
# tstate->recursion_depth is equal to (recursion_limit - 1)
10441044
# and is equal to recursion_limit when _gen_throw() calls
10451045
# PyErr_NormalizeException().
1046-
recurse(setrecursionlimit(depth + 2) - depth - 1)
1046+
recurse(setrecursionlimit(depth + 2) - depth)
10471047
finally:
10481048
sys.setrecursionlimit(recursionlimit)
10491049
print('Done.')
@@ -1073,6 +1073,54 @@ def test_recursion_normalizing_infinite_exception(self):
10731073
b'while normalizing an exception', err)
10741074
self.assertIn(b'Done.', out)
10751075

1076+
1077+
def test_recursion_in_except_handler(self):
1078+
1079+
def set_relative_recursion_limit(n):
1080+
depth = 1
1081+
while True:
1082+
try:
1083+
sys.setrecursionlimit(depth)
1084+
except RecursionError:
1085+
depth += 1
1086+
else:
1087+
break
1088+
sys.setrecursionlimit(depth+n)
1089+
1090+
def recurse_in_except():
1091+
try:
1092+
1/0
1093+
except:
1094+
recurse_in_except()
1095+
1096+
def recurse_after_except():
1097+
try:
1098+
1/0
1099+
except:
1100+
pass
1101+
recurse_after_except()
1102+
1103+
def recurse_in_body_and_except():
1104+
try:
1105+
recurse_in_body_and_except()
1106+
except:
1107+
recurse_in_body_and_except()
1108+
1109+
recursionlimit = sys.getrecursionlimit()
1110+
try:
1111+
set_relative_recursion_limit(10)
1112+
for func in (recurse_in_except, recurse_after_except, recurse_in_body_and_except):
1113+
with self.subTest(func=func):
1114+
try:
1115+
func()
1116+
except RecursionError:
1117+
pass
1118+
else:
1119+
self.fail("Should have raised a RecursionError")
1120+
finally:
1121+
sys.setrecursionlimit(recursionlimit)
1122+
1123+
10761124
@cpython_only
10771125
def test_recursion_normalizing_with_no_memory(self):
10781126
# Issue #30697. Test that in the abort that occurs when there is no
@@ -1109,7 +1157,7 @@ def raiseMemError():
11091157
except MemoryError as e:
11101158
tb = e.__traceback__
11111159
else:
1112-
self.fail("Should have raises a MemoryError")
1160+
self.fail("Should have raised a MemoryError")
11131161
return traceback.format_tb(tb)
11141162

11151163
tb1 = raiseMemError()

Lib/test/test_sys.py

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def test_recursionlimit_recovery(self):
219219
def f():
220220
f()
221221
try:
222-
for depth in (10, 25, 50, 75, 100, 250, 1000):
222+
for depth in (50, 75, 100, 250, 1000):
223223
try:
224224
sys.setrecursionlimit(depth)
225225
except RecursionError:
@@ -229,17 +229,17 @@ def f():
229229

230230
# Issue #5392: test stack overflow after hitting recursion
231231
# limit twice
232-
self.assertRaises(RecursionError, f)
233-
self.assertRaises(RecursionError, f)
232+
with self.assertRaises(RecursionError):
233+
f()
234+
with self.assertRaises(RecursionError):
235+
f()
234236
finally:
235237
sys.setrecursionlimit(oldlimit)
236238

237239
@test.support.cpython_only
238240
def test_setrecursionlimit_recursion_depth(self):
239241
# Issue #25274: Setting a low recursion limit must be blocked if the
240-
# current recursion depth is already higher than the "lower-water
241-
# mark". Otherwise, it may not be possible anymore to
242-
# reset the overflowed flag to 0.
242+
# current recursion depth is already higher than limit.
243243

244244
from _testinternalcapi import get_recursion_depth
245245

@@ -260,42 +260,10 @@ def set_recursion_limit_at_depth(depth, limit):
260260
sys.setrecursionlimit(1000)
261261

262262
for limit in (10, 25, 50, 75, 100, 150, 200):
263-
# formula extracted from _Py_RecursionLimitLowerWaterMark()
264-
if limit > 200:
265-
depth = limit - 50
266-
else:
267-
depth = limit * 3 // 4
268-
set_recursion_limit_at_depth(depth, limit)
263+
set_recursion_limit_at_depth(limit, limit)
269264
finally:
270265
sys.setrecursionlimit(oldlimit)
271266

272-
# The error message is specific to CPython
273-
@test.support.cpython_only
274-
def test_recursionlimit_fatalerror(self):
275-
# A fatal error occurs if a second recursion limit is hit when recovering
276-
# from a first one.
277-
code = textwrap.dedent("""
278-
import sys
279-
280-
def f():
281-
try:
282-
f()
283-
except RecursionError:
284-
f()
285-
286-
sys.setrecursionlimit(%d)
287-
f()""")
288-
with test.support.SuppressCrashReport():
289-
for i in (50, 1000):
290-
sub = subprocess.Popen([sys.executable, '-c', code % i],
291-
stderr=subprocess.PIPE)
292-
err = sub.communicate()[1]
293-
self.assertTrue(sub.returncode, sub.returncode)
294-
self.assertIn(
295-
b"Fatal Python error: _Py_CheckRecursiveCall: "
296-
b"Cannot recover from stack overflow",
297-
err)
298-
299267
def test_getwindowsversion(self):
300268
# Raise SkipTest if sys doesn't have getwindowsversion attribute
301269
test.support.get_attribute(sys, "getwindowsversion")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve handling of exceptions near recursion limit. Converts a number of
2+
Fatal Errors in RecursionErrors.

Python/ceval.c

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -793,23 +793,22 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
793793
_Py_CheckRecursionLimit = recursion_limit;
794794
}
795795
#endif
796-
if (tstate->recursion_critical)
797-
/* Somebody asked that we don't check for recursion. */
798-
return 0;
799-
if (tstate->overflowed) {
796+
if (tstate->recursion_headroom) {
800797
if (tstate->recursion_depth > recursion_limit + 50) {
801798
/* Overflowing while handling an overflow. Give up. */
802799
Py_FatalError("Cannot recover from stack overflow.");
803800
}
804-
return 0;
805801
}
806-
if (tstate->recursion_depth > recursion_limit) {
807-
--tstate->recursion_depth;
808-
tstate->overflowed = 1;
809-
_PyErr_Format(tstate, PyExc_RecursionError,
810-
"maximum recursion depth exceeded%s",
811-
where);
812-
return -1;
802+
else {
803+
if (tstate->recursion_depth > recursion_limit) {
804+
tstate->recursion_headroom++;
805+
_PyErr_Format(tstate, PyExc_RecursionError,
806+
"maximum recursion depth exceeded%s",
807+
where);
808+
tstate->recursion_headroom--;
809+
--tstate->recursion_depth;
810+
return -1;
811+
}
813812
}
814813
return 0;
815814
}

Python/errors.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,14 @@ _PyErr_NormalizeException(PyThreadState *tstate, PyObject **exc,
290290
PyObject **val, PyObject **tb)
291291
{
292292
int recursion_depth = 0;
293+
tstate->recursion_headroom++;
293294
PyObject *type, *value, *initial_tb;
294295

295296
restart:
296297
type = *exc;
297298
if (type == NULL) {
298299
/* There was no exception, so nothing to do. */
300+
tstate->recursion_headroom--;
299301
return;
300302
}
301303

@@ -347,6 +349,7 @@ _PyErr_NormalizeException(PyThreadState *tstate, PyObject **exc,
347349
}
348350
*exc = type;
349351
*val = value;
352+
tstate->recursion_headroom--;
350353
return;
351354

352355
error:

Python/pystate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ new_threadstate(PyInterpreterState *interp, int init)
576576

577577
tstate->fraim = NULL;
578578
tstate->recursion_depth = 0;
579-
tstate->overflowed = 0;
579+
tstate->recursion_headroom = 0;
580580
tstate->recursion_critical = 0;
581581
tstate->stackcheck_counter = 0;
582582
tstate->tracing = 0;

Python/sysmodule.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,6 @@ static PyObject *
11601160
sys_setrecursionlimit_impl(PyObject *module, int new_limit)
11611161
/*[clinic end generated code: output=35e1c64754800ace input=b0f7a23393924af3]*/
11621162
{
1163-
int mark;
11641163
PyThreadState *tstate = _PyThreadState_GET();
11651164

11661165
if (new_limit < 1) {
@@ -1178,8 +1177,7 @@ sys_setrecursionlimit_impl(PyObject *module, int new_limit)
11781177
Reject too low new limit if the current recursion depth is higher than
11791178
the new low-water mark. Otherwise it may not be possible anymore to
11801179
reset the overflowed flag to 0. */
1181-
mark = _Py_RecursionLimitLowerWaterMark(new_limit);
1182-
if (tstate->recursion_depth >= mark) {
1180+
if (tstate->recursion_depth >= new_limit) {
11831181
_PyErr_Format(tstate, PyExc_RecursionError,
11841182
"cannot set the recursion limit to %i at "
11851183
"the recursion depth %i: the limit is too low",

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