[pygobject] gi/types: make it possible to resolve ambiguous vmethod names



commit f2100902c6fdfd6a3daaab39d3325ffc9ada9a7d
Author: Mathieu Duponchelle <mathieu centricular com>
Date:   Sun Jan 21 19:17:10 2018 +0100

    gi/types: make it possible to resolve ambiguous vmethod names
    
    Related to https://gitlab.gnome.org/GNOME/pygobject/issues/105,
    but the method exposed to resolve the ambiguities does not
    use a decorator, as a decorator cannot rename the function it
    wraps, which means that users will have to provide different names
    if they want to implement ambiguous methods from multiple base
    classes.
    
    Instead, ambiguities can be resolved by implementing methods named
    do_$namespaced_base_class_name_$vfunc_name, eg:
    
    do_gst_base_src_query

 gi/types.py | 50 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 14 deletions(-)
---
diff --git a/gi/types.py b/gi/types.py
index 9bc3779c..f73f30c0 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -24,6 +24,7 @@ from __future__ import absolute_import
 
 import sys
 import warnings
+import re
 
 from ._constants import TYPE_INVALID
 from .docstring import generate_doc_string
@@ -49,6 +50,11 @@ if (3, 0) <= sys.version_info < (3, 3):
         return hasattr(obj, '__call__')
 
 
+def snake_case(name):
+    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+
 class MetaClassHelper(object):
     def _setup_methods(cls):
         for method_info in cls.__info__.get_methods():
@@ -81,6 +87,8 @@ class MetaClassHelper(object):
             if not vfunc_name.startswith("do_") or not callable(py_vfunc):
                 continue
 
+            skip_ambiguity_check = False
+
             # If a method name starts with "do_" assume it is a vfunc, and search
             # in the base classes for a method with the same name to override.
             # Recursion is necessary as overriden methods in most immediate parent
@@ -92,6 +100,20 @@ class MetaClassHelper(object):
                     vfunc_info = method
                     break
 
+                if not hasattr(base, '__info__') or not hasattr(base.__info__, 'get_vfuncs'):
+                    continue
+
+                base_name = snake_case(base.__info__.get_type_name())
+
+                for v in base.__info__.get_vfuncs():
+                    if vfunc_name == 'do_%s_%s' % (base_name, v.get_name()):
+                        vfunc_info = v
+                        skip_ambiguity_check = True
+                        break
+
+                if vfunc_info:
+                    break
+
             # If we did not find a matching method name in the bases, we might
             # be overriding an interface virtual method. Since interfaces do not
             # provide implementations, there will be no method attribute installed
@@ -102,22 +124,22 @@ class MetaClassHelper(object):
                 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
 
             if vfunc_info is not None:
-                assert vfunc_name == ('do_' + vfunc_info.get_name())
                 # Check to see if there are vfuncs with the same name in the bases.
                 # We have no way of specifying which one we are supposed to override.
-                ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
-                if ambiguous_base is not None:
-                    base_info = vfunc_info.get_container()
-                    raise TypeError('Method %s() on class %s.%s is ambiguous '
-                                    'with methods in base classes %s.%s and %s.%s' %
-                                    (vfunc_name,
-                                     cls.__info__.get_namespace(),
-                                     cls.__info__.get_name(),
-                                     base_info.get_namespace(),
-                                     base_info.get_name(),
-                                     ambiguous_base.__info__.get_namespace(),
-                                     ambiguous_base.__info__.get_name()
-                                    ))
+                if not skip_ambiguity_check:
+                    ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
+                    if ambiguous_base is not None:
+                        base_info = vfunc_info.get_container()
+                        raise TypeError('Method %s() on class %s.%s is ambiguous '
+                                        'with methods in base classes %s.%s and %s.%s' %
+                                        (vfunc_name,
+                                         cls.__info__.get_namespace(),
+                                         cls.__info__.get_name(),
+                                         base_info.get_namespace(),
+                                         base_info.get_name(),
+                                         ambiguous_base.__info__.get_namespace(),
+                                         ambiguous_base.__info__.get_name()
+                                        ))
                 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
                                              py_vfunc)
 


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