Imported Upstream version 3.0.3
[platform/upstream/pygobject2.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 if sys.version_info > (3, 0):
37     def callable(obj):
38         return hasattr(obj, '__call__')
39
40 def Function(info):
41
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()
47
48     return function
49
50
51 class NativeVFunc(object):
52
53     def __init__(self, info):
54         self._info = info
55
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()
62
63         return native_vfunc
64
65
66 def Constructor(info):
67
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)
73
74     constructor.__info__ = info
75     constructor.__name__ = info.get_name()
76     constructor.__module__ = info.get_namespace()
77
78     return constructor
79
80
81 class MetaClassHelper(object):
82
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)
89
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():
95                 method = function
96             elif method_info.is_constructor():
97                 continue
98             else:
99                 method = staticmethod(function)
100             setattr(cls, name, method)
101
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))
106
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)
112
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):
116                 continue
117
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.
122             vfunc_info = None
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__
128                     break
129
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_"):])
138
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' %
148                             (vfunc_name,
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__,
156                                              py_vfunc)
157
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):
165             return
166
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)
171
172 def find_vfunc_info_in_interface(bases, vfunc_name):
173     for base in bases:
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):
181             continue
182
183         for vfunc in base.__info__.get_vfuncs():
184             if vfunc.get_name() == vfunc_name:
185                 return vfunc
186
187         vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
188         if vfunc is not None:
189             return vfunc
190
191     return None
192
193 def find_vfunc_conflict_in_bases(vfunc, bases):
194     for klass in bases:
195         if not hasattr(klass, '__info__') or \
196                 not hasattr(klass.__info__, 'get_vfuncs'):
197             continue
198         vfuncs = klass.__info__.get_vfuncs()
199         vfunc_name = vfunc.get_name()
200         for v in vfuncs:
201             if v.get_name() == vfunc_name and v != vfunc:
202                 return klass
203
204         aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
205         if aklass is not None:
206             return aklass
207     return None
208
209 class GObjectMeta(_gobject.GObjectMeta, MetaClassHelper):
210
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():
215             is_gi_defined = True
216
217         is_python_defined = False
218         if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
219             is_python_defined = True
220
221         if is_python_defined:
222             cls._setup_vfuncs()
223         elif is_gi_defined:
224             cls._setup_methods()
225             cls._setup_constants()
226             cls._setup_native_vfuncs()
227
228             if isinstance(cls.__info__, ObjectInfo):
229                 cls._setup_fields()
230                 cls._setup_constructors()
231             elif isinstance(cls.__info__, InterfaceInfo):
232                 register_interface_info(cls.__info__.get_g_type())
233
234     def mro(cls):
235         return mro(cls)
236
237
238 def mro(C):
239     """Compute the class precedence list (mro) according to C3
240
241     Based on http://www.python.org/download/releases/2.3/mro/
242     Modified to consider that interfaces don't create the diamond problem
243     """
244     # TODO: If this turns out being too slow, consider using generators
245     bases = []
246     bases_of_subclasses = [[C]]
247
248     if C.__bases__:
249         bases_of_subclasses += list(map(mro, C.__bases__)) + [list(C.__bases__)]
250
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
257             else:
258                 break
259
260         if candidate is None:
261             raise TypeError('Cannot create a consistent method resolution '
262                             'order (MRO)')
263
264         bases.append(candidate)
265
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)
271
272     return bases
273
274
275 class StructMeta(type, MetaClassHelper):
276
277     def __init__(cls, name, bases, dict_):
278         super(StructMeta, cls).__init__(name, bases, dict_)
279
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:
283             return
284
285         cls._setup_fields()
286         cls._setup_methods()
287         cls._setup_constructors()
288
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))
294                 break