e115fc74b576f3b3cbfa629d247fb2ffab1585bf
[platform/upstream/python-gobject.git] / gi / module.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 # Copyright (C) 2007-2009 Johan Dahlin <johan@gnome.org>
5 #
6 #   module.py: dynamic module for introspected libraries.
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 types
27
28 _have_py3 = (sys.version_info[0] >= 3)
29
30 from . import _glib, _gobject
31 try:
32     maketrans = ''.maketrans
33 except AttributeError:
34     # fallback for Python 2
35     from string import maketrans
36
37 import gi
38 from .overrides import registry
39
40 from ._gi import \
41     Repository, \
42     FunctionInfo, \
43     RegisteredTypeInfo, \
44     EnumInfo, \
45     ObjectInfo, \
46     InterfaceInfo, \
47     ConstantInfo, \
48     StructInfo, \
49     UnionInfo, \
50     CallbackInfo, \
51     Struct, \
52     Boxed, \
53     CCallback, \
54     enum_add, \
55     enum_register_new_gtype_and_add, \
56     flags_add, \
57     flags_register_new_gtype_and_add
58 from .types import \
59     GObjectMeta, \
60     StructMeta, \
61     Function
62
63 repository = Repository.get_default()
64
65
66 def get_parent_for_object(object_info):
67     parent_object_info = object_info.get_parent()
68
69     if not parent_object_info:
70         return object
71
72     namespace = parent_object_info.get_namespace()
73     name = parent_object_info.get_name()
74
75     # Workaround for GObject.Object and GObject.InitiallyUnowned.
76     if namespace == 'GObject' and name == 'Object' or name == 'InitiallyUnowned':
77         return _gobject.GObject
78
79     module = __import__('gi.repository.%s' % namespace, fromlist=[name])
80     return getattr(module, name)
81
82
83 def get_interfaces_for_object(object_info):
84     interfaces = []
85     for interface_info in object_info.get_interfaces():
86         namespace = interface_info.get_namespace()
87         name = interface_info.get_name()
88
89         module = __import__('gi.repository.%s' % namespace, fromlist=[name])
90         interfaces.append(getattr(module, name))
91     return interfaces
92
93
94 class IntrospectionModule(object):
95
96     def __init__(self, namespace, version=None):
97         repository.require(namespace, version)
98         self._namespace = namespace
99         self._version = version
100         self.__name__ = 'gi.repository.' + namespace
101
102         repository.require(self._namespace, self._version)
103         self.__path__ = repository.get_typelib_path(self._namespace)
104         if _have_py3:
105             # get_typelib_path() delivers bytes, not a string
106             self.__path__ = self.__path__.decode('UTF-8')
107
108         if self._version is None:
109             self._version = repository.get_version(self._namespace)
110
111     def __getattr__(self, name):
112         info = repository.find_by_name(self._namespace, name)
113         if not info:
114             raise AttributeError("%r object has no attribute %r" % (
115                                  self.__name__, name))
116
117         if isinstance(info, EnumInfo):
118             g_type = info.get_g_type()
119             wrapper = g_type.pytype
120
121             if wrapper is None:
122                 if info.is_flags():
123                     if g_type.is_a(_gobject.TYPE_FLAGS):
124                         wrapper = flags_add(g_type)
125                     else:
126                         assert g_type == _gobject.TYPE_NONE
127                         wrapper = flags_register_new_gtype_and_add(info)
128                 else:
129                     if g_type.is_a(_gobject.TYPE_ENUM):
130                         wrapper = enum_add(g_type)
131                     else:
132                         assert g_type == _gobject.TYPE_NONE
133                         wrapper = enum_register_new_gtype_and_add(info)
134
135                 wrapper.__info__ = info
136                 wrapper.__module__ = 'gi.repository.' + info.get_namespace()
137
138                 # Don't use upper() here to avoid locale specific
139                 # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
140                 # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
141                 ascii_upper_trans = maketrans(
142                     'abcdefgjhijklmnopqrstuvwxyz',
143                     'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
144                 for value_info in info.get_values():
145                     value_name = value_info.get_name_unescaped().translate(ascii_upper_trans)
146                     setattr(wrapper, value_name, wrapper(value_info.get_value()))
147
148             if g_type != _gobject.TYPE_NONE:
149                 g_type.pytype = wrapper
150
151         elif isinstance(info, RegisteredTypeInfo):
152             g_type = info.get_g_type()
153
154             # Check if there is already a Python wrapper.
155             if g_type != _gobject.TYPE_NONE:
156                 type_ = g_type.pytype
157                 if type_ is not None:
158                     self.__dict__[name] = type_
159                     return type_
160
161             # Create a wrapper.
162             if isinstance(info, ObjectInfo):
163                 parent = get_parent_for_object(info)
164                 interfaces = tuple(interface for interface in get_interfaces_for_object(info)
165                                    if not issubclass(parent, interface))
166                 bases = (parent,) + interfaces
167                 metaclass = GObjectMeta
168             elif isinstance(info, CallbackInfo):
169                 bases = (CCallback,)
170                 metaclass = GObjectMeta
171             elif isinstance(info, InterfaceInfo):
172                 bases = (_gobject.GInterface,)
173                 metaclass = GObjectMeta
174             elif isinstance(info, (StructInfo, UnionInfo)):
175                 if g_type.is_a(_gobject.TYPE_BOXED):
176                     bases = (Boxed,)
177                 elif (g_type.is_a(_gobject.TYPE_POINTER) or
178                       g_type == _gobject.TYPE_NONE or
179                       g_type.fundamental == g_type):
180                     bases = (Struct,)
181                 else:
182                     raise TypeError("unable to create a wrapper for %s.%s" % (info.get_namespace(), info.get_name()))
183                 metaclass = StructMeta
184             else:
185                 raise NotImplementedError(info)
186
187             name = info.get_name()
188             dict_ = {
189                 '__info__': info,
190                 '__module__': 'gi.repository.' + self._namespace,
191                 '__gtype__': g_type
192             }
193             wrapper = metaclass(name, bases, dict_)
194
195             # Register the new Python wrapper.
196             if g_type != _gobject.TYPE_NONE:
197                 g_type.pytype = wrapper
198
199         elif isinstance(info, FunctionInfo):
200             wrapper = Function(info)
201         elif isinstance(info, ConstantInfo):
202             wrapper = info.get_value()
203         else:
204             raise NotImplementedError(info)
205
206         self.__dict__[name] = wrapper
207         return wrapper
208
209     def __repr__(self):
210         path = repository.get_typelib_path(self._namespace)
211         if _have_py3:
212             # get_typelib_path() delivers bytes, not a string
213             path = path.decode('UTF-8')
214         return "<IntrospectionModule %r from %r>" % (self._namespace, path)
215
216     def __dir__(self):
217         # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
218         result = set(dir(self.__class__))
219         result.update(self.__dict__.keys())
220
221         # update *set* because some repository attributes have already been
222         # wrapped by __getattr__() and included in self.__dict__; but skip
223         # Callback types, as these are not real objects which we can actually
224         # get
225         namespace_infos = repository.get_infos(self._namespace)
226         result.update(info.get_name() for info in namespace_infos if
227                       not isinstance(info, CallbackInfo))
228
229         return list(result)
230
231
232 class DynamicModule(types.ModuleType):
233     def __init__(self, namespace):
234         self._namespace = namespace
235         self._introspection_module = None
236         self._overrides_module = None
237         self.__path__ = None
238
239     def _load(self):
240         version = gi.get_required_version(self._namespace)
241         self._introspection_module = IntrospectionModule(self._namespace,
242                                                          version)
243         try:
244             overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
245             self._overrides_module = getattr(overrides_modules, self._namespace, None)
246         except ImportError:
247             self._overrides_module = None
248
249         self.__path__ = repository.get_typelib_path(self._namespace)
250         if _have_py3:
251             # get_typelib_path() delivers bytes, not a string
252             self.__path__ = self.__path__.decode('UTF-8')
253
254     def __getattr__(self, name):
255         if self._overrides_module is not None:
256             override_exports = getattr(self._overrides_module, '__all__', ())
257             if name in override_exports:
258                 return getattr(self._overrides_module, name, None)
259         else:
260             # check the registry just in case the module hasn't loaded yet
261             # TODO: Only gtypes are registered in the registry right now
262             #       but it would be nice to register all overrides and
263             #       get rid of the module imports. We might actually see a
264             #       speedup.
265             key = '%s.%s' % (self._namespace, name)
266             if key in registry:
267                 return registry[key]
268
269         return getattr(self._introspection_module, name)
270
271     def __dir__(self):
272         # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
273         result = set(dir(self.__class__))
274         result.update(self.__dict__.keys())
275
276         result.update(dir(self._introspection_module))
277         override_exports = getattr(self._overrides_module, '__all__', ())
278         result.update(override_exports)
279         return list(result)
280
281     def __repr__(self):
282         path = repository.get_typelib_path(self._namespace)
283         if _have_py3:
284             # get_typelib_path() delivers bytes, not a string
285             path = path.decode('UTF-8')
286
287         return "<%s.%s %r from %r>" % (self.__class__.__module__,
288                                        self.__class__.__name__,
289                                        self._namespace,
290                                        path)
291
292
293 class DynamicGObjectModule(DynamicModule):
294     """Wrapper for the internal GObject module
295
296     This class allows us to access both the static internal PyGObject module and the GI GObject module
297     through the same interface.  It is returned when by importing GObject from the gi repository:
298
299     from gi.repository import GObject
300
301     We use this because some PyGI interfaces generated from the GIR require GObject types not wrapped
302     by the static bindings.  This also allows access to module attributes in a way that is more
303     familiar to GI application developers.  Take signal flags as an example.  The G_SIGNAL_RUN_FIRST
304     flag would be accessed as GObject.SIGNAL_RUN_FIRST in the static bindings but in the dynamic bindings
305     can be accessed as GObject.SignalFlags.RUN_FIRST.  The latter follows a GI naming convention which
306     would be familiar to GI application developers in a number of languages.
307     """
308
309     def __init__(self):
310         DynamicModule.__init__(self, namespace='GObject')
311
312     def __getattr__(self, name):
313         from . import _gobject
314
315         # first see if this attr is in the internal _gobject module
316         attr = getattr(_gobject, name, None)
317
318         # if not in module assume request for an attr exported through GI
319         if attr is None:
320             attr = super(DynamicGObjectModule, self).__getattr__(name)
321
322         return attr
323
324
325 class DynamicGLibModule(DynamicModule):
326     """Wrapper for the internal GLib module
327
328     This class allows us to access both the static internal PyGLib module and the GI GLib module
329     through the same interface.  It is returned when by importing GLib from the gi repository:
330
331     from gi.repository import GLib
332
333     We use this because some PyGI interfaces generated from the GIR require GLib types not wrapped
334     by the static bindings.  This also allows access to module attributes in a way that is more
335     familiar to GI application developers.
336     """
337
338     def __init__(self):
339         DynamicModule.__init__(self, namespace='GLib')
340
341     def __getattr__(self, name):
342         # first see if this attr is in the internal _gobject module
343         attr = getattr(_glib, name, None)
344
345         # if not in module assume request for an attr exported through GI
346         if attr is None:
347             attr = super(DynamicGLibModule, self).__getattr__(name)
348
349         return attr