PyObjects From Python Via ctypes
Lawrence D’Oliveiro <[email protected]> Thu, 11 Jun 2026 07:41:20 -0000 (UTC)
| Newsgroups | comp.lang.python |
|---|---|
| Organization | A noiseless patient Spider |
| Message-ID | <[email protected]> |
I see ctypes has a py_object type, but you can’t seem to do much with
it. The following might be more useful:
---- file: pyobjhack.py
import ctypes as ct
def _init_hack() :
# works out the values of TRACE_REFS and PYOBJECT_HEADER_SIZE.
global TRACE_REFS, PYOBJECT_HEADER_SIZE
o = object()
# CPython implementation detail: “id()” function returns address of object header
if ct.c_size_t.from_address(id(o)).value == 1 :
TRACE_REFS = False
PYOBJECT_HEADER_SIZE = 2
elif ct.c_size_t.from_address(id(o) + 2 * ct.sizeof(ct.c_void_p)).value == 1 :
TRACE_REFS = True
PYOBJECT_HEADER_SIZE = 4
else :
raise RuntimeError("cannot determine pyobject header size")
#end if
# header of every PyObject has ob_refcnt (type ssize_t) followed by ob_type (type pointer),
# but these might be preceded by a pair of next/prev pointers if Py_TRACE_REFS is defined.
# Only way I can figure out to determine which is the case is to construct an object
# with a known refcount of 1, and see if I can find the value 1 at the start.
# Confirm that the refcount field behaves as expected:
o2 = o
assert ct.c_size_t.from_address(id(o) + (PYOBJECT_HEADER_SIZE - 2) * ct.sizeof(ct.c_void_p)).value == 2
del o2
assert ct.c_size_t.from_address(id(o) + (PYOBJECT_HEADER_SIZE - 2) * ct.sizeof(ct.c_void_p)).value == 1
#end _init_hack
_init_hack()
del _init_hack
class PyObject(ct.Structure) :
"header part of a PyObject structure."
_fields_ = \
(
(
[],
[
("_ob_next", ct.c_void_p),
("_ob_prev", ct.c_void_p),
],
)[TRACE_REFS]
+
[
("ob_refcnt", ct.c_ssize_t),
("ob_type", ct.c_void_p),
]
)
@classmethod
def from_obj(celf, obj) :
"aliases the object header to a PyObject struct."
return \
celf.from_address(id(obj))
#end from_obj
#end PyObject