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
27 from . import _gobject
28 from ._gobject._gobject import GInterface
29 from ._gobject.constants import TYPE_INVALID
37 register_interface_info, \
38 hook_up_vfunc_implementation, \
46 if (3, 0) <= sys.version_info < (3, 3):
47 # callable not available for python 3.0 thru 3.2
49 return hasattr(obj, '__call__')
52 def split_function_info_args(info):
53 """Split a functions args into a tuple of two lists.
55 Note that args marked as DIRECTION_INOUT will be in both lists.
58 Tuple of (in_args, out_args)
62 for arg in info.get_arguments():
63 direction = arg.get_direction()
64 if direction in (DIRECTION_IN, DIRECTION_INOUT):
66 if direction in (DIRECTION_OUT, DIRECTION_INOUT):
68 return (in_args, out_args)
71 def get_callable_info_doc_string(info):
72 """Build a signature string which can be used for documentation."""
73 in_args, out_args = split_function_info_args(info)
75 if isinstance(info, VFuncInfo):
76 in_args_strs = ['self']
77 elif isinstance(info, FunctionInfo):
79 in_args_strs = ['self']
80 elif info.is_constructor():
81 in_args_strs = ['cls']
84 argstr = arg.get_name() + ':' + arg.get_pytype_hint()
86 argstr += '=<optional>'
87 in_args_strs.append(argstr)
88 in_args_str = ', '.join(in_args_strs)
91 out_args_str = ', '.join(arg.get_name() + ':' + arg.get_pytype_hint()
93 return '%s(%s) -> %s' % (info.get_name(), in_args_str, out_args_str)
95 return '%s(%s)' % (info.get_name(), in_args_str)
98 def wraps_callable_info(info):
99 """Similar to functools.wraps but with specific GICallableInfo support."""
100 def update_func(func):
102 func.__name__ = info.get_name()
103 func.__module__ = info.get_namespace()
104 func.__doc__ = get_callable_info_doc_string(info)
110 """Warps GIFunctionInfo"""
111 @wraps_callable_info(info)
112 def function(*args, **kwargs):
113 return info.invoke(*args, **kwargs)
118 class NativeVFunc(object):
119 """Wraps GINativeVFuncInfo"""
120 def __init__(self, info):
123 def __get__(self, instance, klass):
124 @wraps_callable_info(self.__info__)
125 def native_vfunc(*args, **kwargs):
126 return self.__info__.invoke(klass.__gtype__, *args, **kwargs)
130 def Constructor(info):
131 """Warps GIFunctionInfo with get_constructor() == True"""
132 @wraps_callable_info(info)
133 def constructor(cls, *args, **kwargs):
134 cls_name = info.get_container().get_name()
135 if cls.__name__ != cls_name:
136 raise TypeError('%s constructor cannot be used to create instances of a subclass' % cls_name)
137 return info.invoke(cls, *args, **kwargs)
141 class MetaClassHelper(object):
143 def _setup_constructors(cls):
144 for method_info in cls.__info__.get_methods():
145 if method_info.is_constructor():
146 name = method_info.get_name()
147 constructor = classmethod(Constructor(method_info))
148 setattr(cls, name, constructor)
150 def _setup_methods(cls):
151 for method_info in cls.__info__.get_methods():
152 name = method_info.get_name()
153 function = Function(method_info)
154 if method_info.is_method():
156 elif method_info.is_constructor():
159 method = staticmethod(function)
160 setattr(cls, name, method)
162 def _setup_fields(cls):
163 for field_info in cls.__info__.get_fields():
164 name = field_info.get_name().replace('-', '_')
165 setattr(cls, name, property(field_info.get_value, field_info.set_value))
167 def _setup_constants(cls):
168 for constant_info in cls.__info__.get_constants():
169 name = constant_info.get_name()
170 value = constant_info.get_value()
171 setattr(cls, name, value)
173 def _setup_vfuncs(cls):
174 for vfunc_name, py_vfunc in cls.__dict__.items():
175 if not vfunc_name.startswith("do_") or not callable(py_vfunc):
178 # If a method name starts with "do_" assume it is a vfunc, and search
179 # in the base classes for a method with the same name to override.
180 # Recursion is necessary as overriden methods in most immediate parent
181 # classes may shadow vfuncs from classes higher in the hierarchy.
183 for base in cls.__mro__:
184 method = getattr(base, vfunc_name, None)
185 if method is not None and hasattr(method, '__info__') and \
186 isinstance(method.__info__, VFuncInfo):
187 vfunc_info = method.__info__
190 # If we did not find a matching method name in the bases, we might
191 # be overriding an interface virtual method. Since interfaces do not
192 # provide implementations, there will be no method attribute installed
193 # on the object. Instead we have to search through
194 # InterfaceInfo.get_vfuncs(). Note that the infos returned by
195 # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
196 if vfunc_info is None:
197 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
199 if vfunc_info is not None:
200 assert vfunc_name == ('do_' + vfunc_info.get_name())
201 # Check to see if there are vfuncs with the same name in the bases.
202 # We have no way of specifying which one we are supposed to override.
203 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
204 if ambiguous_base is not None:
205 base_info = vfunc_info.get_container()
206 raise TypeError('Method %s() on class %s.%s is ambiguous '
207 'with methods in base classes %s.%s and %s.%s' %
209 cls.__info__.get_namespace(),
210 cls.__info__.get_name(),
211 base_info.get_namespace(),
212 base_info.get_name(),
213 ambiguous_base.__info__.get_namespace(),
214 ambiguous_base.__info__.get_name()
216 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
219 def _setup_native_vfuncs(cls):
220 # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
221 # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
222 # Also check if __info__ in __dict__, not hasattr('__info__', ...)
223 # because we do not want to accidentally retrieve __info__ from a base class.
224 class_info = cls.__dict__.get('__info__')
225 if class_info is None or not isinstance(class_info, ObjectInfo):
228 # Special case skipping of vfuncs for GObject.Object because they will break
229 # the static bindings which will try to use them.
230 if cls.__module__ == 'gi.repository.GObject' and cls.__name__ == 'Object':
233 for vfunc_info in class_info.get_vfuncs():
234 name = 'do_%s' % vfunc_info.get_name()
235 value = NativeVFunc(vfunc_info)
236 setattr(cls, name, value)
239 def find_vfunc_info_in_interface(bases, vfunc_name):
241 # All wrapped interfaces inherit from GInterface.
242 # This can be seen in IntrospectionModule.__getattr__() in module.py.
243 # We do not need to search regular classes here, only wrapped interfaces.
244 # We also skip GInterface, because it is not wrapped and has no __info__ attr.
245 # Skip bases without __info__ (static _gobject._gobject.GObject)
246 if base is GInterface or\
247 not issubclass(base, GInterface) or\
248 not hasattr(base, '__info__') or\
249 not isinstance(base.__info__, InterfaceInfo):
252 for vfunc in base.__info__.get_vfuncs():
253 if vfunc.get_name() == vfunc_name:
256 vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
257 if vfunc is not None:
263 def find_vfunc_conflict_in_bases(vfunc, bases):
265 if not hasattr(klass, '__info__') or \
266 not hasattr(klass.__info__, 'get_vfuncs'):
268 vfuncs = klass.__info__.get_vfuncs()
269 vfunc_name = vfunc.get_name()
271 if v.get_name() == vfunc_name and v != vfunc:
274 aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
275 if aklass is not None:
280 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
282 def __init__(cls, name, bases, dict_):
283 super(GObjectMeta, cls).__init__(name, bases, dict_)
284 is_gi_defined = False
285 if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
288 is_python_defined = False
289 if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
290 is_python_defined = True
292 if is_python_defined:
296 cls._setup_constants()
297 cls._setup_native_vfuncs()
299 if isinstance(cls.__info__, ObjectInfo):
301 cls._setup_constructors()
302 elif isinstance(cls.__info__, InterfaceInfo):
303 register_interface_info(cls.__info__.get_g_type())
310 """Compute the class precedence list (mro) according to C3
312 Based on http://www.python.org/download/releases/2.3/mro/
313 Modified to consider that interfaces don't create the diamond problem
315 # TODO: If this turns out being too slow, consider using generators
317 bases_of_subclasses = [[C]]
320 bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
322 while bases_of_subclasses:
323 for subclass_bases in bases_of_subclasses:
324 candidate = subclass_bases[0]
325 not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
326 if not_head and GInterface not in candidate.__bases__:
327 candidate = None # conflict, reject candidate
331 if candidate is None:
332 raise TypeError('Cannot create a consistent method resolution '
335 bases.append(candidate)
337 for subclass_bases in bases_of_subclasses[:]: # remove candidate
338 if subclass_bases and subclass_bases[0] == candidate:
339 del subclass_bases[0]
340 if not subclass_bases:
341 bases_of_subclasses.remove(subclass_bases)
346 class StructMeta(type, MetaClassHelper):
348 def __init__(cls, name, bases, dict_):
349 super(StructMeta, cls).__init__(name, bases, dict_)
351 # Avoid touching anything else than the base class.
352 g_type = cls.__info__.get_g_type()
353 if g_type != TYPE_INVALID and g_type.pytype is not None:
358 cls._setup_constructors()
360 for method_info in cls.__info__.get_methods():
361 if method_info.is_constructor() and \
362 method_info.get_name() == 'new' and \
363 not method_info.get_arguments():
364 cls.__new__ = staticmethod(Constructor(method_info))