[pygobject/a93755ddba9a176] Speed up MRO calculation



commit a93755ddba9a1761b627583d7b9be63783c2c063
Author: Daniel Drake <dsd laptop org>
Date:   Tue Jul 9 13:03:36 2013 -0600

    Speed up MRO calculation
    
    Optimize gi.type.mro() with the following observations and tricks:
    
    1. Python prepares all the base classes before trying to calculate the
       MRO of the current one (it first needs to populate __bases__, for
       example). So we can assume that the base class MRO is already available
       in __mro__ and this will have been previously calculated (by us, in the
       case of gi classes). This avoids repeating a lot of MRO-calculating work,
       and also avoids (re)calculating MROs for inheritance chains that don't
       have any gi classes in them anyway.
    
    2. With that simplification in place, we can avoid recursion, which is not
       all that great in Python...
    
    3. ...except in the uncommon case of a Python2 old-style classes, where
       __mro__ is not available. There doesn't seem to be any existing
       function to calculate or read MRO of old-style python classes, so just
       keep doing as before: calculate the C3 MRO of the old-style class via
       recursion. That behaviour is not really correct, and the recursion is
       not desirable, so we print a warning here.
    
    This makes the "hello world" Sugar app start up approximately 0.5 seconds
    faster on XO-1.5.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=703829

 gi/types.py      |   30 +++++++++++++++++++++++++++---
 tests/test_gi.py |   16 ++++++++++++----
 2 files changed, 39 insertions(+), 7 deletions(-)
---
diff --git a/gi/types.py b/gi/types.py
index 47a81d8..69bb494 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -23,6 +23,7 @@
 from __future__ import absolute_import
 
 import sys
+import warnings
 
 from . import _gobject
 from ._gobject._gobject import GInterface
@@ -309,17 +310,40 @@ class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
 
 
 def mro(C):
-    """Compute the class precedence list (mro) according to C3
+    """Compute the class precedence list (mro) according to C3, with GObject
+    interface considerations.
+
+    We override Python's MRO calculation to account for the fact that
+    GObject classes are not affected by the diamond problem:
+    http://en.wikipedia.org/wiki/Diamond_problem
 
     Based on http://www.python.org/download/releases/2.3/mro/
-    Modified to consider that interfaces don't create the diamond problem
     """
     # TODO: If this turns out being too slow, consider using generators
     bases = []
     bases_of_subclasses = [[C]]
 
     if C.__bases__:
-        bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
+        for base in C.__bases__:
+            # Python causes MRO's to be calculated starting with the lowest
+            # base class and working towards the descendant, storing the result
+            # in __mro__ at each point. Therefore at this point we know that
+            # we already have our base class MRO's available to us, there is
+            # no need for us to (re)calculate them.
+            if hasattr(base, '__mro__'):
+                bases_of_subclasses += [list(base.__mro__)]
+            else:
+                warnings.warn('Mixin class %s is an old style class, please '
+                              'update this to derive from "object".' % base,
+                              RuntimeWarning)
+                # For old-style classes (Python2 only), the MRO is not
+                # easily accessible. As we do need it here, we calculate
+                # it via recursion, according to the C3 algorithm. Using C3
+                # for old style classes deviates from Python's own behaviour,
+                # but visible effects here would be a corner case triggered by
+                # questionable design.
+                bases_of_subclasses += [mro(base)]
+        bases_of_subclasses += [list(C.__bases__)]
 
     while bases_of_subclasses:
         for subclass_bases in bases_of_subclasses:
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 1d56fae..16e51a4 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -2152,7 +2152,7 @@ class TestPythonGObject(unittest.TestCase):
         self.assertTrue(isinstance(GObject, DynamicModule))
 
     def test_subobject_non_vfunc_do_method(self):
-        class PythonObjectWithNonVFuncDoMethod:
+        class PythonObjectWithNonVFuncDoMethod(object):
             def do_not_a_vfunc(self):
                 return 5
 
@@ -2416,9 +2416,17 @@ class TestMRO(unittest.TestCase):
         class Mixin:
             pass
 
-        # Dynamically create a new gi based class with an old
-        # style mixin.
-        type('GIWithOldStyleMixin', (GIMarshallingTests.Object, Mixin), {})
+        with warnings.catch_warnings(record=True) as warn:
+            warnings.simplefilter('always')
+
+            # Dynamically create a new gi based class with an old
+            # style mixin.
+            type('GIWithOldStyleMixin', (GIMarshallingTests.Object, Mixin), {})
+
+            if sys.version_info < (3, 0):
+                self.assertTrue(issubclass(warn[0].category, RuntimeWarning))
+            else:
+                self.assertEqual(len(warn), 0)
 
 
 class TestInterfaceClash(unittest.TestCase):


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