Skip to content

Commit 2e1e7af

Browse files
committed
pythongh-141070: Add PyObject_Dump() function
Promote _PyObject_Dump() as a public function. Keep _PyObject_Dump() alias to PyObject_Dump() for backward compatibility.
1 parent 986bb0a commit 2e1e7af

File tree

7 files changed

+78
-2
lines changed

7 files changed

+78
-2
lines changed

Doc/c-api/object.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ Object Protocol
8585
instead of the :func:`repr`.
8686
8787
88+
.. c:function:: void PyObject_Dump(PyObject* op)
89+
90+
Dump an object *op* to ``stderr``. Function used for debugging.
91+
92+
It can be called without an :term:`attached thread state`, even if it's not
93+
recommended.
94+
95+
Implement an heuristic to detect if the object memory has been freed.
96+
97+
Example of output::
98+
99+
object address : 0x7f80124702c0
100+
object refcount : 2
101+
object type : 0x9902e0
102+
object type name: str
103+
object repr : 'abcdef'
104+
105+
.. versionadded:: next
106+
107+
88108
.. c:function:: int PyObject_HasAttrWithError(PyObject *o, PyObject *attr_name)
89109
90110
Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise.

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,10 @@ New features
947947

948948
(Contributed by Victor Stinner in :gh:`129813`.)
949949

950+
* Add :c:func:`PyObject_Dump` to dump an object to ``stderr``. Function used
951+
for debugging.
952+
(Contributed by Victor Stinner in :gh:`141070`.)
953+
950954
* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
951955
(Contributed by Victor Stinner in :gh:`111489`.)
952956

Include/cpython/object.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,10 @@ PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);
295295

296296
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
297297
PyAPI_FUNC(void) _Py_BreakPoint(void);
298-
PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
298+
PyAPI_FUNC(void) PyObject_Dump(PyObject *);
299+
300+
// Alias for backward compatibility
301+
#define _PyObject_Dump PyObject_Dump
299302

300303
PyAPI_FUNC(PyObject*) _PyObject_GetAttrId(PyObject *, _Py_Identifier *);
301304

Lib/test/test_capi/test_object.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import enum
2+
import os
23
import sys
34
import textwrap
45
import unittest
@@ -13,6 +14,8 @@
1314
_testcapi = import_helper.import_module('_testcapi')
1415
_testinternalcapi = import_helper.import_module('_testinternalcapi')
1516

17+
STDERR_FD = 2
18+
1619

1720
class Constant(enum.IntEnum):
1821
Py_CONSTANT_NONE = 0
@@ -247,5 +250,40 @@ def func(x):
247250

248251
func(object())
249252

253+
def test_pyobject_dump(self):
254+
pyobject_dump = _testcapi.pyobject_dump
255+
obj = 'test string'
256+
257+
filename = os_helper.TESTFN
258+
self.addCleanup(os_helper.unlink, filename)
259+
260+
try:
261+
old_stderr = os.dup(STDERR_FD)
262+
except OSError as exc:
263+
# os.dup(STDERR_FD) is not supported on WASI
264+
self.skipTest(f"os.dup() failed with {exc!r}")
265+
266+
try:
267+
with open(filename, "wb") as fp:
268+
fd = fp.fileno()
269+
os.dup2(fd, STDERR_FD)
270+
pyobject_dump(obj)
271+
finally:
272+
os.dup2(old_stderr, STDERR_FD)
273+
os.close(old_stderr)
274+
275+
with open(filename) as fp:
276+
output = fp.read()
277+
278+
hex_regex = r'0x[0-9a-fA-F]+'
279+
self.assertRegex(output.rstrip(),
280+
fr"object address : {hex_regex}\n"
281+
r"object refcount : [0-9]+\n"
282+
fr"object type : {hex_regex}\n"
283+
r"object type name: str\n"
284+
r"object repr : 'test string'"
285+
)
286+
287+
250288
if __name__ == "__main__":
251289
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyObject_Dump` to dump an object to ``stderr``. Function used
2+
for debugging. Patch by Victor Stinner.

Modules/_testcapi/object.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,14 @@ is_uniquely_referenced(PyObject *self, PyObject *op)
485485
}
486486

487487

488+
static PyObject *
489+
pyobject_dump(PyObject *self, PyObject *op)
490+
{
491+
PyObject_Dump(op);
492+
Py_RETURN_NONE;
493+
}
494+
495+
488496
static PyMethodDef test_methods[] = {
489497
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
490498
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
@@ -511,6 +519,7 @@ static PyMethodDef test_methods[] = {
511519
{"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
512520
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
513521
{"is_uniquely_referenced", is_uniquely_referenced, METH_O},
522+
{"pyobject_dump", pyobject_dump, METH_O},
514523
{NULL},
515524
};
516525

Objects/object.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ _PyObject_IsFreed(PyObject *op)
713713

714714
/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */
715715
void
716-
_PyObject_Dump(PyObject* op)
716+
PyObject_Dump(PyObject* op)
717717
{
718718
if (_PyObject_IsFreed(op)) {
719719
/* It seems like the object memory has been freed:

0 commit comments

Comments
 (0)