https://github.com/python/cpython/commit/0a6c1ed34118b091230ee38fc047bcc2df8e5c5e
commit: 0a6c1ed34118b091230ee38fc047bcc2df8e5c5e
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-09-16T18:58:59+05:30
summary:
gh-157361: Implement `anext()` in Python instead of C (#157362)
files:
A Lib/_pybuiltins.py
A
Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-16-40-00.gh-issue-157361.anextpy.rst
M Include/internal/pycore_pylifecycle.h
M Lib/test/test_asyncgen.py
M Lib/test/test_asyncio/test_graph.py
M Lib/test/test_coroutines.py
M Lib/test/test_importlib/util.py
M Lib/test/test_inspect/test_inspect.py
M Makefile.pre.in
M Objects/iterobject.c
M Objects/object.c
M PCbuild/_freeze_module.vcxproj
M PCbuild/_freeze_module.vcxproj.filters
M Programs/_bootstrap_python.c
M Python/bltinmodule.c
M Python/clinic/bltinmodule.c.h
M Python/frozen.c
M Python/pylifecycle.c
M Python/stdlib_module_names.h
M Tools/build/freeze_modules.py
M Tools/c-analyzer/cpython/globals-to-fix.tsv
diff --git a/Include/internal/pycore_pylifecycle.h
b/Include/internal/pycore_pylifecycle.h
index ab627c28c1fa5ee..bfc94e3e8529b75 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -26,6 +26,7 @@ extern int _Py_IsLocaleCoercionTarget(const char *ctype_loc);
extern void _Py_InitVersion(void);
extern PyStatus _PyFaulthandler_Init(int enable);
extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp);
+extern int _PyBuiltin_InitPythonFunctions(PyObject *dict);
extern PyStatus _PySys_Create(
PyThreadState *tstate,
PyObject **sysmod_p);
diff --git a/Lib/_pybuiltins.py b/Lib/_pybuiltins.py
new file mode 100644
index 000000000000000..01da295f53d0c73
--- /dev/null
+++ b/Lib/_pybuiltins.py
@@ -0,0 +1,42 @@
+"""Builtins implemented in Python.
+
+This module is frozen into the interpreter and imported during startup,
+before the import system exists. The names listed in ``__all__`` are
+copied into the ``builtins`` module.
+"""
+
+__all__ = ['anext']
+
+_NOT_GIVEN = sentinel("_NOT_GIVEN")
+
+
+def anext(async_iterator, default=_NOT_GIVEN, /):
+ """Return the next item from the async iterator.
+
+ If default is given and the async iterator is exhausted,
+ it is returned instead of raising StopAsyncIteration.
+ """
+ cls = type(async_iterator)
+ try:
+ # Looked up on the type, like the C slot am_anext.
+ anext_method = cls.__anext__
+ except AttributeError:
+ raise TypeError(
+ f"{cls.__name__!r} object is not an async iterator"
+ ) from None
+ awaitable = anext_method(async_iterator)
+ if default is _NOT_GIVEN:
+ return awaitable
+ return _anext_with_default(awaitable, default)
+
+
+async def _anext_with_default(awaitable, default):
+ try:
+ return await awaitable
+ except StopAsyncIteration:
+ return default
+
+
+for _name in __all__:
+ globals()[_name].__module__ = 'builtins'
+del _name
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index cdae58b3e89ae36..b5e0891feb794e8 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -1,7 +1,9 @@
import inspect
+import traceback
import types
import unittest
import contextlib
+import warnings
from test.support.import_helper import import_module
from test.support import gc_collect, requires_working_socket
@@ -709,7 +711,16 @@ def test_send():
async def test_throw():
p = ait_class()
obj = anext(p, "completed")
- self.assertRaises(SyntaxError, obj.throw, SyntaxError)
+ with warnings.catch_warnings():
+ # Throwing into the unstarted anext() coroutine leaves the
+ # inner __anext__() awaitable never awaited.
+ warnings.simplefilter("ignore", RuntimeWarning)
+ self.assertRaises(SyntaxError, obj.throw, SyntaxError)
+ if isinstance(p, types.AsyncGeneratorType):
+ # The never-run asend() already registered the async
+ # generator with the loop's finalizer; close it explicitly
+ # so no aclose() task is left pending at loop close.
+ await p.aclose()
return "completed"
result = self.loop.run_until_complete(test_throw())
@@ -1036,6 +1047,40 @@ async def do_test():
result = self.loop.run_until_complete(do_test())
self.assertEqual(result, "completed")
+ def test_anext_traceback_filename(self):
+ # anext() is implemented in Python in Lib/_pybuiltins.py, which is
+ # frozen under the builtins ID, so its frames name builtins rather
+ # than the module they are frozen from.
+ def filenames(exc):
+ return [frame.filename
+ for frame in traceback.extract_tb(exc.__traceback__)]
+
+ class AIter:
+ def __aiter__(self):
+ return self
+ async def __anext__(self):
+ raise ZeroDivisionError
+
+ # assertRaises() drops the traceback, so catch the exceptions here.
+ async def do_test():
+ try:
+ anext(42, "default")
+ except TypeError as exc:
+ self.assertIn("<frozen builtins>", filenames(exc))
+ else:
+ self.fail("TypeError was not raised")
+
+ try:
+ await anext(AIter(), "default")
+ except ZeroDivisionError as exc:
+ self.assertIn("<frozen builtins>", filenames(exc))
+ else:
+ self.fail("ZeroDivisionError was not raised")
+ return "completed"
+
+ result = self.loop.run_until_complete(do_test())
+ self.assertEqual(result, "completed")
+
def test_anext_iter(self):
@types.coroutine
def _async_yield(v):
@@ -1132,9 +1177,13 @@ async def agenfn():
yield 'aaa'
agen = agenfn()
- with contextlib.closing(anext(agen, "default").__await__()) as g:
- with self.assertRaises(MyError):
- g.throw(MyError())
+ with warnings.catch_warnings():
+ # Throwing into the unstarted anext() coroutine leaves the
+ # inner asend() awaitable never awaited.
+ warnings.simplefilter("ignore", RuntimeWarning)
+ with contextlib.closing(anext(agen, "default").__await__()) as
g:
+ with self.assertRaises(MyError):
+ g.throw(MyError())
def run_test(test):
with self.subTest('pure-Python anext()'):
diff --git a/Lib/test/test_asyncio/test_graph.py
b/Lib/test/test_asyncio/test_graph.py
index a442a346ff06d91..1326ef50c149b0b 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -148,6 +148,46 @@ async def main():
'async generator
CallStackTestBase.test_stack_async_gen.<locals>.gen()',
stack_for_gen_nested_call[1])
+ async def test_stack_anext_default(self):
+ # anext() with a default wraps the awaitable in a coroutine, so the
+ # call graph of a suspended task sees through it into __anext__().
+
+ loop = asyncio.get_running_loop()
+ blocker = loop.create_future()
+
+ async def inner():
+ await blocker
+
+ class AIter:
+ def __aiter__(self):
+ return self
+
+ async def __anext__(self):
+ await inner()
+ return 1
+
+ async def main():
+ await anext(AIter(), None)
+
+ task = asyncio.create_task(main(), name='anext task')
+ await asyncio.sleep(0)
+ try:
+ stack = capture_test_stack(fut=task)
+ finally:
+ blocker.set_result(None)
+ await task
+
+ self.assertEqual(stack[0], [
+ 'T<anext task>',
+ [
+ 'a inner',
+ 'a __anext__',
+ 'a _anext_with_default',
+ 'a main',
+ ],
+ []
+ ])
+
def test_ag_frame_used_for_async_generator(self):
# Regression test for gh-148736: the ag_await branch of
# _build_graph_for_future must read ag_frame, not cr_frame.
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index ab854d56d5a3ebf..c5900da09501b36 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -1312,8 +1312,12 @@ async def __anext__(self):
def __aiter__(self):
return self
- with contextlib.closing(anext(A(), "a").__await__()) as
anext_awaitable:
- self.assertRaises(TypeError, anext_awaitable.close, 1)
+ with warnings.catch_warnings():
+ # Closing the unstarted anext() coroutine leaves the inner
+ # __anext__() coroutine never awaited.
+ warnings.simplefilter("ignore", RuntimeWarning)
+ with contextlib.closing(anext(A(), "a").__await__()) as
anext_awaitable:
+ self.assertRaises(TypeError, anext_awaitable.close, 1)
def test_with_1(self):
class Manager:
diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py
index 6399f952f9e912b..0cc0e651e45622b 100644
--- a/Lib/test/test_importlib/util.py
+++ b/Lib/test/test_importlib/util.py
@@ -69,7 +69,8 @@ def import_importlib(module_name):
fresh = ('importlib',) if '.' in module_name else ()
frozen = import_helper.import_fresh_module(module_name)
source = import_helper.import_fresh_module(module_name, fresh=fresh,
- blocked=('_frozen_importlib',
'_frozen_importlib_external'))
+ blocked=('_frozen_importlib',
'_frozen_importlib_external',
+ '_pybuiltins'))
return {'Frozen': frozen, 'Source': source}
diff --git a/Lib/test/test_inspect/test_inspect.py
b/Lib/test/test_inspect/test_inspect.py
index df5843abfcb8753..25276fc40cb0287 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -6174,7 +6174,7 @@ def test_builtins_have_signatures(self):
"next", "vars"}
no_signature |= needs_groups
# These have unrepresentable parameter default values of NULL
- unsupported_signature = {"anext", "aiter", "iter"}
+ unsupported_signature = {"aiter", "iter"}
# These need *args support in Argument Clinic
needs_varargs = {"min", "max", "__build_class__"}
no_signature |= needs_varargs
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 166087f32dff187..72e0ba3267d069d 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1623,7 +1623,8 @@ Programs/_testembed: Programs/_testembed.o
$(LINK_PYTHON_DEPS)
BOOTSTRAP_HEADERS = \
Python/frozen_modules/importlib._bootstrap.h \
Python/frozen_modules/importlib._bootstrap_external.h \
- Python/frozen_modules/zipimport.h
+ Python/frozen_modules/zipimport.h \
+ Python/frozen_modules/builtins.h
Programs/_bootstrap_python.o: Programs/_bootstrap_python.c
$(BOOTSTRAP_HEADERS) $(PYTHON_HEADERS)
@@ -1664,6 +1665,7 @@ FROZEN_FILES_IN = \
Lib/importlib/_bootstrap.py \
Lib/importlib/_bootstrap_external.py \
Lib/zipimport.py \
+ Lib/_pybuiltins.py \
Lib/abc.py \
Lib/codecs.py \
Lib/io.py \
@@ -1690,6 +1692,7 @@ FROZEN_FILES_OUT = \
Python/frozen_modules/importlib._bootstrap.h \
Python/frozen_modules/importlib._bootstrap_external.h \
Python/frozen_modules/zipimport.h \
+ Python/frozen_modules/builtins.h \
Python/frozen_modules/abc.h \
Python/frozen_modules/codecs.h \
Python/frozen_modules/io.h \
@@ -1735,6 +1738,9 @@ Python/frozen_modules/importlib._bootstrap_external.h:
Lib/importlib/_bootstrap_
Python/frozen_modules/zipimport.h: Lib/zipimport.py
$(FREEZE_MODULE_BOOTSTRAP_DEPS)
$(FREEZE_MODULE_BOOTSTRAP) zipimport $(srcdir)/Lib/zipimport.py
Python/frozen_modules/zipimport.h
+Python/frozen_modules/builtins.h: Lib/_pybuiltins.py
$(FREEZE_MODULE_BOOTSTRAP_DEPS)
+ $(FREEZE_MODULE_BOOTSTRAP) builtins $(srcdir)/Lib/_pybuiltins.py
Python/frozen_modules/builtins.h
+
Python/frozen_modules/abc.h: Lib/abc.py $(FREEZE_MODULE_DEPS)
$(FREEZE_MODULE) abc $(srcdir)/Lib/abc.py Python/frozen_modules/abc.h
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-16-40-00.gh-issue-157361.anextpy.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-16-40-00.gh-issue-157361.anextpy.rst
new file mode 100644
index 000000000000000..b56307056a7fff5
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-16-40-00.gh-issue-157361.anextpy.rst
@@ -0,0 +1,4 @@
+Implement :func:`anext` in Python instead of C, in a frozen ``_pybuiltins``
+module. The awaitable returned by ``anext(it, default)`` is now a plain
+coroutine, so introspection tools such as :func:`asyncio.print_call_graph`
+can see through it into :meth:`~object.__anext__`.
diff --git a/Objects/iterobject.c b/Objects/iterobject.c
index b5783c92c8eb689..2d5e3709a27dfb0 100644
--- a/Objects/iterobject.c
+++ b/Objects/iterobject.c
@@ -403,33 +403,6 @@ PyTypeObject PyCallIter_Type = {
/* -------------------------------------- */
-typedef struct {
- PyObject_HEAD
- PyObject *wrapped;
- PyObject *default_value;
-} anextawaitableobject;
-
-#define anextawaitableobject_CAST(op) ((anextawaitableobject *)(op))
-
-static void
-anextawaitable_dealloc(PyObject *op)
-{
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- _PyObject_GC_UNTRACK(obj);
- Py_XDECREF(obj->wrapped);
- Py_XDECREF(obj->default_value);
- PyObject_GC_Del(obj);
-}
-
-static int
-anextawaitable_traverse(PyObject *op, visitproc visit, void *arg)
-{
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- Py_VISIT(obj->wrapped);
- Py_VISIT(obj->default_value);
- return 0;
-}
-
static PyObject *
awaitable_getiter(PyObject *owner, PyObject *wrapped)
{
@@ -461,99 +434,6 @@ awaitable_getiter(PyObject *owner, PyObject *wrapped)
return awaitable;
}
-static PyObject *
-anextawaitable_iternext(PyObject *op)
-{
- /* Consider the following class:
- *
- * class A:
- * async def __anext__(self):
- * ...
- * a = A()
- *
- * Then `await anext(a)` should call
- * a.__anext__().__await__().__next__()
- *
- * On the other hand, given
- *
- * async def agen():
- * yield 1
- * yield 2
- * gen = agen()
- *
- * Then `await anext(gen)` can just call
- * gen.__anext__().__next__()
- */
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- PyObject *awaitable = awaitable_getiter(op, obj->wrapped);
- if (awaitable == NULL) {
- return NULL;
- }
- PyObject *result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);
- Py_DECREF(awaitable);
- if (result != NULL) {
- return result;
- }
- if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
- PyErr_Clear();
- _PyGen_SetStopIterationValue(obj->default_value);
- }
- return NULL;
-}
-
-
-static PyObject *
-anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg)
-{
- PyObject *awaitable = awaitable_getiter((PyObject *)obj, obj->wrapped);
- if (awaitable == NULL) {
- return NULL;
- }
- // When specified, 'arg' may be a tuple (if coming from a METH_VARARGS
- // method) or a single object (if coming from a METH_O method).
- PyObject *ret = arg == NULL
- ? PyObject_CallMethod(awaitable, meth, NULL)
- : PyObject_CallMethod(awaitable, meth, "O", arg);
- Py_DECREF(awaitable);
- if (ret != NULL) {
- return ret;
- }
- if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
- /* `anextawaitableobject` is only used by `anext()` when
- * a default value is provided. So when we have a StopAsyncIteration
- * exception we replace it with a `StopIteration(default)`, as if
- * it was the return value of `__anext__()` coroutine.
- */
- PyErr_Clear();
- _PyGen_SetStopIterationValue(obj->default_value);
- }
- return NULL;
-}
-
-
-static PyObject *
-anextawaitable_send(PyObject *op, PyObject *arg)
-{
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- return anextawaitable_proxy(obj, "send", arg);
-}
-
-
-static PyObject *
-anextawaitable_throw(PyObject *op, PyObject *args)
-{
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- return anextawaitable_proxy(obj, "throw", args);
-}
-
-
-static PyObject *
-anextawaitable_close(PyObject *op, PyObject *Py_UNUSED(dummy))
-{
- anextawaitableobject *obj = anextawaitableobject_CAST(op);
- return anextawaitable_proxy(obj, "close", NULL);
-}
-
PyDoc_STRVAR(send_doc,
"send(arg) -> send 'arg' into the wrapped iterator,\n\
@@ -574,68 +454,6 @@ PyDoc_STRVAR(close_doc,
"close() -> raise GeneratorExit inside generator.");
-static PyMethodDef anextawaitable_methods[] = {
- {"send", anextawaitable_send, METH_O, send_doc},
- {"throw", anextawaitable_throw, METH_VARARGS, throw_doc},
- {"close", anextawaitable_close, METH_NOARGS, close_doc},
- {NULL, NULL} /* Sentinel */
-};
-
-
-static PyAsyncMethods anextawaitable_as_async = {
- PyObject_SelfIter, /* am_await */
- 0, /* am_aiter */
- 0, /* am_anext */
- 0, /* am_send */
-};
-
-PyTypeObject _PyAnextAwaitable_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "anext_awaitable", /* tp_name */
- sizeof(anextawaitableobject), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- anextawaitable_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- &anextawaitable_as_async, /* tp_as_async */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
- 0, /* tp_doc */
- anextawaitable_traverse, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- PyObject_SelfIter, /* tp_iter */
- anextawaitable_iternext, /* tp_iternext */
- anextawaitable_methods, /* tp_methods */
-};
-
-PyObject *
-PyAnextAwaitable_New(PyObject *awaitable, PyObject *default_value)
-{
- anextawaitableobject *anext = PyObject_GC_New(
- anextawaitableobject, &_PyAnextAwaitable_Type);
- if (anext == NULL) {
- return NULL;
- }
- anext->wrapped = Py_NewRef(awaitable);
- anext->default_value = Py_NewRef(default_value);
- _PyObject_GC_TRACK(anext);
- return (PyObject *)anext;
-}
-
-
/* -------------------------------------- */
/* The asynchronous counterpart of calliterobject: the callable is called
diff --git a/Objects/object.c b/Objects/object.c
index a83f8d4c04ca079..e3f29b71301695e 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -2522,7 +2522,6 @@ _PyObject_FiniState(PyInterpreterState *interp)
extern PyTypeObject _PyACallIter_Type;
extern PyTypeObject _PyACallIterAwaitable_Type;
-extern PyTypeObject _PyAnextAwaitable_Type;
extern PyTypeObject _PyLegacyEventHandler_Type;
extern PyTypeObject _PyLineIterator;
extern PyTypeObject _PyMemoryIter_Type;
@@ -2617,7 +2616,6 @@ static PyTypeObject*
static_types[_Py_NUM_MANAGED_PREINITIALIZED_TYPES] = {
&Py_GenericAliasType,
&_PyACallIter_Type,
&_PyACallIterAwaitable_Type,
- &_PyAnextAwaitable_Type,
&_PyAsyncGenASend_Type,
&_PyAsyncGenAThrow_Type,
&_PyAsyncGenWrappedValue_Type,
diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj
index 70c54e0e41efc63..69833f132b5e4d4 100644
--- a/PCbuild/_freeze_module.vcxproj
+++ b/PCbuild/_freeze_module.vcxproj
@@ -306,6 +306,11 @@
<IntFile>$(IntDir)zipimport.g.h</IntFile>
<OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\zipimport.h</OutFile>
</None>
+ <None Include="..\Lib\_pybuiltins.py">
+ <ModName>builtins</ModName>
+ <IntFile>$(IntDir)builtins.g.h</IntFile>
+
<OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\builtins.h</OutFile>
+ </None>
<None Include="..\Lib\abc.py">
<ModName>abc</ModName>
<IntFile>$(IntDir)abc.g.h</IntFile>
diff --git a/PCbuild/_freeze_module.vcxproj.filters
b/PCbuild/_freeze_module.vcxproj.filters
index b0799b8dc9ecddb..207552113c3dd23 100644
--- a/PCbuild/_freeze_module.vcxproj.filters
+++ b/PCbuild/_freeze_module.vcxproj.filters
@@ -549,6 +549,9 @@
<None Include="..\Lib\zipimport.py">
<Filter>Python Files</Filter>
</None>
+ <None Include="..\Lib\_pybuiltins.py">
+ <Filter>Python Files</Filter>
+ </None>
<None Include="..\Lib\abc.py">
<Filter>Python Files</Filter>
</None>
diff --git a/Programs/_bootstrap_python.c b/Programs/_bootstrap_python.c
index 6443d814a22dabf..d30ef8c879d8153 100644
--- a/Programs/_bootstrap_python.c
+++ b/Programs/_bootstrap_python.c
@@ -13,6 +13,7 @@
#include "Python/frozen_modules/importlib._bootstrap.h"
#include "Python/frozen_modules/importlib._bootstrap_external.h"
#include "Python/frozen_modules/zipimport.h"
+#include "Python/frozen_modules/builtins.h"
/* End includes */
/* Note that a negative size indicates a package. */
@@ -21,6 +22,7 @@ static const struct _frozen bootstrap_modules[] = {
{"_frozen_importlib", _Py_M__importlib__bootstrap,
(int)sizeof(_Py_M__importlib__bootstrap)},
{"_frozen_importlib_external", _Py_M__importlib__bootstrap_external,
(int)sizeof(_Py_M__importlib__bootstrap_external)},
{"zipimport", _Py_M__zipimport, (int)sizeof(_Py_M__zipimport)},
+ {"_pybuiltins", _Py_M__builtins, (int)sizeof(_Py_M__builtins)},
{0, 0, 0} /* bootstrap sentinel */
};
static const struct _frozen stdlib_modules[] = {
@@ -36,6 +38,7 @@ const struct _frozen *_PyImport_FrozenTest = test_modules;
static const struct _module_alias aliases[] = {
{"_frozen_importlib", "importlib._bootstrap"},
{"_frozen_importlib_external", "importlib._bootstrap_external"},
+ {"_pybuiltins", "builtins"},
{0, 0} /* aliases sentinel */
};
const struct _module_alias *_PyImport_FrozenAliases = aliases;
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index d28e6fa9cd01aed..965cf20fe617787 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1960,52 +1960,6 @@ builtin_aiter_impl(PyObject *module, PyObject *object,
PyObject *stop_value,
return _PyACallIter_New(object, stop_value, stop_exception);
}
-PyObject *PyAnextAwaitable_New(PyObject *, PyObject *);
-
-/*[clinic input]
-anext as builtin_anext
-
- async_iterator as aiterator: object
- default: object = NULL
- /
-
-Return the next item from the async iterator.
-
-If default is given and the async iterator is exhausted,
-it is returned instead of raising StopAsyncIteration.
-[clinic start generated code]*/
-
-static PyObject *
-builtin_anext_impl(PyObject *module, PyObject *aiterator,
- PyObject *default_value)
-/*[clinic end generated code: output=f02c060c163a81fa input=f3dc5a93f073e5ac]*/
-{
- PyTypeObject *t;
- PyObject *awaitable;
-
- t = Py_TYPE(aiterator);
- if (t->tp_as_async == NULL || t->tp_as_async->am_anext == NULL) {
- PyErr_Format(PyExc_TypeError,
- "'%.200s' object is not an async iterator",
- t->tp_name);
- return NULL;
- }
-
- awaitable = (*t->tp_as_async->am_anext)(aiterator);
- if (awaitable == NULL) {
- return NULL;
- }
- if (default_value == NULL) {
- return awaitable;
- }
-
- PyObject* new_awaitable = PyAnextAwaitable_New(
- awaitable, default_value);
- Py_DECREF(awaitable);
- return new_awaitable;
-}
-
-
/*[clinic input]
len as builtin_len
@@ -3500,7 +3454,6 @@ static PyMethodDef builtin_methods[] = {
{"max", _PyCFunction_CAST(builtin_max), METH_FASTCALL | METH_KEYWORDS,
max_doc},
{"min", _PyCFunction_CAST(builtin_min), METH_FASTCALL | METH_KEYWORDS,
min_doc},
{"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc},
- BUILTIN_ANEXT_METHODDEF
BUILTIN_OCT_METHODDEF
BUILTIN_ORD_METHODDEF
BUILTIN_POW_METHODDEF
@@ -3539,6 +3492,57 @@ static struct PyModuleDef builtinsmodule = {
};
+/* Builtins implemented in Python.
+
+ Lib/_pybuiltins.py is frozen into the interpreter as a bootstrap module
+ (see Tools/build/freeze_modules.py), so it can be imported here before
+ the import system exists. The names in its __all__ are copied into the
+ builtins dict. */
+
+int
+_PyBuiltin_InitPythonFunctions(PyObject *dict)
+{
+ if (PyImport_ImportFrozenModule("_pybuiltins") <= 0) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError,
+ "frozen module _pybuiltins not found");
+ }
+ return -1;
+ }
+ PyObject *mod = PyImport_AddModuleRef("_pybuiltins");
+ if (mod == NULL) {
+ return -1;
+ }
+
+ int rc = -1;
+ PyObject *all = PyObject_GetAttr(mod, &_Py_ID(__all__));
+ if (all == NULL) {
+ goto done;
+ }
+ Py_ssize_t n = PyList_Size(all);
+ if (n < 0) {
+ goto done;
+ }
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyObject *name = PyList_GET_ITEM(all, i);
+ PyObject *func = PyObject_GetAttr(mod, name);
+ if (func == NULL) {
+ goto done;
+ }
+ int r = PyDict_SetItem(dict, name, func);
+ Py_DECREF(func);
+ if (r < 0) {
+ goto done;
+ }
+ }
+ rc = 0;
+
+done:
+ Py_XDECREF(all);
+ Py_DECREF(mod);
+ return rc;
+}
+
PyObject *
_PyBuiltin_Init(PyInterpreterState *interp)
{
diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h
index c10bb03d8178161..5858ca9ff88ec22 100644
--- a/Python/clinic/bltinmodule.c.h
+++ b/Python/clinic/bltinmodule.c.h
@@ -1011,44 +1011,6 @@ builtin_aiter(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObjec
return return_value;
}
-PyDoc_STRVAR(builtin_anext__doc__,
-"anext($module, async_iterator, default=<unrepresentable>, /)\n"
-"--\n"
-"\n"
-"Return the next item from the async iterator.\n"
-"\n"
-"If default is given and the async iterator is exhausted,\n"
-"it is returned instead of raising StopAsyncIteration.");
-
-#define BUILTIN_ANEXT_METHODDEF \
- {"anext", _PyCFunction_CAST(builtin_anext), METH_FASTCALL,
builtin_anext__doc__},
-
-static PyObject *
-builtin_anext_impl(PyObject *module, PyObject *aiterator,
- PyObject *default_value);
-
-static PyObject *
-builtin_anext(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
-{
- PyObject *return_value = NULL;
- PyObject *aiterator;
- PyObject *default_value = NULL;
-
- if (!_PyArg_CheckPositional("anext", nargs, 1, 2)) {
- goto exit;
- }
- aiterator = args[0];
- if (nargs < 2) {
- goto skip_optional;
- }
- default_value = args[1];
-skip_optional:
- return_value = builtin_anext_impl(module, aiterator, default_value);
-
-exit:
- return return_value;
-}
-
PyDoc_STRVAR(builtin_len__doc__,
"len($module, obj, /)\n"
"--\n"
@@ -1539,4 +1501,4 @@ builtin_issubclass(PyObject *module, PyObject *const
*args, Py_ssize_t nargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=5fb1ac6a4253ee2f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b56739f2e13f616a input=a9049054013a1b77]*/
diff --git a/Python/frozen.c b/Python/frozen.c
index 9433d90c15e2eca..1f92ea01dc38bbc 100644
--- a/Python/frozen.c
+++ b/Python/frozen.c
@@ -44,6 +44,7 @@
#include "frozen_modules/importlib._bootstrap.h"
#include "frozen_modules/importlib._bootstrap_external.h"
#include "frozen_modules/zipimport.h"
+#include "frozen_modules/builtins.h"
#include "frozen_modules/abc.h"
#include "frozen_modules/codecs.h"
#include "frozen_modules/io.h"
@@ -71,6 +72,7 @@ static const struct _frozen bootstrap_modules[] = {
{"_frozen_importlib", _Py_M__importlib__bootstrap,
(int)sizeof(_Py_M__importlib__bootstrap), false},
{"_frozen_importlib_external", _Py_M__importlib__bootstrap_external,
(int)sizeof(_Py_M__importlib__bootstrap_external), false},
{"zipimport", _Py_M__zipimport, (int)sizeof(_Py_M__zipimport), false},
+ {"_pybuiltins", _Py_M__builtins, (int)sizeof(_Py_M__builtins), false},
{0, 0, 0} /* bootstrap sentinel */
};
static const struct _frozen stdlib_modules[] = {
@@ -119,6 +121,7 @@ const struct _frozen *_PyImport_FrozenTest = test_modules;
static const struct _module_alias aliases[] = {
{"_frozen_importlib", "importlib._bootstrap"},
{"_frozen_importlib_external", "importlib._bootstrap_external"},
+ {"_pybuiltins", "builtins"},
{"__hello_alias__", "__hello__"},
{"__phello_alias__", "__hello__"},
{"__phello_alias__.spam", "__hello__"},
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 500a1a1949a5a8a..3feb06915a59c2c 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -924,6 +924,16 @@ pycore_init_builtins(PyThreadState *tstate)
return _PyStatus_ERR("failed to add exceptions to builtins");
}
+ /* The Python-implemented builtins live in the frozen _pybuiltins module.
+ Programs/_freeze_module has no frozen modules (it's what creates
+ them) and opts out via _install_importlib, like the import system. */
+ const PyConfig *config = _PyInterpreterState_GetConfig(interp);
+ if (config->_install_importlib) {
+ if (_PyBuiltin_InitPythonFunctions(builtins_dict) < 0) {
+ return _PyStatus_ERR("failed to add Python-implemented builtins");
+ }
+ }
+
interp->builtins_copy = PyDict_Copy(interp->builtins);
if (interp->builtins_copy == NULL) {
goto error;
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index 8937e666bbbdd5b..565be27b7cebda5 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -65,6 +65,7 @@ static const char* _Py_stdlib_module_names[] = {
"_posixsubprocess",
"_py_abc",
"_py_warnings",
+"_pybuiltins",
"_pydatetime",
"_pydecimal",
"_pyio",
diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py
index a866336fa78879e..8c817daec0f0b37 100644
--- a/Tools/build/freeze_modules.py
+++ b/Tools/build/freeze_modules.py
@@ -16,6 +16,9 @@
FROZEN_ONLY = os.path.join(ROOT_DIR, 'Tools', 'freeze', 'flag.py')
STDLIB_DIR = os.path.join(ROOT_DIR, 'Lib')
+# Frozen under the "builtins" ID rather than its own name, so that the frames
+# of the builtins it defines show up as "<frozen builtins>" in tracebacks.
+PYBUILTINS = os.path.join(STDLIB_DIR, '_pybuiltins.py')
# If FROZEN_MODULES_DIR or DEEPFROZEN_MODULES_DIR is changed then the
# .gitattributes and .gitignore files needs to be updated.
FROZEN_MODULES_DIR = os.path.join(ROOT_DIR, 'Python', 'frozen_modules')
@@ -45,6 +48,8 @@
# This module is important because some Python builds rely
# on a builtin zip file instead of a filesystem.
'zipimport',
+ # Builtins implemented in Python; loaded while builtins is set up.
+ f'builtins : _pybuiltins = {PYBUILTINS}',
]),
# (You can delete entries from here down to the end of the list.)
('stdlib - startup, without site (python -S)', [
@@ -91,6 +96,7 @@
'importlib._bootstrap',
'importlib._bootstrap_external',
'zipimport',
+ 'builtins',
}
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv
b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index b8488899c4595de..67ced170243e4a2 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -60,7 +60,6 @@ Objects/iterobject.c - PyCallIter_Type -
Objects/iterobject.c - PySeqIter_Type -
Objects/iterobject.c - _PyACallIter_Type -
Objects/iterobject.c - _PyACallIterAwaitable_Type -
-Objects/iterobject.c - _PyAnextAwaitable_Type -
Objects/lazyimportobject.c - PyLazyImport_Type -
Objects/listobject.c - PyListIter_Type -
Objects/listobject.c - PyListRevIter_Type -
@@ -77,7 +76,6 @@ Objects/object.c - _PyNone_Type -
Objects/object.c - _PyNotImplemented_Type -
Objects/object.c - _PyACallIter_Type -
Objects/object.c - _PyACallIterAwaitable_Type -
-Objects/object.c - _PyAnextAwaitable_Type -
Objects/odictobject.c - PyODictItems_Type -
Objects/odictobject.c - PyODictIter_Type -
Objects/odictobject.c - PyODictKeys_Type -
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]