[pygobject] Speed up _setup_native_vfuncs()



commit b15e8e2c0c933d0f827a70280faf875ac383d81b
Author: Laszlo Pandy <lpandy src gnome org>
Date:   Wed Jan 26 00:40:49 2011 +0100

    Speed up _setup_native_vfuncs()
    
    This changes _setup_native_vfuncs() to only install native
    vfunc wrappers from the current class on the current class.
    Native vfuncs will not be propogated up or down the class
    hierarchy as this is unnecessary and wastes CPU and memory.
    
    Since the normal process in python to retrieve a method or
    attribute recurses to the base classes if an attribute is not
    found in the subclass, there is no need to setup all base class
    virtual functions on a subclass.
    
    This patch removes the recursion in _setup_native_vfuncs()
    and lets Python find them in the base classes like a normal
    Python class would work. This significantly increases the speed
    of any class which is or inherits from a C class which includes
    virtual methods.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=640629

 gi/types.py      |   26 +++++++++++++-------------
 tests/test_gi.py |   13 +++++++++++++
 2 files changed, 26 insertions(+), 13 deletions(-)
---
diff --git a/gi/types.py b/gi/types.py
index 6e486e7..b3a9d3d 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -149,19 +149,19 @@ class MetaClassHelper(object):
                 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
                                              py_vfunc)
 
-    def _setup_native_vfuncs(cls, impl):
-        for base in cls.__bases__ + (cls,):
-            if not hasattr(base, '__info__') or \
-                    not hasattr(base.__info__, 'get_vfuncs') or \
-                    isinstance(base.__info__, InterfaceInfo):
-                continue
-            for vfunc_info in base.__info__.get_vfuncs():
-                name = 'do_%s' % vfunc_info.get_name()
-                value = NativeVFunc(vfunc_info, impl)
-                setattr(impl, name, value)
+    def _setup_native_vfuncs(cls):
+        # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
+        # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
+        # Also check if __info__ in __dict__, not hasattr('__info__', ...)
+        # because we do not want to accidentally retrieve __info__ from a base class.
+        class_info = cls.__dict__.get('__info__')
+        if class_info is None or not isinstance(class_info, ObjectInfo):
+            return
 
-            if base != cls:
-                base._setup_native_vfuncs(impl)
+        for vfunc_info in class_info.get_vfuncs():
+            name = 'do_%s' % vfunc_info.get_name()
+            value = NativeVFunc(vfunc_info, cls)
+            setattr(cls, name, value)
 
 def find_vfunc_info_in_interface(bases, vfunc_name):
     for base in bases:
@@ -215,7 +215,7 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
         elif is_gi_defined:
             cls._setup_methods()
             cls._setup_constants()
-            cls._setup_native_vfuncs(cls)
+            cls._setup_native_vfuncs()
 
             if isinstance(cls.__info__, ObjectInfo):
                 cls._setup_fields()
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 08899c4..19a7d46 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1447,6 +1447,19 @@ class TestPythonGObject(unittest.TestCase):
         object_ = ObjectOverrideNonVFuncDoMethod()
         self.assertEquals(18, object_.do_not_a_vfunc())
 
+    def test_native_function_not_set_in_subclass_dict(self):
+        # Previously, GI was setting virtual functions on the class as well
+        # as any *native* class that subclasses it. Here we check that it is only
+        # set on the class that the method is originally from.
+        self.assertTrue('do_method_with_default_implementation' in GIMarshallingTests.Object.__dict__)
+        self.assertTrue('do_method_with_default_implementation' not in GIMarshallingTests.SubObject.__dict__)
+
+        # Here we check that accessing a vfunc from the subclass returns the same wrapper object,
+        # meaning that multiple wrapper objects have not been created for the same vfunc.
+        self.assertTrue(GIMarshallingTests.Object.do_method_with_default_implementation.im_func is \
+                GIMarshallingTests.SubObject.do_method_with_default_implementation.im_func)
+
+
 class TestMultiOutputArgs(unittest.TestCase):
 
     def test_int_out_out(self):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]