[pygobject/a93755ddba9a176] Speed up MRO calculation
- From: Simon Feltman <sfeltman src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject/a93755ddba9a176] Speed up MRO calculation
- Date: Wed, 10 Jul 2013 18:26:17 +0000 (UTC)
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]