[Python-checkins] bpo-47039: Normalize repr() of asyncio future and task objects (GH-31950)

asvetlov webhook-mailer at python.org
Wed Mar 16 21:03:16 EDT 2022


https://github.com/python/cpython/commit/30b5d41fabad04f9f34d603f1ce2249452c18c71
commit: 30b5d41fabad04f9f34d603f1ce2249452c18c71
branch: main
author: Andrew Svetlov <andrew.svetlov at gmail.com>
committer: asvetlov <andrew.svetlov at gmail.com>
date: 2022-03-17T03:03:09+02:00
summary:

bpo-47039: Normalize repr() of asyncio future and task objects (GH-31950)

files:
A Misc/NEWS.d/next/Library/2022-03-17-01-54-13.bpo-47039.0Yxv0K.rst
M Lib/asyncio/base_futures.py
M Lib/asyncio/base_tasks.py
M Lib/asyncio/futures.py
M Lib/asyncio/tasks.py
M Modules/_asynciomodule.c
M Modules/clinic/_asynciomodule.c.h

diff --git a/Lib/asyncio/base_futures.py b/Lib/asyncio/base_futures.py
index 2c01ac98e10fc..cd811a788cb6d 100644
--- a/Lib/asyncio/base_futures.py
+++ b/Lib/asyncio/base_futures.py
@@ -42,16 +42,6 @@ def format_cb(callback):
     return f'cb=[{cb}]'
 
 
-# bpo-42183: _repr_running is needed for repr protection
-# when a Future or Task result contains itself directly or indirectly.
-# The logic is borrowed from @reprlib.recursive_repr decorator.
-# Unfortunately, the direct decorator usage is impossible because of
-# AttributeError: '_asyncio.Task' object has no attribute '__module__' error.
-#
-# After fixing this thing we can return to the decorator based approach.
-_repr_running = set()
-
-
 def _future_repr_info(future):
     # (Future) -> str
     """helper function for Future.__repr__"""
@@ -60,17 +50,9 @@ def _future_repr_info(future):
         if future._exception is not None:
             info.append(f'exception={future._exception!r}')
         else:
-            key = id(future), get_ident()
-            if key in _repr_running:
-                result = '...'
-            else:
-                _repr_running.add(key)
-                try:
-                    # use reprlib to limit the length of the output, especially
-                    # for very long strings
-                    result = reprlib.repr(future._result)
-                finally:
-                    _repr_running.discard(key)
+            # use reprlib to limit the length of the output, especially
+            # for very long strings
+            result = reprlib.repr(future._result)
             info.append(f'result={result}')
     if future._callbacks:
         info.append(_format_callbacks(future._callbacks))
@@ -78,3 +60,9 @@ def _future_repr_info(future):
         frame = future._source_traceback[-1]
         info.append(f'created at {frame[0]}:{frame[1]}')
     return info
+
+
+ at reprlib.recursive_repr()
+def _future_repr(future):
+    info = ' '.join(_future_repr_info(future))
+    return f'<{future.__class__.__name__} {info}>'
diff --git a/Lib/asyncio/base_tasks.py b/Lib/asyncio/base_tasks.py
index 1d623899f69a9..26298e638cbf0 100644
--- a/Lib/asyncio/base_tasks.py
+++ b/Lib/asyncio/base_tasks.py
@@ -1,4 +1,5 @@
 import linecache
+import reprlib
 import traceback
 
 from . import base_futures
@@ -22,6 +23,12 @@ def _task_repr_info(task):
     return info
 
 
+ at reprlib.recursive_repr()
+def _task_repr(task):
+    info = ' '.join(_task_repr_info(task))
+    return f'<{task.__class__.__name__} {info}>'
+
+
 def _task_get_stack(task, limit):
     frames = []
     if hasattr(task._coro, 'cr_frame'):
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 5a92f1769fa9f..bfd4ee926f028 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -85,11 +85,8 @@ def __init__(self, *, loop=None):
             self._source_traceback = format_helpers.extract_stack(
                 sys._getframe(1))
 
-    _repr_info = base_futures._future_repr_info
-
     def __repr__(self):
-        return '<{} {}>'.format(self.__class__.__name__,
-                                ' '.join(self._repr_info()))
+        return base_futures._future_repr(self)
 
     def __del__(self):
         if not self.__log_traceback:
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index b4f1eed91a932..0b5f3226802de 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -133,8 +133,8 @@ def __del__(self):
 
     __class_getitem__ = classmethod(GenericAlias)
 
-    def _repr_info(self):
-        return base_tasks._task_repr_info(self)
+    def __repr__(self):
+        return base_tasks._task_repr(self)
 
     def get_coro(self):
         return self._coro
