mirror of https://github.com/python/cpython
bpo-47039: Normalize repr() of asyncio future and task objects (GH-31950)
This commit is contained in:
parent
a7c5414832
commit
30b5d41fab
|
@ -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}>'
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Normalize ``repr()`` of asyncio future and task objects.
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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]*/
|
||||
|
|
Loading…
Reference in New Issue