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 ._constants import TYPE_INVALID
29 from .docstring import generate_doc_string
36 register_interface_info, \
37 hook_up_vfunc_implementation, \
40 GInterface = _gobject.GInterface
44 from . import _propertyhelper as propertyhelper
45 from . import _signalhelper as signalhelper
47 if (3, 0) <= sys.version_info < (3, 3):
48 # callable not available for python 3.0 thru 3.2
50 return hasattr(obj, '__call__')
53 class MetaClassHelper(object):
54 def _setup_methods(cls):
55 for method_info in cls.__info__.get_methods():
56 setattr(cls, method_info.__name__, method_info)
58 def _setup_fields(cls):
59 for field_info in cls.__info__.get_fields():
60 name = field_info.get_name().replace('-', '_')
61 setattr(cls, name, property(field_info.get_value, field_info.set_value))
63 def _setup_constants(cls):
64 for constant_info in cls.__info__.get_constants():
65 name = constant_info.get_name()
66 value = constant_info.get_value()
67 setattr(cls, name, value)
69 def _setup_vfuncs(cls):
70 for vfunc_name, py_vfunc in cls.__dict__.items():
71 if not vfunc_name.startswith("do_") or not callable(py_vfunc):
74 # If a method name starts with "do_" assume it is a vfunc, and search
75 # in the base classes for a method with the same name to override.
76 # Recursion is necessary as overriden methods in most immediate parent
77 # classes may shadow vfuncs from classes higher in the hierarchy.
79 for base in cls.__mro__:
80 method = getattr(base, vfunc_name, None)
81 if method is not None and isinstance(method, VFuncInfo):
85 # If we did not find a matching method name in the bases, we might
86 # be overriding an interface virtual method. Since interfaces do not
87 # provide implementations, there will be no method attribute installed
88 # on the object. Instead we have to search through
89 # InterfaceInfo.get_vfuncs(). Note that the infos returned by
90 # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
91 if vfunc_info is None:
92 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
94 if vfunc_info is not None:
95 assert vfunc_name == ('do_' + vfunc_info.get_name())
96 # Check to see if there are vfuncs with the same name in the bases.
97 # We have no way of specifying which one we are supposed to override.
98 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
99 if ambiguous_base is not None:
100 base_info = vfunc_info.get_container()
101 raise TypeError('Method %s() on class %s.%s is ambiguous '
102 'with methods in base classes %s.%s and %s.%s' %
104 cls.__info__.get_namespace(),
105 cls.__info__.get_name(),
106 base_info.get_namespace(),
107 base_info.get_name(),
108 ambiguous_base.__info__.get_namespace(),
109 ambiguous_base.__info__.get_name()
111 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
114 def _setup_native_vfuncs(cls):
115 # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
116 # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
117 # Also check if __info__ in __dict__, not hasattr('__info__', ...)
118 # because we do not want to accidentally retrieve __info__ from a base class.
119 class_info = cls.__dict__.get('__info__')
120 if class_info is None or not isinstance(class_info, ObjectInfo):
123 # Special case skipping of vfuncs for GObject.Object because they will break
124 # the static bindings which will try to use them.
125 if cls.__module__ == 'gi.repository.GObject' and cls.__name__ == 'Object':
128 for vfunc_info in class_info.get_vfuncs():
129 name = 'do_%s' % vfunc_info.__name__
130 setattr(cls, name, vfunc_info)
133 def find_vfunc_info_in_interface(bases, vfunc_name):
135 # All wrapped interfaces inherit from GInterface.
136 # This can be seen in IntrospectionModule.__getattr__() in module.py.
137 # We do not need to search regular classes here, only wrapped interfaces.
138 # We also skip GInterface, because it is not wrapped and has no __info__ attr.
139 # Skip bases without __info__ (static _gobject._gobject.GObject)
140 if base is GInterface or\
141 not issubclass(base, GInterface) or\
142 not hasattr(base, '__info__'):
145 # Only look at this classes vfuncs if it is an interface.
146 if isinstance(base.__info__, InterfaceInfo):
147 for vfunc in base.__info__.get_vfuncs():
148 if vfunc.get_name() == vfunc_name:
151 # Recurse into the parent classes
152 vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
153 if vfunc is not None:
159 def find_vfunc_conflict_in_bases(vfunc, bases):
161 if not hasattr(klass, '__info__') or \
162 not hasattr(klass.__info__, 'get_vfuncs'):
164 vfuncs = klass.__info__.get_vfuncs()
165 vfunc_name = vfunc.get_name()
167 if v.get_name() == vfunc_name and v != vfunc:
170 aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
171 if aklass is not None:
176 class _GObjectMetaBase(type):
177 """Metaclass for automatically registering GObject classes."""
178 def __init__(cls, name, bases, dict_):
179 type.__init__(cls, name, bases, dict_)
180 propertyhelper.install_properties(cls)
181 signalhelper.install_signals(cls)
182 cls._type_register(cls.__dict__)
184 def _type_register(cls, namespace):
185 ## don't register the class if already registered
186 if '__gtype__' in namespace:
189 # Do not register a new GType for the overrides, as this would sort of
190 # defeat the purpose of overrides...
191 if cls.__module__.startswith('gi.overrides.'):
194 _gobject.type_register(cls, namespace.get('__gtype_name__'))
196 _gobject._install_metaclass(_GObjectMetaBase)
199 class GObjectMeta(_GObjectMetaBase, MetaClassHelper):
200 """Meta class used for GI GObject based types."""
201 def __init__(cls, name, bases, dict_):
202 super(GObjectMeta, cls).__init__(name, bases, dict_)
203 is_gi_defined = False
204 if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
207 is_python_defined = False
208 if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
209 is_python_defined = True
211 if is_python_defined:
215 cls._setup_constants()
216 cls._setup_native_vfuncs()
218 if isinstance(cls.__info__, ObjectInfo):
220 elif isinstance(cls.__info__, InterfaceInfo):
221 register_interface_info(cls.__info__.get_g_type())
228 if cls == GObjectMeta:
230 return generate_doc_string(cls.__info__)
234 """Compute the class precedence list (mro) according to C3, with GObject
235 interface considerations.
237 We override Python's MRO calculation to account for the fact that
238 GObject classes are not affected by the diamond problem:
239 http://en.wikipedia.org/wiki/Diamond_problem
241 Based on http://www.python.org/download/releases/2.3/mro/
243 # TODO: If this turns out being too slow, consider using generators
245 bases_of_subclasses = [[C]]
248 for base in C.__bases__:
249 # Python causes MRO's to be calculated starting with the lowest
250 # base class and working towards the descendant, storing the result
251 # in __mro__ at each point. Therefore at this point we know that
252 # we already have our base class MRO's available to us, there is
253 # no need for us to (re)calculate them.
254 if hasattr(base, '__mro__'):
255 bases_of_subclasses += [list(base.__mro__)]
257 warnings.warn('Mixin class %s is an old style class, please '
258 'update this to derive from "object".' % base,
260 # For old-style classes (Python2 only), the MRO is not
261 # easily accessible. As we do need it here, we calculate
262 # it via recursion, according to the C3 algorithm. Using C3
263 # for old style classes deviates from Python's own behaviour,
264 # but visible effects here would be a corner case triggered by
265 # questionable design.
266 bases_of_subclasses += [mro(base)]
267 bases_of_subclasses += [list(C.__bases__)]
269 while bases_of_subclasses:
270 for subclass_bases in bases_of_subclasses:
271 candidate = subclass_bases[0]
272 not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
273 if not_head and GInterface not in candidate.__bases__:
274 candidate = None # conflict, reject candidate
278 if candidate is None:
279 raise TypeError('Cannot create a consistent method resolution '
282 bases.append(candidate)
284 for subclass_bases in bases_of_subclasses[:]: # remove candidate
285 if subclass_bases and subclass_bases[0] == candidate:
286 del subclass_bases[0]
287 if not subclass_bases:
288 bases_of_subclasses.remove(subclass_bases)
293 class StructMeta(type, MetaClassHelper):
294 """Meta class used for GI Struct based types."""
296 def __init__(cls, name, bases, dict_):
297 super(StructMeta, cls).__init__(name, bases, dict_)
299 # Avoid touching anything else than the base class.
300 g_type = cls.__info__.get_g_type()
301 if g_type != TYPE_INVALID and g_type.pytype is not None:
307 for method_info in cls.__info__.get_methods():
308 if method_info.is_constructor() and \
309 method_info.__name__ == 'new' and \
310 not method_info.get_arguments():
311 cls.__new__ = staticmethod(method_info)
316 if cls == StructMeta:
318 return generate_doc_string(cls.__info__)