diff --git a/Misc/NEWS.d/next/Library/2022-03-17-01-54-13.bpo-47039.0Yxv0K.rst b/Misc/NEWS.d/next/Library/2022-03-17-01-54-13.bpo-47039.0Yxv0K.rst
new file mode 100644
index 0000000000000..66785e1617c7c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-03-17-01-54-13.bpo-47039.0Yxv0K.rst
@@ -0,0 +1 @@
+Normalize ``repr()`` of asyncio future and task objects.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 4b12744e625e1..24119782d9c4d 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -29,11 +29,11 @@ _Py_IDENTIFIER(throw);
 static PyObject *asyncio_mod;
 static PyObject *traceback_extract_stack;
 static PyObject *asyncio_get_event_loop_policy;
-static PyObject *asyncio_future_repr_info_func;
+static PyObject *asyncio_future_repr_func;
 static PyObject *asyncio_iscoroutine_func;
 static PyObject *asyncio_task_get_stack_func;
 static PyObject *asyncio_task_print_stack_func;
-static PyObject *asyncio_task_repr_info_func;
+static PyObject *asyncio_task_repr_func;
 static PyObject *asyncio_InvalidStateError;
 static PyObject *asyncio_CancelledError;
 static PyObject *context_kwname;
@@ -1360,6 +1360,13 @@ FutureObj_get_state(FutureObj *fut, void *Py_UNUSED(ignored))
     return ret;
 }
 
+static PyObject *
+FutureObj_repr(FutureObj *fut)
+{
+    ENSURE_FUTURE_ALIVE(fut)
+    return PyObject_CallOneArg(asyncio_future_repr_func, (PyObject *)fut);
+}
+
 /*[clinic input]
 _asyncio.Future._make_cancelled_error
 
@@ -1376,42 +1383,6 @@ _asyncio_Future__make_cancelled_error_impl(FutureObj *self)
     return create_cancelled_error(self);
 }
 
-/*[clinic input]
-_asyncio.Future._repr_info
-[clinic start generated code]*/
-
-static PyObject *
-_asyncio_Future__repr_info_impl(FutureObj *self)
-/*[clinic end generated code: output=fa69e901bd176cfb input=f21504d8e2ae1ca2]*/
-{
-    return PyObject_CallOneArg(asyncio_future_repr_info_func, (PyObject *)self);
-}
-
-static PyObject *
-FutureObj_repr(FutureObj *fut)
-{
-    _Py_IDENTIFIER(_repr_info);
-
-    ENSURE_FUTURE_ALIVE(fut)
-
-    PyObject *rinfo = _PyObject_CallMethodIdNoArgs((PyObject*)fut,
-                                                   &PyId__repr_info);
-    if (rinfo == NULL) {
-        return NULL;
-    }
-
-    PyObject *rinfo_s = PyUnicode_Join(NULL, rinfo);
-    Py_DECREF(rinfo);
-    if (rinfo_s == NULL) {
-        return NULL;
-    }
-
-    PyObject *rstr = PyUnicode_FromFormat("<%s %U>",
-                                          _PyType_Name(Py_TYPE(fut)), rinfo_s);
-    Py_DECREF(rinfo_s);
-    return rstr;
-}
-
 static void
 FutureObj_finalize(FutureObj *fut)
 {
@@ -1497,7 +1468,6 @@ static PyMethodDef FutureType_methods[] = {
     _ASYNCIO_FUTURE_DONE_METHODDEF
     _ASYNCIO_FUTURE_GET_LOOP_METHODDEF
     _ASYNCIO_FUTURE__MAKE_CANCELLED_ERROR_METHODDEF
-    _ASYNCIO_FUTURE__REPR_INFO_METHODDEF
     {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
     {NULL, NULL}        /* Sentinel */
 };
@@ -2145,6 +2115,13 @@ TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(ignored))
     Py_RETURN_NONE;
 }
 
