Imported Upstream version 3.7.3
[platform/upstream/python-gobject.git] / gi / types.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 # Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org>
5 #
6 #   types.py: base types for introspected items.
7 #
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.
12 #
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.
17 #
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
21 # USA
22
23 from __future__ import absolute_import
24
25 import sys
26
27 from . import _gobject
28 from ._gobject._gobject import GInterface
29 from ._gobject.constants import TYPE_INVALID
30
31 from ._gi import \
32     InterfaceInfo, \
33     ObjectInfo, \
34     StructInfo, \
35     VFuncInfo, \
36     FunctionInfo, \
37     register_interface_info, \
38     hook_up_vfunc_implementation, \
39     DIRECTION_IN, \
40     DIRECTION_OUT, \
41     DIRECTION_INOUT
42
43
44 StructInfo  # pyflakes
45
46 if (3, 0) <= sys.version_info < (3, 3):
47     # callable not available for python 3.0 thru 3.2
48     def callable(obj):
49         return hasattr(obj, '__call__')
50
51
52 def split_function_info_args(info):
53     """Split a functions args into a tuple of two lists.
54
55     Note that args marked as DIRECTION_INOUT will be in both lists.
56
57     :Returns:
58         Tuple of (in_args, out_args)
59     """
60     in_args = []
61     out_args = []
62     for arg in info.get_arguments():
63         direction = arg.get_direction()
64         if direction in (DIRECTION_IN, DIRECTION_INOUT):
65             in_args.append(arg)
66         if direction in (DIRECTION_OUT, DIRECTION_INOUT):
67             out_args.append(arg)
68     return (in_args, out_args)
69
70
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)
74     in_args_strs = []
75     if isinstance(info, VFuncInfo):
76         in_args_strs = ['self']
77     elif isinstance(info, FunctionInfo):
78         if info.is_method():
79             in_args_strs = ['self']
80         elif info.is_constructor():
81             in_args_strs = ['cls']
82
83     for arg in in_args:
84         argstr = arg.get_name() + ':' + arg.get_pytype_hint()
85         if arg.is_optional():
86             argstr += '=<optional>'
87         in_args_strs.append(argstr)
88     in_args_str = ', '.join(in_args_strs)
89
90     if out_args:
91         out_args_str = ', '.join(arg.get_name() + ':' + arg.get_pytype_hint()
92                                  for arg in out_args)
93         return '%s(%s) -> %s' % (info.get_name(), in_args_str, out_args_str)
94     else:
95         return '%s(%s)' % (info.get_name(), in_args_str)
96
97
98 def wraps_callable_info(info):
99     """Similar to functools.wraps but with specific GICallableInfo support."""
100     def update_func(func):
101         func.__info__ = info
102         func.__name__ = info.get_name()
103         func.__module__ = info.get_namespace()
104         func.__doc__ = get_callable_info_doc_string(info)
105         return func
106     return update_func
107
108
109 def Function(info):
110     """Warps GIFunctionInfo"""
111     @wraps_callable_info(info)
112     def function(*args, **kwargs):
113         return info.invoke(*args, **kwargs)
114
115     return function
116
117
118 class NativeVFunc(object):
119     """Wraps GINativeVFuncInfo"""
120     def __init__(self, info):
121         self.__info__ = info
122
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)
127         return native_vfunc
128
129
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)
138     return constructor
139
140
141 class MetaClassHelper(object):
142
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)
149
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():
155                 method = function
156             elif method_info.is_constructor():
157                 continue
158             else:
159                 method = staticmethod(function)
160             setattr(cls, name, method)
161
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))
166
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)
172
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):
176                 continue
177
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.
182             vfunc_info = None
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__
188                     break
189
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_"):])
198
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' %
208                                     (vfunc_name,
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()
215                                     ))
216                 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
217                                              py_vfunc)
218
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):
226             return
227
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':
231             return
232
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)
237
238
239 def find_vfunc_info_in_interface(bases, vfunc_name):
240     for base in bases:
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):
250             continue
251
252         for vfunc in base.__info__.get_vfuncs():
253             if vfunc.get_name() == vfunc_name:
254                 return vfunc
255
256         vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
257         if vfunc is not None:
258             return vfunc
259
260     return None
261
262
263 def find_vfunc_conflict_in_bases(vfunc, bases):
264     for klass in bases:
265         if not hasattr(klass, '__info__') or \
266                 not hasattr(klass.__info__, 'get_vfuncs'):
267             continue
268         vfuncs = klass.__info__.get_vfuncs()
269         vfunc_name = vfunc.get_name()
270         for v in vfuncs:
271             if v.get_name() == vfunc_name and v != vfunc:
272                 return klass
273
274         aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
275         if aklass is not None:
276             return aklass
277     return None
278
279
280 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
281
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():
286             is_gi_defined = True
287
288         is_python_defined = False
289         if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
290             is_python_defined = True
291
292         if is_python_defined:
293             cls._setup_vfuncs()
294         elif is_gi_defined:
295             cls._setup_methods()
296             cls._setup_constants()
297             cls._setup_native_vfuncs()
298
299             if isinstance(cls.__info__, ObjectInfo):
300                 cls._setup_fields()
301                 cls._setup_constructors()
302             elif isinstance(cls.__info__, InterfaceInfo):
303                 register_interface_info(cls.__info__.get_g_type())
304
305     def mro(cls):
306         return mro(cls)
307
308
309 def mro(C):
310     """Compute the class precedence list (mro) according to C3
311
312     Based on http://www.python.org/download/releases/2.3/mro/
313     Modified to consider that interfaces don't create the diamond problem
314     """
315     # TODO: If this turns out being too slow, consider using generators
316     bases = []
317     bases_of_subclasses = [[C]]
318
319     if C.__bases__:
320         bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
321
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
328             else:
329                 break
330
331         if candidate is None:
332             raise TypeError('Cannot create a consistent method resolution '
333                             'order (MRO)')
334
335         bases.append(candidate)
336
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)
342
343     return bases
344
345
346 class StructMeta(type, MetaClassHelper):
347
348     def __init__(cls, name, bases, dict_):
349         super(StructMeta, cls).__init__(name, bases, dict_)
350
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:
354             return
355
356         cls._setup_fields()
357         cls._setup_methods()
358         cls._setup_constructors()
359
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))
365                 break