diff options
author | Andrew Svetlov <andrew.svetlov@gmail.com> | 2022-03-17 01:03:09 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-17 01:03:09 (GMT) |
commit | 30b5d41fabad04f9f34d603f1ce2249452c18c71 (patch) | |
tree | 7eed9b2cc60f96768193498b051583dd733e2a40 | |
parent | a7c54148322781cb0f332d440a3454d550ef6414 (diff) | |
download | cpython-30b5d41fabad04f9f34d603f1ce2249452c18c71.zip cpython-30b5d41fabad04f9f34d603f1ce2249452c18c71.tar.gz cpython-30b5d41fabad04f9f34d603f1ce2249452c18c71.tar.bz2 |
bpo-47039: Normalize repr() of asyncio future and task objects (GH-31950)
-rw-r--r-- | Lib/asyncio/base_futures.py | 30 | ||||
-rw-r--r-- | Lib/asyncio/base_tasks.py | 7 | ||||
-rw-r--r-- | Lib/asyncio/futures.py | 5 | ||||
-rw-r--r-- | Lib/asyncio/tasks.py | 4 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-03-17-01-54-13.bpo-47039.0Yxv0K.rst | 1 | ||||
-rw-r--r-- | Modules/_asynciomodule.c | 77 | ||||
-rw-r--r-- | Modules/clinic/_asynciomodule.c.h | 36 |
7 files changed, 42 insertions, 118 deletions
diff --git a/Lib/asyncio/base_futures.py b/Lib/asyncio/base_futures.py index 2c01ac9..cd811a7 100644 --- a/Lib/asyncio/base_futures.py +++ b/Lib/asyncio/base_futures.py @@ -42,16 +42,6 @@ def _format_callbacks(cb): 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 + + +@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 1d62389..26298e6 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 +@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 5a92f17..bfd4ee9 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -85,11 +85,8 @@ class Future: 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 b4f1eed..0b5f322 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -133,8 +133,8 @@ class Task(futures._PyFuture): # Inherit Python Task implementation __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 0000000..66785e1 --- /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 4b12744..2411978 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 @@ -2164,17 +2141,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 msg: object = None @@ -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 4a90dfa..4b64367 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]*/ |