Skip to content

Commit 2d55425

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. * Replace _PyObject_Dump() with PyObject_Dump().
1 parent 986bb0a commit 2d55425

File tree

11 files changed

+87
-11
lines changed

11 files changed

+87
-11
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: 5 additions & 2 deletions
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

@@ -387,7 +390,7 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
387390
process with a message on stderr if the given condition fails to hold,
388391
but compile away to nothing if NDEBUG is defined.
389392
390-
However, before aborting, Python will also try to call _PyObject_Dump() on
393+
However, before aborting, Python will also try to call PyObject_Dump() on
391394
the given object. This may be of use when investigating bugs in which a
392395
particular object is corrupt (e.g. buggy a tp_visit method in an extension
393396
module breaking the garbage collector), to help locate the broken objects.

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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: 2 additions & 2 deletions
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:
@@ -3150,7 +3150,7 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
31503150

31513151
/* This might succeed or fail, but we're about to abort, so at least
31523152
try to provide any extra info we can: */
3153-
_PyObject_Dump(obj);
3153+
PyObject_Dump(obj);
31543154

31553155
fprintf(stderr, "\n");
31563156
fflush(stderr);

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ unicode_check_encoding_errors(const char *encoding, const char *errors)
547547
}
548548

549549
/* Disable checks during Python finalization. For example, it allows to
550-
call _PyObject_Dump() during finalization for debugging purpose. */
550+
call PyObject_Dump() during finalization for debugging purpose. */
551551
if (_PyInterpreterState_GetFinalizing(interp) != NULL) {
552552
return 0;
553553
}

Python/gc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2235,7 +2235,7 @@ _PyGC_Fini(PyInterpreterState *interp)
22352235
void
22362236
_PyGC_Dump(PyGC_Head *g)
22372237
{
2238-
_PyObject_Dump(FROM_GC(g));
2238+
PyObject_Dump(FROM_GC(g));
22392239
}
22402240

22412241

0 commit comments

Comments
 (0)