diff options
-rw-r--r-- | gi/types.py | 30 | ||||
-rw-r--r-- | tests/test_gi.py | 16 |
2 files changed, 39 insertions, 7 deletions
diff --git a/gi/types.py b/gi/types.py index 47a81d84..69bb4944 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 1d56faef..16e51a4b 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): |