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
30 from .overrides import registry
45 enum_register_new_gtype_and_add, \
47 flags_register_new_gtype_and_add
53 repository = Repository.get_default()
55 def get_parent_for_object(object_info):
56 parent_object_info = object_info.get_parent()
58 if not parent_object_info:
61 namespace = parent_object_info.get_namespace()
62 name = parent_object_info.get_name()
64 # Workaround for GObject.Object and GObject.InitiallyUnowned.
65 if namespace == 'GObject' and name == 'Object' or name == 'InitiallyUnowned':
66 return gobject.GObject
68 module = __import__('gi.repository.%s' % namespace, fromlist=[name])
69 return getattr(module, name)
72 def get_interfaces_for_object(object_info):
74 for interface_info in object_info.get_interfaces():
75 namespace = interface_info.get_namespace()
76 name = interface_info.get_name()
78 module = __import__('gi.repository.%s' % namespace, fromlist=[name])
79 interfaces.append(getattr(module, name))
83 class IntrospectionModule(object):
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
91 repository.require(self._namespace, self._version)
92 self.__path__ = repository.get_typelib_path(self._namespace)
94 if self._version is None:
95 self._version = repository.get_version(self._namespace)
97 def __getattr__(self, name):
98 info = repository.find_by_name(self._namespace, name)
100 raise AttributeError("%r object has no attribute %r" % (
101 self.__name__, name))
103 if isinstance(info, EnumInfo):
104 g_type = info.get_g_type()
105 wrapper = g_type.pytype
109 if g_type.is_a(gobject.TYPE_FLAGS):
110 wrapper = flags_add(g_type)
112 assert g_type == gobject.TYPE_NONE
113 wrapper = flags_register_new_gtype_and_add(info)
115 if g_type.is_a(gobject.TYPE_ENUM):
116 wrapper = enum_add(g_type)
118 assert g_type == gobject.TYPE_NONE
119 wrapper = enum_register_new_gtype_and_add(info)
121 wrapper.__info__ = info
122 wrapper.__module__ = 'gi.repository.' + info.get_namespace()
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()))
134 if g_type != gobject.TYPE_NONE:
135 g_type.pytype = wrapper
137 elif isinstance(info, RegisteredTypeInfo):
138 g_type = info.get_g_type()
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_
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):
160 elif g_type.is_a(gobject.TYPE_POINTER) or \
161 g_type == gobject.TYPE_NONE or \
162 g_type.fundamental == g_type:
165 raise TypeError("unable to create a wrapper for %s.%s" % (info.get_namespace(), info.get_name()))
166 metaclass = StructMeta
168 raise NotImplementedError(info)
170 name = info.get_name()
173 '__module__': 'gi.repository.' + self._namespace,
176 wrapper = metaclass(name, bases, dict_)
178 # Register the new Python wrapper.
179 if g_type != gobject.TYPE_NONE:
180 g_type.pytype = wrapper
182 elif isinstance(info, FunctionInfo):
183 wrapper = Function(info)
184 elif isinstance(info, ConstantInfo):
185 wrapper = info.get_value()
187 raise NotImplementedError(info)
189 self.__dict__[name] = wrapper
193 path = repository.get_typelib_path(self._namespace)
194 return "<IntrospectionModule %r from %r>" % (self._namespace, path)
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())
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)
209 class DynamicGObjectModule(IntrospectionModule):
210 """Wrapper for the GObject module
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:
215 from gi.repository import GObject
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.
226 IntrospectionModule.__init__(self, namespace='GObject')
228 def __getattr__(self, name):
229 # first see if this attr is in the gobject module
230 attr = getattr(gobject, name, None)
232 # if not in module assume request for an attr exported through GI
234 attr = super(DynamicGObjectModule, self).__getattr__(name)
239 class DynamicModule(object):
240 def __init__(self, namespace):
241 self._namespace = namespace
242 self._introspection_module = None
243 self._overrides_module = None
247 version = gi.get_required_version(self._namespace)
248 self._introspection_module = IntrospectionModule(self._namespace,
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)
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)
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
266 key = '%s.%s' % (self._namespace, name)
270 return getattr(self._introspection_module, name)
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())
277 result.update(dir(self._introspection_module))
278 override_exports = getattr(self._overrides_module, '__all__', ())
279 result.update(override_exports)
283 path = repository.get_typelib_path(self._namespace)
284 return "<%s.%s %r from %r>" % (self.__class__.__module__,
285 self.__class__.__name__,