+static PyObject *
+TaskObj_repr(TaskObj *task)
+{
+    return PyObject_CallOneArg(asyncio_task_repr_func, (PyObject *)task);
+}
+
+
 /*[clinic input]
 _asyncio.Task._make_cancelled_error
 
@@ -2163,17 +2140,6 @@ _asyncio_Task__make_cancelled_error_impl(TaskObj *self)
 }
 
 
-/*[clinic input]
-_asyncio.Task._repr_info
-[clinic start generated code]*/
-
-static PyObject *
-_asyncio_Task__repr_info_impl(TaskObj *self)
-/*[clinic end generated code: output=6a490eb66d5ba34b input=3c6d051ed3ddec8b]*/
-{
-    return PyObject_CallOneArg(asyncio_task_repr_info_func, (PyObject *)self);
-}
-
 /*[clinic input]
 _asyncio.Task.cancel
 
@@ -2514,7 +2480,6 @@ static PyMethodDef TaskType_methods[] = {
     _ASYNCIO_TASK_GET_STACK_METHODDEF
     _ASYNCIO_TASK_PRINT_STACK_METHODDEF
     _ASYNCIO_TASK__MAKE_CANCELLED_ERROR_METHODDEF
-    _ASYNCIO_TASK__REPR_INFO_METHODDEF
     _ASYNCIO_TASK_GET_NAME_METHODDEF
     _ASYNCIO_TASK_SET_NAME_METHODDEF
     _ASYNCIO_TASK_GET_CORO_METHODDEF
@@ -2539,7 +2504,7 @@ static PyTypeObject TaskType = {
     .tp_base = &FutureType,
     .tp_dealloc = TaskObj_dealloc,
     .tp_as_async = &FutureType_as_async,
-    .tp_repr = (reprfunc)FutureObj_repr,
+    .tp_repr = (reprfunc)TaskObj_repr,
     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
     .tp_doc = _asyncio_Task___init____doc__,
     .tp_traverse = (traverseproc)TaskObj_traverse,
@@ -3337,12 +3302,12 @@ module_free(void *m)
 {
     Py_CLEAR(asyncio_mod);
     Py_CLEAR(traceback_extract_stack);
-    Py_CLEAR(asyncio_future_repr_info_func);
+    Py_CLEAR(asyncio_future_repr_func);
     Py_CLEAR(asyncio_get_event_loop_policy);
     Py_CLEAR(asyncio_iscoroutine_func);
     Py_CLEAR(asyncio_task_get_stack_func);
     Py_CLEAR(asyncio_task_print_stack_func);
-    Py_CLEAR(asyncio_task_repr_info_func);
+    Py_CLEAR(asyncio_task_repr_func);
     Py_CLEAR(asyncio_InvalidStateError);
     Py_CLEAR(asyncio_CancelledError);
 
@@ -3403,14 +3368,14 @@ module_init(void)
     GET_MOD_ATTR(asyncio_get_event_loop_policy, "get_event_loop_policy")
 
     WITH_MOD("asyncio.base_futures")
-    GET_MOD_ATTR(asyncio_future_repr_info_func, "_future_repr_info")
+    GET_MOD_ATTR(asyncio_future_repr_func, "_future_repr")
 
     WITH_MOD("asyncio.exceptions")
     GET_MOD_ATTR(asyncio_InvalidStateError, "InvalidStateError")
     GET_MOD_ATTR(asyncio_CancelledError, "CancelledError")
 
     WITH_MOD("asyncio.base_tasks")
-    GET_MOD_ATTR(asyncio_task_repr_info_func, "_task_repr_info")
+    GET_MOD_ATTR(asyncio_task_repr_func, "_task_repr")
     GET_MOD_ATTR(asyncio_task_get_stack_func, "_task_get_stack")
     GET_MOD_ATTR(asyncio_task_print_stack_func, "_task_print_stack")
 
diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h
index 4a90dfa67c22b..4b64367a3f631 100644
--- a/Modules/clinic/_asynciomodule.c.h
+++ b/Modules/clinic/_asynciomodule.c.h
@@ -292,23 +292,6 @@ _asyncio_Future__make_cancelled_error(FutureObj *self, PyObject *Py_UNUSED(ignor
     return _asyncio_Future__make_cancelled_error_impl(self);
 }
 
-PyDoc_STRVAR(_asyncio_Future__repr_info__doc__,
-"_repr_info($self, /)\n"
-"--\n"
-"\n");
-
-#define _ASYNCIO_FUTURE__REPR_INFO_METHODDEF    \
-    {"_repr_info", (PyCFunction)_asyncio_Future__repr_info, METH_NOARGS, _asyncio_Future__repr_info__doc__},
-
-static PyObject *
-_asyncio_Future__repr_info_impl(FutureObj *self);
-
-static PyObject *
-_asyncio_Future__repr_info(FutureObj *self, PyObject *Py_UNUSED(ignored))
-{
-    return _asyncio_Future__repr_info_impl(self);
-}
-
 PyDoc_STRVAR(_asyncio_Task___init____doc__,
 "Task(coro, *, loop=None, name=None, context=None)\n"
 "--\n"
@@ -383,23 +366,6 @@ _asyncio_Task__make_cancelled_error(TaskObj *self, PyObject *Py_UNUSED(ignored))
     return _asyncio_Task__make_cancelled_error_impl(self);
 }
 
-PyDoc_STRVAR(_asyncio_Task__repr_info__doc__,
-"_repr_info($self, /)\n"
-"--\n"
-"\n");
-
-#define _ASYNCIO_TASK__REPR_INFO_METHODDEF    \
-    {"_repr_info", (PyCFunction)_asyncio_Task__repr_info, METH_NOARGS, _asyncio_Task__repr_info__doc__},
-
-static PyObject *
-_asyncio_Task__repr_info_impl(TaskObj *self);
-
-static PyObject *
-_asyncio_Task__repr_info(TaskObj *self, PyObject *Py_UNUSED(ignored))
-{
-    return _asyncio_Task__repr_info_impl(self);
-}
-
 PyDoc_STRVAR(_asyncio_Task_cancel__doc__,
 "cancel($self, /, msg=None)\n"
 "--\n"
@@ -924,4 +890,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=540ed3caf5a4d57d input=a9049054013a1b77]*/
+/*[clinic end generated code: output=64b3836574e8a18c input=a9049054013a1b77]*/



More information about the Python-checkins mailing list