1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org>
6 # types.py: base types for introspected items.
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 from __future__ import absolute_import
28 from . import _gobject
29 from ._gobject._gobject import GInterface
30 from ._gobject.constants import TYPE_INVALID
37 register_interface_info, \
38 hook_up_vfunc_implementation
43 if (3, 0) <= sys.version_info < (3, 3):
44 # callable not available for python 3.0 thru 3.2
46 return hasattr(obj, '__call__')
49 class MetaClassHelper(object):
50 def _setup_methods(cls):
51 for method_info in cls.__info__.get_methods():
52 setattr(cls, method_info.__name__, method_info)
54 def _setup_fields(cls):
55 for field_info in cls.__info__.get_fields():
56 name = field_info.get_name().replace('-', '_')
57 setattr(cls, name, property(field_info.get_value, field_info.set_value))
59 def _setup_constants(cls):
60 for constant_info in cls.__info__.get_constants():
61 name = constant_info.get_name()
62 value = constant_info.get_value()
63 setattr(cls, name, value)
65 def _setup_vfuncs(cls):
66 for vfunc_name, py_vfunc in cls.__dict__.items():
67 if not vfunc_name.startswith("do_") or not callable(py_vfunc):
70 # If a method name starts with "do_" assume it is a vfunc, and search
71 # in the base classes for a method with the same name to override.
72 # Recursion is necessary as overriden methods in most immediate parent
73 # classes may shadow vfuncs from classes higher in the hierarchy.
75 for base in cls.__mro__:
76 method = getattr(base, vfunc_name, None)
77 if method is not None and isinstance(method, VFuncInfo):
81 # If we did not find a matching method name in the bases, we might
82 # be overriding an interface virtual method. Since interfaces do not
83 # provide implementations, there will be no method attribute installed
84 # on the object. Instead we have to search through
85 # InterfaceInfo.get_vfuncs(). Note that the infos returned by
86 # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
87 if vfunc_info is None:
88 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
90 if vfunc_info is not None:
91 assert vfunc_name == ('do_' + vfunc_info.get_name())
92 # Check to see if there are vfuncs with the same name in the bases.
93 # We have no way of specifying which one we are supposed to override.
94 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
95 if ambiguous_base is not None:
96 base_info = vfunc_info.get_container()
97 raise TypeError('Method %s() on class %s.%s is ambiguous '
98 'with methods in base classes %s.%s and %s.%s' %
100 cls.__info__.get_namespace(),
101 cls.__info__.get_name(),
102 base_info.get_namespace(),
103 base_info.get_name(),
104 ambiguous_base.__info__.get_namespace(),
105 ambiguous_base.__info__.get_name()
107 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
110 def _setup_native_vfuncs(cls):
111 # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
112 # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
113 # Also check if __info__ in __dict__, not hasattr('__info__', ...)
114 # because we do not want to accidentally retrieve __info__ from a base class.
115 class_info = cls.__dict__.get('__info__')
116 if class_info is None or not isinstance(class_info, ObjectInfo):
119 # Special case skipping of vfuncs for GObject.Object because they will break
120 # the static bindings which will try to use them.
121 if cls.__module__ == 'gi.repository.GObject' and cls.__name__ == 'Object':
124 for vfunc_info in class_info.get_vfuncs():
125 name = 'do_%s' % vfunc_info.__name__
126 setattr(cls, name, vfunc_info)
129 def find_vfunc_info_in_interface(bases, vfunc_name):
131 # All wrapped interfaces inherit from GInterface.
132 # This can be seen in IntrospectionModule.__getattr__() in module.py.
133 # We do not need to search regular classes here, only wrapped interfaces.
134 # We also skip GInterface, because it is not wrapped and has no __info__ attr.
135 # Skip bases without __info__ (static _gobject._gobject.GObject)
136 if base is GInterface or\
137 not issubclass(base, GInterface) or\
138 not hasattr(base, '__info__'):
141 # Only look at this classes vfuncs if it is an interface.
142 if isinstance(base.__info__, InterfaceInfo):
143 for vfunc in base.__info__.get_vfuncs():
144 if vfunc.get_name() == vfunc_name:
147 # Recurse into the parent classes
148 vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
149 if vfunc is not None:
155 def find_vfunc_conflict_in_bases(vfunc, bases):
157 if not hasattr(klass, '__info__') or \
158 not hasattr(klass.__info__, 'get_vfuncs'):
160 vfuncs = klass.__info__.get_vfuncs()
161 vfunc_name = vfunc.get_name()
163 if v.get_name() == vfunc_name and v != vfunc:
166 aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
167 if aklass is not None:
172 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
174 def __init__(cls, name, bases, dict_):
175 super(GObjectMeta, cls).__init__(name, bases, dict_)
176 is_gi_defined = False
177 if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
180 is_python_defined = False
181 if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
182 is_python_defined = True
184 if is_python_defined:
188 cls._setup_constants()
189 cls._setup_native_vfuncs()
191 if isinstance(cls.__info__, ObjectInfo):
193 elif isinstance(cls.__info__, InterfaceInfo):
194 register_interface_info(cls.__info__.get_g_type())
201 """Compute the class precedence list (mro) according to C3, with GObject
202 interface considerations.
204 We override Python's MRO calculation to account for the fact that
205 GObject classes are not affected by the diamond problem:
206 http://en.wikipedia.org/wiki/Diamond_problem
208 Based on http://www.python.org/download/releases/2.3/mro/
210 # TODO: If this turns out being too slow, consider using generators
212 bases_of_subclasses = [[C]]
215 for base in C.__bases__:
216 # Python causes MRO's to be calculated starting with the lowest
217 # base class and working towards the descendant, storing the result
218 # in __mro__ at each point. Therefore at this point we know that
219 # we already have our base class MRO's available to us, there is
220 # no need for us to (re)calculate them.
221 if hasattr(base, '__mro__'):
222 bases_of_subclasses += [list(base.__mro__)]
224 warnings.warn('Mixin class %s is an old style class, please '
225 'update this to derive from "object".' % base,
227 # For old-style classes (Python2 only), the MRO is not
228 # easily accessible. As we do need it here, we calculate
229 # it via recursion, according to the C3 algorithm. Using C3
230 # for old style classes deviates from Python's own behaviour,
231 # but visible effects here would be a corner case triggered by
232 # questionable design.
233 bases_of_subclasses += [mro(base)]
234 bases_of_subclasses += [list(C.__bases__)]
236 while bases_of_subclasses:
237 for subclass_bases in bases_of_subclasses:
238 candidate = subclass_bases[0]
239 not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
240 if not_head and GInterface not in candidate.__bases__:
241 candidate = None # conflict, reject candidate
245 if candidate is None:
246 raise TypeError('Cannot create a consistent method resolution '
249 bases.append(candidate)
251 for subclass_bases in bases_of_subclasses[:]: # remove candidate
252 if subclass_bases and subclass_bases[0] == candidate:
253 del subclass_bases[0]
254 if not subclass_bases:
255 bases_of_subclasses.remove(subclass_bases)
260 class StructMeta(type, MetaClassHelper):
262 def __init__(cls, name, bases, dict_):
263 super(StructMeta, cls).__init__(name, bases, dict_)
265 # Avoid touching anything else than the base class.
266 g_type = cls.__info__.get_g_type()
267 if g_type != TYPE_INVALID and g_type.pytype is not None:
273 for method_info in cls.__info__.get_methods():
274 if method_info.is_constructor() and \
275 method_info.__name__ == 'new' and \
276 not method_info.get_arguments():
277 cls.__new__ = staticmethod(method_info)