Imported Upstream version 3.13.1
[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 import warnings
27
28 from ._constants import TYPE_INVALID
29 from .docstring import generate_doc_string
30
31 from ._gi import \
32     InterfaceInfo, \
33     ObjectInfo, \
34     StructInfo, \
35     VFuncInfo, \
36     register_interface_info, \
37     hook_up_vfunc_implementation, \
38     _gobject
39
40 GInterface = _gobject.GInterface
41
42 StructInfo  # pyflakes
43
44 from . import _propertyhelper as propertyhelper
45 from . import _signalhelper as signalhelper
46
47 if (3, 0) <= sys.version_info < (3, 3):
48     # callable not available for python 3.0 thru 3.2
49     def callable(obj):
50         return hasattr(obj, '__call__')
51
52
53 class MetaClassHelper(object):
54     def _setup_methods(cls):
55         for method_info in cls.__info__.get_methods():
56             setattr(cls, method_info.__name__, method_info)
57
58     def _setup_fields(cls):
59         for field_info in cls.__info__.get_fields():
60             name = field_info.get_name().replace('-', '_')
61             setattr(cls, name, property(field_info.get_value, field_info.set_value))
62
63     def _setup_constants(cls):
64         for constant_info in cls.__info__.get_constants():
65             name = constant_info.get_name()
66             value = constant_info.get_value()
67             setattr(cls, name, value)
68
69     def _setup_vfuncs(cls):
70         for vfunc_name, py_vfunc in cls.__dict__.items():
71             if not vfunc_name.startswith("do_") or not callable(py_vfunc):
72                 continue
73
74             # If a method name starts with "do_" assume it is a vfunc, and search
75             # in the base classes for a method with the same name to override.
76             # Recursion is necessary as overriden methods in most immediate parent
77             # classes may shadow vfuncs from classes higher in the hierarchy.
78             vfunc_info = None
79             for base in cls.__mro__:
80                 method = getattr(base, vfunc_name, None)
81                 if method is not None and isinstance(method, VFuncInfo):
82                     vfunc_info = method
83                     break
84
85             # If we did not find a matching method name in the bases, we might
86             # be overriding an interface virtual method. Since interfaces do not
87             # provide implementations, there will be no method attribute installed
88             # on the object. Instead we have to search through
89             # InterfaceInfo.get_vfuncs(). Note that the infos returned by
90             # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix).
91             if vfunc_info is None:
92                 vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):])
93
94             if vfunc_info is not None:
95                 assert vfunc_name == ('do_' + vfunc_info.get_name())
96                 # Check to see if there are vfuncs with the same name in the bases.
97                 # We have no way of specifying which one we are supposed to override.
98                 ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__)
99                 if ambiguous_base is not None:
100                     base_info = vfunc_info.get_container()
101                     raise TypeError('Method %s() on class %s.%s is ambiguous '
102                                     'with methods in base classes %s.%s and %s.%s' %
103                                     (vfunc_name,
104                                      cls.__info__.get_namespace(),
105                                      cls.__info__.get_name(),
106                                      base_info.get_namespace(),
107                                      base_info.get_name(),
108                                      ambiguous_base.__info__.get_namespace(),
109                                      ambiguous_base.__info__.get_name()
110                                     ))
111                 hook_up_vfunc_implementation(vfunc_info, cls.__gtype__,
112                                              py_vfunc)
113
114     def _setup_native_vfuncs(cls):
115         # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method.
116         # We skip InterfaceInfo because interfaces have no implementations for vfuncs.
117         # Also check if __info__ in __dict__, not hasattr('__info__', ...)
118         # because we do not want to accidentally retrieve __info__ from a base class.
119         class_info = cls.__dict__.get('__info__')
120         if class_info is None or not isinstance(class_info, ObjectInfo):
121             return
122
123         # Special case skipping of vfuncs for GObject.Object because they will break
124         # the static bindings which will try to use them.
125         if cls.__module__ == 'gi.repository.GObject' and cls.__name__ == 'Object':
126             return
127
128         for vfunc_info in class_info.get_vfuncs():
129             name = 'do_%s' % vfunc_info.__name__
130             setattr(cls, name, vfunc_info)
131
132
133 def find_vfunc_info_in_interface(bases, vfunc_name):
134     for base in bases:
135         # All wrapped interfaces inherit from GInterface.
136         # This can be seen in IntrospectionModule.__getattr__() in module.py.
137         # We do not need to search regular classes here, only wrapped interfaces.
138         # We also skip GInterface, because it is not wrapped and has no __info__ attr.
139         # Skip bases without __info__ (static _gobject._gobject.GObject)
140         if base is GInterface or\
141                 not issubclass(base, GInterface) or\
142                 not hasattr(base, '__info__'):
143             continue
144
145         # Only look at this classes vfuncs if it is an interface.
146         if isinstance(base.__info__, InterfaceInfo):
147             for vfunc in base.__info__.get_vfuncs():
148                 if vfunc.get_name() == vfunc_name:
149                     return vfunc
150
151         # Recurse into the parent classes
152         vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name)
153         if vfunc is not None:
154             return vfunc
155
156     return None
157
158
159 def find_vfunc_conflict_in_bases(vfunc, bases):
160     for klass in bases:
161         if not hasattr(klass, '__info__') or \
162                 not hasattr(klass.__info__, 'get_vfuncs'):
163             continue
164         vfuncs = klass.__info__.get_vfuncs()
165         vfunc_name = vfunc.get_name()
166         for v in vfuncs:
167             if v.get_name() == vfunc_name and v != vfunc:
168                 return klass
169
170         aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__)
171         if aklass is not None:
172             return aklass
173     return None
174
175
176 class _GObjectMetaBase(type):
177     """Metaclass for automatically registering GObject classes."""
178     def __init__(cls, name, bases, dict_):
179         type.__init__(cls, name, bases, dict_)
180         propertyhelper.install_properties(cls)
181         signalhelper.install_signals(cls)
182         cls._type_register(cls.__dict__)
183
184     def _type_register(cls, namespace):
185         ## don't register the class if already registered
186         if '__gtype__' in namespace:
187             return
188
189         # Do not register a new GType for the overrides, as this would sort of
190         # defeat the purpose of overrides...
191         if cls.__module__.startswith('gi.overrides.'):
192             return
193
194         _gobject.type_register(cls, namespace.get('__gtype_name__'))
195
196 _gobject._install_metaclass(_GObjectMetaBase)
197
198
199 class GObjectMeta(_GObjectMetaBase, MetaClassHelper):
200     """Meta class used for GI GObject based types."""
201     def __init__(cls, name, bases, dict_):
202         super(GObjectMeta, cls).__init__(name, bases, dict_)
203         is_gi_defined = False
204         if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace():
205             is_gi_defined = True
206
207         is_python_defined = False
208         if not is_gi_defined and cls.__module__ != GObjectMeta.__module__:
209             is_python_defined = True
210
211         if is_python_defined:
212             cls._setup_vfuncs()
213         elif is_gi_defined:
214             cls._setup_methods()
215             cls._setup_constants()
216             cls._setup_native_vfuncs()
217
218             if isinstance(cls.__info__, ObjectInfo):
219                 cls._setup_fields()
220             elif isinstance(cls.__info__, InterfaceInfo):
221                 register_interface_info(cls.__info__.get_g_type())
222
223     def mro(cls):
224         return mro(cls)
225
226     @property
227     def __doc__(cls):
228         if cls == GObjectMeta:
229             return ''
230         return generate_doc_string(cls.__info__)
231
232
233 def mro(C):
234     """Compute the class precedence list (mro) according to C3, with GObject
235     interface considerations.
236
237     We override Python's MRO calculation to account for the fact that
238     GObject classes are not affected by the diamond problem:
239     http://en.wikipedia.org/wiki/Diamond_problem
240
241     Based on http://www.python.org/download/releases/2.3/mro/
242     """
243     # TODO: If this turns out being too slow, consider using generators
244     bases = []
245     bases_of_subclasses = [[C]]
246
247     if C.__bases__:
248         for base in C.__bases__:
249             # Python causes MRO's to be calculated starting with the lowest
250             # base class and working towards the descendant, storing the result
251             # in __mro__ at each point. Therefore at this point we know that
252             # we already have our base class MRO's available to us, there is
253             # no need for us to (re)calculate them.
254             if hasattr(base, '__mro__'):
255                 bases_of_subclasses += [list(base.__mro__)]
256             else:
257                 warnings.warn('Mixin class %s is an old style class, please '
258                               'update this to derive from "object".' % base,
259                               RuntimeWarning)
260                 # For old-style classes (Python2 only), the MRO is not
261                 # easily accessible. As we do need it here, we calculate
262                 # it via recursion, according to the C3 algorithm. Using C3
263                 # for old style classes deviates from Python's own behaviour,
264                 # but visible effects here would be a corner case triggered by
265                 # questionable design.
266                 bases_of_subclasses += [mro(base)]
267         bases_of_subclasses += [list(C.__bases__)]
268
269     while bases_of_subclasses:
270         for subclass_bases in bases_of_subclasses:
271             candidate = subclass_bases[0]
272             not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
273             if not_head and GInterface not in candidate.__bases__:
274                 candidate = None  # conflict, reject candidate
275             else:
276                 break
277
278         if candidate is None:
279             raise TypeError('Cannot create a consistent method resolution '
280                             'order (MRO)')
281
282         bases.append(candidate)
283
284         for subclass_bases in bases_of_subclasses[:]:  # remove candidate
285             if subclass_bases and subclass_bases[0] == candidate:
286                 del subclass_bases[0]
287                 if not subclass_bases:
288                     bases_of_subclasses.remove(subclass_bases)
289
290     return bases
291
292
293 class StructMeta(type, MetaClassHelper):
294     """Meta class used for GI Struct based types."""
295
296     def __init__(cls, name, bases, dict_):
297         super(StructMeta, cls).__init__(name, bases, dict_)
298
299         # Avoid touching anything else than the base class.
300         g_type = cls.__info__.get_g_type()
301         if g_type != TYPE_INVALID and g_type.pytype is not None:
302             return
303
304         cls._setup_fields()
305         cls._setup_methods()
306
307         for method_info in cls.__info__.get_methods():
308             if method_info.is_constructor() and \
309                     method_info.__name__ == 'new' and \
310                     not method_info.get_arguments():
311                 cls.__new__ = staticmethod(method_info)
312                 break
313
314     @property
315     def __doc__(cls):
316         if cls == StructMeta:
317             return ''
318         return generate_doc_string(cls.__info__)