1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2007-2009 Johan Dahlin <johan@gnome.org>
6 # module.py: dynamic module for introspected libraries.
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.
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.
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
23 from __future__ import absolute_import
28 _have_py3 = (sys.version_info[0] >= 3)
30 from . import _glib, _gobject
32 maketrans = ''.maketrans
33 except AttributeError:
34 # fallback for Python 2
35 from string import maketrans
38 from .overrides import registry
55 enum_register_new_gtype_and_add, \
57 flags_register_new_gtype_and_add
63 repository = Repository.get_default()
66 def get_parent_for_object(object_info):
67 parent_object_info = object_info.get_parent()
69 if not parent_object_info:
72 namespace = parent_object_info.get_namespace()
73 name = parent_object_info.get_name()
75 # Workaround for GObject.Object and GObject.InitiallyUnowned.
76 if namespace == 'GObject' and name == 'Object' or name == 'InitiallyUnowned':
77 return _gobject.GObject
79 module = __import__('gi.repository.%s' % namespace, fromlist=[name])
80 return getattr(module, name)
83 def get_interfaces_for_object(object_info):
85 for interface_info in object_info.get_interfaces():
86 namespace = interface_info.get_namespace()
87 name = interface_info.get_name()
89 module = __import__('gi.repository.%s' % namespace, fromlist=[name])
90 interfaces.append(getattr(module, name))
94 class IntrospectionModule(object):
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
102 repository.require(self._namespace, self._version)
103 self.__path__ = repository.get_typelib_path(self._namespace)
105 # get_typelib_path() delivers bytes, not a string
106 self.__path__ = self.__path__.decode('UTF-8')
108 if self._version is None:
109 self._version = repository.get_version(self._namespace)
111 def __getattr__(self, name):
112 info = repository.find_by_name(self._namespace, name)
114 raise AttributeError("%r object has no attribute %r" % (
115 self.__name__, name))
117 if isinstance(info, EnumInfo):
118 g_type = info.get_g_type()
119 wrapper = g_type.pytype
123 if g_type.is_a(_gobject.TYPE_FLAGS):
124 wrapper = flags_add(g_type)
126 assert g_type == _gobject.TYPE_NONE
127 wrapper = flags_register_new_gtype_and_add(info)
129 if g_type.is_a(_gobject.TYPE_ENUM):
130 wrapper = enum_add(g_type)
132 assert g_type == _gobject.TYPE_NONE
133 wrapper = enum_register_new_gtype_and_add(info)
135 wrapper.__info__ = info
136 wrapper.__module__ = 'gi.repository.' + info.get_namespace()
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()))
148 if g_type != _gobject.TYPE_NONE:
149 g_type.pytype = wrapper
151 elif isinstance(info, RegisteredTypeInfo):
152 g_type = info.get_g_type()
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_
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):
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):
177 elif (g_type.is_a(_gobject.TYPE_POINTER) or
178 g_type == _gobject.TYPE_NONE or
179 g_type.fundamental == g_type):
182 raise TypeError("unable to create a wrapper for %s.%s" % (info.get_namespace(), info.get_name()))
183 metaclass = StructMeta
185 raise NotImplementedError(info)
187 name = info.get_name()
190 '__module__': 'gi.repository.' + self._namespace,
193 wrapper = metaclass(name, bases, dict_)
195 # Register the new Python wrapper.
196 if g_type != _gobject.TYPE_NONE:
197 g_type.pytype = wrapper
199 elif isinstance(info, FunctionInfo):
200 wrapper = Function(info)
201 elif isinstance(info, ConstantInfo):
202 wrapper = info.get_value()
204 raise NotImplementedError(info)
206 self.__dict__[name] = wrapper
210 path = repository.get_typelib_path(self._namespace)
212 # get_typelib_path() delivers bytes, not a string
213 path = path.decode('UTF-8')
214 return "<IntrospectionModule %r from %r>" % (self._namespace, path)
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())
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
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))
232 class DynamicModule(types.ModuleType):
233 def __init__(self, namespace):
234 self._namespace = namespace
235 self._introspection_module = None
236 self._overrides_module = None
240 version = gi.get_required_version(self._namespace)
241 self._introspection_module = IntrospectionModule(self._namespace,
244 overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
245 self._overrides_module = getattr(overrides_modules, self._namespace, None)
247 self._overrides_module = None
249 self.__path__ = repository.get_typelib_path(self._namespace)
251 # get_typelib_path() delivers bytes, not a string
252 self.__path__ = self.__path__.decode('UTF-8')
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)
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
265 key = '%s.%s' % (self._namespace, name)
269 return getattr(self._introspection_module, name)
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())
276 result.update(dir(self._introspection_module))
277 override_exports = getattr(self._overrides_module, '__all__', ())
278 result.update(override_exports)
282 path = repository.get_typelib_path(self._namespace)
284 # get_typelib_path() delivers bytes, not a string
285 path = path.decode('UTF-8')
287 return "<%s.%s %r from %r>" % (self.__class__.__module__,
288 self.__class__.__name__,
293 class DynamicGObjectModule(DynamicModule):
294 """Wrapper for the internal GObject module
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:
299 from gi.repository import GObject
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.
310 DynamicModule.__init__(self, namespace='GObject')
312 def __getattr__(self, name):
313 from . import _gobject
315 # first see if this attr is in the internal _gobject module
316 attr = getattr(_gobject, name, None)
318 # if not in module assume request for an attr exported through GI
320 attr = super(DynamicGObjectModule, self).__getattr__(name)
325 class DynamicGLibModule(DynamicModule):
326 """Wrapper for the internal GLib module
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:
331 from gi.repository import GLib
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.
339 DynamicModule.__init__(self, namespace='GLib')
341 def __getattr__(self, name):
342 # first see if this attr is in the internal _gobject module
343 attr = getattr(_glib, name, None)
345 # if not in module assume request for an attr exported through GI
347 attr = super(DynamicGLibModule, self).__getattr__(name)