Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #197798 > unrolled thread
| Started by | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| First post | 2026-06-11 07:41 +0000 |
| Last post | 2026-06-11 07:44 +0000 |
| Articles | 2 — 1 participant |
Back to article view | Back to comp.lang.python
PyObjects From Python Via ctypes Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-06-11 07:41 +0000
Re: PyObjects From Python Via ctypes Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-06-11 07:44 +0000
| From | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| Date | 2026-06-11 07:41 +0000 |
| Subject | PyObjects From Python Via ctypes |
| Message-ID | <110doqv$19tv7$1@dont-email.me> |
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
[toc] | [next] | [standalone]
| From | Lawrence D’Oliveiro <ldo@nz.invalid> |
|---|---|
| Date | 2026-06-11 07:44 +0000 |
| Message-ID | <110dp0f$19tv7$2@dont-email.me> |
| In reply to | #197798 |
On Thu, 11 Jun 2026 07:41:20 -0000 (UTC), I wrote:
> I see ctypes has a py_object type, but you can’t seem to do much
> with it. The following might be more useful:
Example use: accessing a more powerful Cairo wrapper in a context
where the GUI toolkit wrapper (e.g. PyGObject for GTK) wants to
give you only a pycairo object.
----
import ctypes as ct
import qahirah as qah
from pyobjhack import PyObject
class PyCairo_Context(ct.Structure) :
"extract cairo_t pointer from pycairo.Context object."
_fields_ = \
[
("header", PyObject),
("ctx", ct.c_void_p),
]
@classmethod
def get_ctx(celf, pyctx) :
return \
ct.c_void_p(celf.from_address(id(pyctx)).ctx).value
#end get_ctx
#end PyCairo_Context
def pycairo_to_qah(ctx) :
"returns a qah.Context object wrapping the cairo_t pointer from" \
" a given cairo.Context object."
gx = PyCairo_Context.get_ctx(ctx)
qah.cairo.cairo_reference(gx) # grab one for myself
return \
qah.Context(gx)
#end pycairo_to_qah
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web