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