3b94294d24a47e5f6538e84897f9d60159da47a4
[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 from . import _gobject
27
28 from ._gi import \
29     InterfaceInfo, \
30     ObjectInfo, \
31     StructInfo, \
32     VFuncInfo, \
33     register_interface_info, \
34     hook_up_vfunc_implementation
35
36
37 StructInfo  # pyflakes
38
39 if sys.version_info > (3, 0):
40     def callable(obj):
41         return hasattr(obj, '__call__')
42
43
44 def Function(info):
45
46     def function(*args, **kwargs):
47         return info.invoke(*args, **kwargs)
48     function.__info__ = info
49     function.__name__ = info.get_name()
50     function.__module__ = info.get_namespace()
51
52     return function
53
54
55 class NativeVFunc(object):
56
57     def __init__(self, info):
58         self._info = info
59
60     def __get__(self, instance, klass):
61         def native_vfunc(*args, **kwargs):
62             return self._info.invoke(klass.__gtype__, *args, **kwargs)
63         native_vfunc.__info__ = self._info
64         native_vfunc.__name__ = self._info.get_name()
65         native_vfunc.__module__ = self._info.get_namespace()
66
67         return native_vfunc
68
69
70 def Constructor(info):
71
72     def constructor(cls, *args, **kwargs):
73         cls_name = info.get_container().get_name()
74         if cls.__name__ != cls_name:
75             raise TypeError('%s constructor cannot be used to create instances of a subclass' % cls_name)
76         return info.invoke(cls, *args, **kwargs)
77
78     constructor.__info__ = info
79     constructor.__name__ = info.get_name()
80     constructor.__module__ = info.get_namespace()
81
82     return constructor
83
84
85 class MetaClassHelper(object):
86
87     def _setup_constructors(cls):
88         for method_info in cls.__info__.get_methods():
89             if method_info.is_constructor():
90                 name = method_info.get_name()
91                 constructor = classmethod(Constructor(method_info))
92                 setattr(cls, name, constructor)
93
94     def _setup_methods(cls):
95         for method_info in cls.__info__.get_methods():
96             name = method_info.get_name()
97             function = Function(method_info)
98             if method_info.is_method():
99                 method = function
100             elif method_info.is_constructor():
101                 continue
102             else:
103                 method = staticmethod(function)
104             setattr(cls, name, method)
105
106     def _setup_fields(cls):
107         for field_info in cls.__info__.get_fields():
108             name = field_info.get_name().replace('-', '_')
109             setattr(cls, name, property(field_info.get_value, field_info.set_value))
110
111     def _setup_constants(cls):
112         for constant_info in cls.__info__.get_constants():
113             name = constant_info.get_name()
114             value = constant_info.get_value()
115             setattr(cls, name, value)
116
117     def _setup_vfuncs(cls):
118         for vfunc_name, py_vfunc in cls.__dict__.items():
119             if not vfunc_name.startswith("do_") or not callable(py_vfunc):
120                 continue
121
122             # If a method name starts with "do_" assume it is a vfunc, and search
123             # in the base classes for a method with the same name to override.
124             # Recursion is necessary as overriden methods in most immediate parent
125             # classes may shadow vfuncs from classes higher in the hierarchy.
126             vfunc_info = None
127             for base in cls.__mro__:
128                 method = getattr(base, vfunc_name, None)
129                 if method is not None and hasattr(method, '__info__') and \
130                         isinstance(method.__info__, VFuncInfo):
131                     vfunc_info = method.__info__
132                     break
133
134             # If we did not find a matching method name in the bases, we might
135             # be overriding an interface virtual method. Since interfaces do not
136             # provide implementations, there will be no method attribute installed
137             # on the object. Instead we have to search through
138             # InterfaceInfo.get_vfuncs(). Note that the infos returned by
139             # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
140             if vfunc_info is None:
141                 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
142
143             if vfunc_info is not None:
144                 assert vfunc_name == ('do_' + vfunc_info.get_name())
145                 # Check to see if there are vfuncs with the same name in the bases.
146                 # We have no way of specifying which one we are supposed to override.
147                 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
148                 if ambiguous_base is not None:
149                     base_info = vfunc_info.get_container()
150                     raise TypeError('Method %s() on class %s.%s is ambiguous '
151                                     'with methods in base classes %s.%s and %s.%s' %
152                                     (vfunc_name,
153                                      cls.__info__.get_namespace(),
154                                      cls.__info__.get_name(),
155                                      base_info.get_namespace(),
156                                      base_info.get_name(),
157                                      ambiguous_base.__info__.get_namespace(),
158                                      ambiguous_base.__info__.get_name()
159                                     ))
160                 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
161                                              py_vfunc)
162
163     def _setup_native_vfuncs(cls):
164         # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
165         # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
166         # Also check if __info__ in __dict__, not hasattr('__info__', ...)
167         # because we do not want to accidentally retrieve __info__ from a base class.
168         class_info = cls.__dict__.get('__info__')
169         if class_info is None or not isinstance(class_info, ObjectInfo):
170             return
171
172         for vfunc_info in class_info.get_vfuncs():
173             name = 'do_%s' % vfunc_info.get_name()
174             value = NativeVFunc(vfunc_info)
175             setattr(cls, name, value)
176
177
178 def find_vfunc_info_in_interface(bases, vfunc_name):
179     for base in bases:
180         # All wrapped interfaces inherit from GInterface.
181         # This can be seen in IntrospectionModule.__getattr__() in module.py.
182         # We do not need to search regular classes here, only wrapped interfaces.
183         # We also skip GInterface, because it is not wrapped and has no __info__ attr.
184         if base is _gobject.GInterface or\
185                 not issubclass(base, _gobject.GInterface) or\
186                 not isinstance(base.__info__, InterfaceInfo):
187             continue
188
189         for vfunc in base.__info__.get_vfuncs():
190             if vfunc.get_name() == vfunc_name:
191                 return vfunc
192
193         vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
194         if vfunc is not None:
195             return vfunc
196
197     return None
198
199
200 def find_vfunc_conflict_in_bases(vfunc, bases):
201     for klass in bases:
202         if not hasattr(klass, '__info__') or \
203                 not hasattr(klass.__info__, 'get_vfuncs'):
204             continue
205         vfuncs = klass.__info__.get_vfuncs()
206         vfunc_name = vfunc.get_name()
207         for v in vfuncs:
208             if v.get_name() == vfunc_name and v != vfunc:
209                 return klass
210
211         aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
212         if aklass is not None:
213             return aklass
214     return None
215
216
217 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
218
219     def __init__(cls, name, bases, dict_):
220         super(GObjectMeta, cls).__init__(name, bases, dict_)
221         is_gi_defined = False
222         if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
223             is_gi_defined = True
224
225         is_python_defined = False
226         if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
227             is_python_defined = True
228
229         if is_python_defined:
230             cls._setup_vfuncs()
231         elif is_gi_defined:
232             cls._setup_methods()
233             cls._setup_constants()
234             cls._setup_native_vfuncs()
235
236             if isinstance(cls.__info__, ObjectInfo):
237                 cls._setup_fields()
238                 cls._setup_constructors()
239             elif isinstance(cls.__info__, InterfaceInfo):
240                 register_interface_info(cls.__info__.get_g_type())
241
242     def mro(cls):
243         return mro(cls)
244
245
246 def mro(C):
247     """Compute the class precedence list (mro) according to C3
248
249     Based on http://www.python.org/download/releases/2.3/mro/
250     Modified to consider that interfaces don't create the diamond problem
251     """
252     # TODO: If this turns out being too slow, consider using generators
253     bases = []
254     bases_of_subclasses = [[C]]
255
256     if C.__bases__:
257         bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
258
259     while bases_of_subclasses:
260         for subclass_bases in bases_of_subclasses:
261             candidate = subclass_bases[0]
262             not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
263             if not_head and _gobject.GInterface not in candidate.__bases__:
264                 candidate = None  # conflict, reject candidate
265             else:
266                 break
267
268         if candidate is None:
269             raise TypeError('Cannot create a consistent method resolution '
270                             'order (MRO)')
271
272         bases.append(candidate)
273
274         for subclass_bases in bases_of_subclasses[:]:  # remove candidate
275             if subclass_bases and subclass_bases[0] == candidate:
276                 del subclass_bases[0]
277                 if not subclass_bases:
278                     bases_of_subclasses.remove(subclass_bases)
279
280     return bases
281
282
283 class StructMeta(type, MetaClassHelper):
284
285     def __init__(cls, name, bases, dict_):
286         super(StructMeta, cls).__init__(name, bases, dict_)
287
288         # Avoid touching anything else than the base class.
289         g_type = cls.__info__.get_g_type()
290         if g_type != _gobject.TYPE_INVALID and g_type.pytype is not None:
291             return
292
293         cls._setup_fields()
294         cls._setup_methods()
295         cls._setup_constructors()
296
297         for method_info in cls.__info__.get_methods():
298             if method_info.is_constructor() and \
299                     method_info.get_name() == 'new' and \
300                     not method_info.get_arguments():
301                 cls.__new__ = staticmethod(Constructor(method_info))
302                 break