From e6bd93faac6f54fac8369ba2ed1176c26c593765 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sat, 11 Jul 2026 16:51:51 +0100 Subject: [PATCH 1/2] gh-153568: Pause the GC while converting the AST to Python objects Freshly converted AST nodes cannot be part of a reference cycle yet, so collections during the conversion only pay to traverse the half-built tree without ever freeing any of it. --- .../2026-07-11-16-51-51.gh-issue-153568.ast2objgc.rst | 2 ++ Parser/asdl_c.py | 8 ++++++++ Python/Python-ast.c | 8 ++++++++ 3 files changed, 18 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-16-51-51.gh-issue-153568.ast2objgc.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-16-51-51.gh-issue-153568.ast2objgc.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-16-51-51.gh-issue-153568.ast2objgc.rst new file mode 100644 index 000000000000000..b7d413d422994ff --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-16-51-51.gh-issue-153568.ast2objgc.rst @@ -0,0 +1,2 @@ +Speed up :func:`ast.parse` by pausing the garbage collector while the AST is +converted to Python objects. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 08a433277660794..6ac8c4a0ca0a5e3 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -2116,7 +2116,15 @@ class PartingShots(StaticVisitor): if (state == NULL) { return NULL; } + // Freshly converted AST nodes cannot be part of a reference cycle yet, + // so a garbage collection while building them cannot free anything of + // this tree and only pays to traverse its half-built nodes. Pause the + // collector while the conversion runs. + int gc_was_enabled = PyGC_Disable(); PyObject *result = ast2obj_mod(state, t); + if (gc_was_enabled) { + PyGC_Enable(); + } return result; } diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 87f160ed19301ea..4af892eb4a0a76f 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -18543,7 +18543,15 @@ PyObject* PyAST_mod2obj(mod_ty t) if (state == NULL) { return NULL; } + // Freshly converted AST nodes cannot be part of a reference cycle yet, + // so a garbage collection while building them cannot free anything of + // this tree and only pays to traverse its half-built nodes. Pause the + // collector while the conversion runs. + int gc_was_enabled = PyGC_Disable(); PyObject *result = ast2obj_mod(state, t); + if (gc_was_enabled) { + PyGC_Enable(); + } return result; } From e00fbc2c76eae521b24aeb99979403de440f6fa1 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Wed, 26 Aug 2026 01:03:25 +0100 Subject: [PATCH 2/2] gh-153568: Skip GC pause without the GIL --- Parser/asdl_c.py | 7 ++++++- Python/Python-ast.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 6ac8c4a0ca0a5e3..daddeaf1388686f 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -2116,15 +2116,20 @@ class PartingShots(StaticVisitor): if (state == NULL) { return NULL; } +#ifndef Py_GIL_DISABLED // Freshly converted AST nodes cannot be part of a reference cycle yet, // so a garbage collection while building them cannot free anything of // this tree and only pays to traverse its half-built nodes. Pause the - // collector while the conversion runs. + // collector while the conversion runs. PyGC_Disable() is interpreter-wide, + // so this optimization is not safe in a free-threaded build. int gc_was_enabled = PyGC_Disable(); +#endif PyObject *result = ast2obj_mod(state, t); +#ifndef Py_GIL_DISABLED if (gc_was_enabled) { PyGC_Enable(); } +#endif return result; } diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 4af892eb4a0a76f..48deed0be90daae 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -18543,15 +18543,20 @@ PyObject* PyAST_mod2obj(mod_ty t) if (state == NULL) { return NULL; } +#ifndef Py_GIL_DISABLED // Freshly converted AST nodes cannot be part of a reference cycle yet, // so a garbage collection while building them cannot free anything of // this tree and only pays to traverse its half-built nodes. Pause the - // collector while the conversion runs. + // collector while the conversion runs. PyGC_Disable() is interpreter-wide, + // so this optimization is not safe in a free-threaded build. int gc_was_enabled = PyGC_Disable(); +#endif PyObject *result = ast2obj_mod(state, t); +#ifndef Py_GIL_DISABLED if (gc_was_enabled) { PyGC_Enable(); } +#endif return result; }