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
26 from . import _gobject
33 register_interface_info, \
34 hook_up_vfunc_implementation
36 if sys.version_info > (3, 0):
38 return hasattr(obj, '__call__')
42 def function(*args, **kwargs):
43 return info.invoke(*args, **kwargs)
44 function.__info__ = info
45 function.__name__ = info.get_name()
46 function.__module__ = info.get_namespace()
51 class NativeVFunc(object):
53 def __init__(self, info):
56 def __get__(self, instance, klass):
57 def native_vfunc(*args, **kwargs):
58 return self._info.invoke(klass.__gtype__, *args, **kwargs)
59 native_vfunc.__info__ = self._info
60 native_vfunc.__name__ = self._info.get_name()
61 native_vfunc.__module__ = self._info.get_namespace()
66 def Constructor(info):
68 def constructor(cls, *args, **kwargs):
69 cls_name = info.get_container().get_name()
70 if cls.__name__ != cls_name:
71 raise TypeError('%s constructor cannot be used to create instances of a subclass' % cls_name)
72 return info.invoke(cls, *args, **kwargs)
74 constructor.__info__ = info
75 constructor.__name__ = info.get_name()
76 constructor.__module__ = info.get_namespace()
81 class MetaClassHelper(object):
83 def _setup_constructors(cls):
84 for method_info in cls.__info__.get_methods():
85 if method_info.is_constructor():
86 name = method_info.get_name()
87 constructor = classmethod(Constructor(method_info))
88 setattr(cls, name, constructor)
90 def _setup_methods(cls):
91 for method_info in cls.__info__.get_methods():
92 name = method_info.get_name()
93 function = Function(method_info)
94 if method_info.is_method():
96 elif method_info.is_constructor():
99 method = staticmethod(function)
100 setattr(cls, name, method)
102 def _setup_fields(cls):
103 for field_info in cls.__info__.get_fields():
104 name = field_info.get_name().replace('-', '_')
105 setattr(cls, name, property(field_info.get_value, field_info.set_value))
107 def _setup_constants(cls):
108 for constant_info in cls.__info__.get_constants():
109 name = constant_info.get_name()
110 value = constant_info.get_value()
111 setattr(cls, name, value)
113 def _setup_vfuncs(cls):
114 for vfunc_name, py_vfunc in cls.__dict__.items():
115 if not vfunc_name.startswith("do_") or not callable(py_vfunc):
118 # If a method name starts with "do_" assume it is a vfunc, and search
119 # in the base classes for a method with the same name to override.
120 # Recursion is not necessary here because getattr() searches all
121 # super class attributes as well.
123 for base in cls.__bases__:
124 method = getattr(base, vfunc_name, None)
125 if method is not None and hasattr(method, '__info__') and \
126 isinstance(method.__info__, VFuncInfo):
127 vfunc_info = method.__info__
130 # If we did not find a matching method name in the bases, we might
131 # be overriding an interface virtual method. Since interfaces do not
132 # provide implementations, there will be no method attribute installed
133 # on the object. Instead we have to search through
134 # InterfaceInfo.get_vfuncs(). Note that the infos returned by
135 # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
136 if vfunc_info is None:
137 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
139 if vfunc_info is not None:
140 assert vfunc_name == ('do_' + vfunc_info.get_name())
141 # Check to see if there are vfuncs with the same name in the bases.
142 # We have no way of specifying which one we are supposed to override.
143 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
144 if ambiguous_base is not None:
145 base_info = vfunc_info.get_container()
146 raise TypeError('Method %s() on class %s.%s is ambiguous '
147 'with methods in base classes %s.%s and %s.%s' %
149 cls.__info__.get_namespace(),
150 cls.__info__.get_name(),
151 base_info.get_namespace(),
152 base_info.get_name(),
153 ambiguous_base.__info__.get_namespace(),
154 ambiguous_base.__info__.get_name()))
155 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
158 def _setup_native_vfuncs(cls):
159 # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
160 # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
161 # Also check if __info__ in __dict__, not hasattr('__info__', ...)
162 # because we do not want to accidentally retrieve __info__ from a base class.
163 class_info = cls.__dict__.get('__info__')
164 if class_info is None or not isinstance(class_info, ObjectInfo):
167 for vfunc_info in class_info.get_vfuncs():
168 name = 'do_%s' % vfunc_info.get_name()
169 value = NativeVFunc(vfunc_info)
170 setattr(cls, name, value)
172 def find_vfunc_info_in_interface(bases, vfunc_name):
174 # All wrapped interfaces inherit from GInterface.
175 # This can be seen in IntrospectionModule.__getattr__() in module.py.
176 # We do not need to search regular classes here, only wrapped interfaces.
177 # We also skip GInterface, because it is not wrapped and has no __info__ attr.
178 if base is _gobject.GInterface or\
179 not issubclass(base, _gobject.GInterface) or\
180 not isinstance(base.__info__, InterfaceInfo):
183 for vfunc in base.__info__.get_vfuncs():
184 if vfunc.get_name() == vfunc_name:
187 vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
188 if vfunc is not None:
193 def find_vfunc_conflict_in_bases(vfunc, bases):
195 if not hasattr(klass, '__info__') or \
196 not hasattr(klass.__info__, 'get_vfuncs'):
198 vfuncs = klass.__info__.get_vfuncs()
199 vfunc_name = vfunc.get_name()
201 if v.get_name() == vfunc_name and v != vfunc:
204 aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
205 if aklass is not None:
209 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
211 def __init__(cls, name, bases, dict_):
212 super(GObjectMeta, cls).__init__(name, bases, dict_)
213 is_gi_defined = False
214 if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
217 is_python_defined = False
218 if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
219 is_python_defined = True
221 if is_python_defined:
225 cls._setup_constants()
226 cls._setup_native_vfuncs()
228 if isinstance(cls.__info__, ObjectInfo):
230 cls._setup_constructors()
231 elif isinstance(cls.__info__, InterfaceInfo):
232 register_interface_info(cls.__info__.get_g_type())
239 """Compute the class precedence list (mro) according to C3
241 Based on http://www.python.org/download/releases/2.3/mro/
242 Modified to consider that interfaces don't create the diamond problem
244 # TODO: If this turns out being too slow, consider using generators
246 bases_of_subclasses = [[C]]
249 bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
251 while bases_of_subclasses:
252 for subclass_bases in bases_of_subclasses:
253 candidate = subclass_bases[0]
254 not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
255 if not_head and _gobject.GInterface not in candidate.__bases__:
256 candidate = None # conflict, reject candidate
260 if candidate is None:
261 raise TypeError('Cannot create a consistent method resolution '
264 bases.append(candidate)
266 for subclass_bases in bases_of_subclasses[:]: # remove candidate
267 if subclass_bases and subclass_bases[0] == candidate:
268 del subclass_bases[0]
269 if not subclass_bases:
270 bases_of_subclasses.remove(subclass_bases)
275 class StructMeta(type, MetaClassHelper):
277 def __init__(cls, name, bases, dict_):
278 super(StructMeta, cls).__init__(name, bases, dict_)
280 # Avoid touching anything else than the base class.
281 g_type = cls.__info__.get_g_type()
282 if g_type != _gobject.TYPE_INVALID and g_type.pytype is not None:
287 cls._setup_constructors()
289 for method_info in cls.__info__.get_methods():
290 if method_info.is_constructor() and \
291 method_info.get_name() == 'new' and \
292 not method_info.get_arguments():
293 cls.__new__ = staticmethod(Constructor(method_info))