Create python object from memory address (using gi.repository)

旧街凉风 提交于 2019-12-04 07:18:26

The _PyGObject_API interface has changed at some point. I needed to drop the register_sinkfunc function. The following works:

from gi.repository import Gio, GLib
import gi
import ctypes

class _PyGObject_Functions(ctypes.Structure):
   _fields_ = [
       ('register_class',
        ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
                          ctypes.c_int, ctypes.py_object,
                          ctypes.py_object)),
       ('register_wrapper',
        ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
       ('lookup_class',
        ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
       ('newgobj',
        ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
       ]

class PyGObjectCPAI(object):
   def __init__(self):
       PyCObject_AsVoidPtr = ctypes.pythonapi.PyCObject_AsVoidPtr
       PyCObject_AsVoidPtr.restype = ctypes.c_void_p
       PyCObject_AsVoidPtr.argtypes = [ctypes.py_object]
       addr = PyCObject_AsVoidPtr(ctypes.py_object(
           gi._gobject._PyGObject_API))
       self._api = _PyGObject_Functions.from_address(addr)

   def pygobject_new(self, addr):
       return self._api.newgobj(addr)

capi = PyGObjectCPAI()

To get an object from a pointer:

obj = capi.pygobject_new(pointer)

to get a pointer from a (g)object:

pointer = hash(obj)

I have to add, in my case this didn't help me solve my actual problem. I was trying to interface with dconf, but dconf returns values of type GVariant, which does not inherit from GObject. It seems PyGI/GObject unfortunately does not expose the neccessary functions to turn a C (*GVariant) into a Python GLib.Variant. I guess it's of those times when you have to throw away your initial approach and try something different.

The code in jdm's answer isn't compatible with Python 3. Since CObject is deprecated in Python 2.7 and 3.1, and removed starting with 3.2, I tweaked the code to use Capsule (available in 2.7 and since 3.1):

import ctypes, gi

class _PyGObject_Functions(ctypes.Structure):
    _fields_ = [
        ('register_class',
            ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
                ctypes.c_int, ctypes.py_object, ctypes.py_object)),
        ('register_wrapper',
            ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
        ('lookup_class',
            ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
        ('newgobj',
            ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
        ]

class PyGObjectCAPI(object):
    def __init__(self):
        self._as_void_ptr.restype = ctypes.c_void_p
        self._as_void_ptr.argtypes = [ctypes.py_object]
        addr = self._as_void_ptr(ctypes.py_object(
            gi._gobject._PyGObject_API))
        self._api = _PyGObject_Functions.from_address(addr)

    @staticmethod
    def _as_void_ptr(obj):
        name = ctypes.pythonapi.PyCapsule_GetName(obj)
        return ctypes.pythonapi.PyCapsule_GetPointer(obj, name)

    def pygobject_new(self, addr):
        return self._api.newgobj(addr)

capi = PyGObjectCAPI()

(I also renamed it to PyGobjectCAPI -- not sure if CPAI stood for something, but it made more sense to me this way.)

hugosenari

PyGOject api changed since AlliedEnvy update

import gi
import ctypes
from ctypes import pythonapi

class _PyGObject_Functions(ctypes.Structure):
    _fields_ = [
        ('pygobject_register_class',
            ctypes.PYFUNCTYPE(ctypes.c_void_p)),
        ('pygobject_register_wrapper',
            ctypes.PYFUNCTYPE(ctypes.c_void_p)),
        ('pygobject_lookup_class',
            ctypes.PYFUNCTYPE(ctypes.c_void_p)),
        ('pygobject_new',
            ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
        ]

  class PyGObjectCAPI(object):
    def __init__(self):
        addr = self._as_void_ptr(gi._gobject._PyGObject_API)
        self._api = _PyGObject_Functions.from_address(addr)

    @classmethod
    def _capsule_name(cls, capsule):
        pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p
        pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object]
        return pythonapi.PyCapsule_GetName(capsule)

    @classmethod
    def _as_void_ptr(cls, capsule):
        name = cls._capsule_name(capsule)
        pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
        pythonapi.PyCapsule_GetPointer.argtypes = [
            ctypes.py_object, ctypes.c_char_p]
        return pythonapi.PyCapsule_GetPointer(capsule, name)

    def to_object(self, addr):
        return self._api.pygobject_new(addr)

capi = PyGObjectCAPI()

With the advent of instrospection files (.typelib, .gir), the same API should be available regardless of the language being used, that is, if you're using a C function not in the API, you're probably using a function intended to be used only internally.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!