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)
31 maketrans
= ''.maketrans
32 except AttributeError:
33 # fallback for Python 2
34 from string
import maketrans
53 enum_register_new_gtype_and_add
, \
55 flags_register_new_gtype_and_add
, \
61 from ._constants
import \
69 repository
= Repository
.get_default()
71 # Cache of IntrospectionModules that have been loaded.
72 _introspection_modules
= {}
75 def get_parent_for_object(object_info
):
76 parent_object_info
= object_info
.get_parent()
78 if not parent_object_info
:
79 # If we reach the end of the introspection info class hierarchy, look
80 # for an existing wrapper on the GType and use it as a base for the
81 # new introspection wrapper. This allows static C wrappers already
82 # registered with the GType to be used as the introspection base
83 # (_gi.GObject for example)
84 gtype
= object_info
.get_g_type()
85 if gtype
and gtype
.pytype
:
88 # Otherwise use builtins.object as the base
91 namespace
= parent_object_info
.get_namespace()
92 name
= parent_object_info
.get_name()
94 module
= importlib
.import_module('gi.repository.' + namespace
)
95 return getattr(module
, name
)
98 def get_interfaces_for_object(object_info
):
100 for interface_info
in object_info
.get_interfaces():
101 namespace
= interface_info
.get_namespace()
102 name
= interface_info
.get_name()
104 module
= importlib
.import_module('gi.repository.' + namespace
)
105 interfaces
.append(getattr(module
, name
))
109 class IntrospectionModule(object):
110 """An object which wraps an introspection typelib.
112 This wrapping creates a python module like representation of the typelib
113 using gi repository as a foundation. Accessing attributes of the module
114 will dynamically pull them in and create wrappers for the members.
115 These members are then cached on this introspection module.
117 def __init__(self
, namespace
, version
=None):
118 """Might raise gi._gi.RepositoryError"""
120 repository
.require(namespace
, version
)
121 self
._namespace
= namespace
122 self
._version
= version
123 self
.__name
__ = 'gi.repository.' + namespace
125 self
.__path
__ = repository
.get_typelib_path(self
._namespace
)
127 # get_typelib_path() delivers bytes, not a string
128 self
.__path
__ = self
.__path
__.decode('UTF-8')
130 if self
._version
is None:
131 self
._version
= repository
.get_version(self
._namespace
)
133 def __getattr__(self
, name
):
134 info
= repository
.find_by_name(self
._namespace
, name
)
136 raise AttributeError("%r object has no attribute %r" % (
137 self
.__name
__, name
))
139 if isinstance(info
, EnumInfo
):
140 g_type
= info
.get_g_type()
141 wrapper
= g_type
.pytype
145 if g_type
.is_a(TYPE_FLAGS
):
146 wrapper
= flags_add(g_type
)
148 assert g_type
== TYPE_NONE
149 wrapper
= flags_register_new_gtype_and_add(info
)
151 if g_type
.is_a(TYPE_ENUM
):
152 wrapper
= enum_add(g_type
)
154 assert g_type
== TYPE_NONE
155 wrapper
= enum_register_new_gtype_and_add(info
)
157 wrapper
.__info
__ = info
158 wrapper
.__module
__ = 'gi.repository.' + info
.get_namespace()
160 # Don't use upper() here to avoid locale specific
161 # identifier conversion (e. g. in Turkish 'i'.upper() == 'i')
162 # see https://bugzilla.gnome.org/show_bug.cgi?id=649165
163 ascii_upper_trans
= maketrans(
164 'abcdefgjhijklmnopqrstuvwxyz',
165 'ABCDEFGJHIJKLMNOPQRSTUVWXYZ')
166 for value_info
in info
.get_values():
167 value_name
= value_info
.get_name_unescaped().translate(ascii_upper_trans
)
168 setattr(wrapper
, value_name
, wrapper(value_info
.get_value()))
169 for method_info
in info
.get_methods():
170 setattr(wrapper
, method_info
.__name
__, method_info
)
172 if g_type
!= TYPE_NONE
:
173 g_type
.pytype
= wrapper
175 elif isinstance(info
, RegisteredTypeInfo
):
176 g_type
= info
.get_g_type()
179 if isinstance(info
, ObjectInfo
):
180 parent
= get_parent_for_object(info
)
181 interfaces
= tuple(interface
for interface
in get_interfaces_for_object(info
)
182 if not issubclass(parent
, interface
))
183 bases
= (parent
,) + interfaces
184 metaclass
= GObjectMeta
185 elif isinstance(info
, CallbackInfo
):
187 metaclass
= GObjectMeta
188 elif isinstance(info
, InterfaceInfo
):
189 bases
= (GInterface
,)
190 metaclass
= GObjectMeta
191 elif isinstance(info
, (StructInfo
, UnionInfo
)):
192 if g_type
.is_a(TYPE_BOXED
):
194 elif (g_type
.is_a(TYPE_POINTER
) or
195 g_type
== TYPE_NONE
or
196 g_type
.fundamental
== g_type
):
199 raise TypeError("unable to create a wrapper for %s.%s" % (info
.get_namespace(), info
.get_name()))
200 metaclass
= StructMeta
202 raise NotImplementedError(info
)
204 # Check if there is already a Python wrapper that is not a parent class
205 # of the wrapper being created. If it is a parent, it is ok to clobber
206 # g_type.pytype with a new child class wrapper of the existing parent.
207 # Note that the return here never occurs under normal circumstances due
208 # to caching on the __dict__ itself.
209 if g_type
!= TYPE_NONE
:
210 type_
= g_type
.pytype
211 if type_
is not None and type_
not in bases
:
212 self
.__dict
__[name
] = type_
217 '__module__': 'gi.repository.' + self
._namespace
,
220 wrapper
= metaclass(name
, bases
, dict_
)
222 # Register the new Python wrapper.
223 if g_type
!= TYPE_NONE
:
224 g_type
.pytype
= wrapper
226 elif isinstance(info
, FunctionInfo
):
228 elif isinstance(info
, ConstantInfo
):
229 wrapper
= info
.get_value()
231 raise NotImplementedError(info
)
233 # Cache the newly created wrapper which will then be
234 # available directly on this introspection module instead of being
235 # lazily constructed through the __getattr__ we are currently in.
236 self
.__dict
__[name
] = wrapper
240 path
= repository
.get_typelib_path(self
._namespace
)
242 # get_typelib_path() delivers bytes, not a string
243 path
= path
.decode('UTF-8')
244 return "<IntrospectionModule %r from %r>" % (self
._namespace
, path
)
247 # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
248 result
= set(dir(self
.__class
__))
249 result
.update(self
.__dict
__.keys())
251 # update *set* because some repository attributes have already been
252 # wrapped by __getattr__() and included in self.__dict__; but skip
253 # Callback types, as these are not real objects which we can actually
255 namespace_infos
= repository
.get_infos(self
._namespace
)
256 result
.update(info
.get_name() for info
in namespace_infos
if
257 not isinstance(info
, CallbackInfo
))
262 def get_introspection_module(namespace
):
265 An object directly wrapping the gi module without overrides.
267 Might raise gi._gi.RepositoryError
269 if namespace
in _introspection_modules
:
270 return _introspection_modules
[namespace
]
272 version
= gi
.get_required_version(namespace
)
273 module
= IntrospectionModule(namespace
, version
)
274 _introspection_modules
[namespace
] = module