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