2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Johan Dahlin
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 from . import cgobject
26 from .odict import odict
27 from .ast import (Callback, Enum, Function, Parameter, Property, Return,
28 Sequence, Struct, Type)
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30 GLibInterface, GLibObject, GLibSignal)
33 # Copied from h2defs.py
34 _upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
35 _upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
36 _upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
38 def to_underscores(name):
39 """Converts a typename to the equivalent underscores name.
40 This is used to form the type conversion macros and enum/flag
42 name = _upperstr_pat1.sub(r'\1_\2', name)
43 name = _upperstr_pat2.sub(r'\1_\2', name)
44 name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
47 _libtool_pat = re.compile("dlname='([A-z0-9\.\-\+]+)'\n")
49 def resolve_libtool(libname):
50 data = open(libname).read()
51 filename = _libtool_pat.search(data).groups()[0]
52 libname = os.path.join(os.path.dirname(libname),
57 class GLibTransformer(object):
58 def __init__(self, namespace_name):
59 self._namespace_name = namespace_name
60 self._output_ns = odict()
67 return self._output_ns.values()
69 def load_library(self, libname):
70 if libname.endswith('.la'):
71 libname = resolve_libtool(libname)
72 self._library = ctypes.cdll.LoadLibrary(libname)
74 def parse(self, nodes):
76 self._parse_node(node)
79 for node in self._output_ns.values():
80 # associate GtkButtonClass with GtkButton
81 if isinstance(node, Struct):
82 self._pair_class_struct(node)
84 def register_include(self, filename):
85 if filename.endswith('.gir'):
86 from .girparser import GIRParser
87 parser = GIRParser(filename)
88 elif filename.endswith('.gidl'):
89 from .gidlparser import GIDLParser
90 parser = GIDLParser(filename)
92 raise NotImplementedError(filename)
93 nsname = parser.get_namespace_name()
94 for node in parser.get_nodes():
95 self._type_names[node.type_name] = (nsname, node)
99 def _add_attribute(self, node, replace=False):
100 node_name = node.name
101 if node_name in self._output_ns and not replace:
103 self._output_ns[node_name] = node
105 def _remove_attribute(self, name):
106 del self._output_ns[name]
108 def _get_attribute(self, name):
109 return self._output_ns.get(name)
111 def _register_internal_type(self, type_name, node):
112 self._type_names[type_name] = (None, node)
114 def _strip_namespace_func(self, name):
116 prefix = self._namespace_name.lower() + '_'
118 if name.startswith(prefix):
119 name = orig_name[len(prefix):]
122 def _strip_namespace_object(self, name):
124 prefix = self._namespace_name.lower()
126 if name.startswith(prefix):
127 name = orig_name[len(prefix):]
130 def _resolve_type_name(self, type_name):
131 item = self._type_names.get(type_name)
136 return '%s.%s' % (nsname, item.name)
139 def _resolve_param_type(self, ptype):
140 type_name = ptype.name.replace('*', '')
141 ptype.name = self._resolve_type_name(type_name)
144 def _parse_node(self, node):
145 if isinstance(node, Enum):
146 self._parse_enum(node)
147 elif isinstance(node, Function):
148 self._parse_function(node)
149 elif isinstance(node, Struct):
150 self._parse_struct(node)
151 elif isinstance(node, Callback):
152 self._parse_callback(node)
154 print 'GOBJECT BUILDER: Unhandled node:', node
156 def _parse_enum(self, enum):
157 enum.name = self._strip_namespace_object(enum.name)
158 self._add_attribute(enum)
160 def _parse_function(self, func):
161 if self._parse_get_type_function(func):
163 elif self._parse_constructor(func):
165 elif self._parse_method(func):
168 self._parse_parameters(func.parameters)
169 func.retval.type = self._resolve_param_type(func.retval.type)
171 func.name = self._strip_namespace_func(func.name)
172 self._add_attribute(func)
174 def _parse_parameters(self, parameters):
175 for parameter in parameters:
176 parameter.type = self._resolve_param_type(parameter.type)
178 def _parse_get_type_function(self, func):
179 if self._library is None:
181 # GType *_get_type(void)
183 if not symbol.endswith('_get_type'):
185 if func.retval.type.name != 'GType':
190 func = getattr(self._library, symbol)
191 func.restype = cgobject.GType
194 self._introspect_type(type_id, symbol)
197 def _parse_method(self, func):
198 if not func.parameters:
201 # FIXME: This is hackish, we should preserve the pointer structures
202 # here, so we can find pointers to objects and not just
203 # pointers to anything
204 first_arg = func.parameters[0].type.name
205 if first_arg.count('*') != 1:
208 object_name = first_arg.replace('*', '')
209 return self._parse_method_common(func, object_name, is_method=True)
211 def _parse_constructor(self, func):
212 # FIXME: This is hackish, we should preserve the pointer structures
213 # here, so we can find pointers to objects and not just
214 # pointers to anything
215 rtype = func.retval.type
216 if rtype.name.count('*') != 1:
219 object_name = rtype.name.replace('*', '')
220 return self._parse_method_common(func, object_name, is_method=False)
222 def _parse_method_common(self, func, object_name, is_method):
223 orig_name = object_name
224 if object_name.lower().startswith(self._namespace_name.lower()):
225 object_name = object_name[len(self._namespace_name):]
226 class_ = self._get_attribute(object_name)
227 if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
230 # GtkButton -> gtk_button_, so we can figure out the method name
231 prefix = to_underscores(orig_name).lower() + '_'
232 if not func.name.startswith(prefix):
235 # Strip namespace and object prefix: gtk_window_new -> new
236 func.name = func.name[len(prefix):]
238 class_.methods.append(func)
240 class_.constructors.append(func)
241 self._parse_parameters(func.parameters)
242 func.retval.type = self._resolve_param_type(func.retval.type)
245 def _parse_struct(self, struct):
246 type_name = self._resolve_type_name(struct.name)
247 node = self._output_ns.get(type_name)
249 self._add_attribute(struct, replace=True)
251 node.fields = struct.fields[:]
253 def _parse_callback(self, callback):
254 self._add_attribute(callback)
256 def _pair_class_struct(self, class_node):
257 name = class_node.name
258 if (name.endswith('Class') or
259 name.endswith('Iface')):
261 elif name.endswith('Interface'):
266 node = self._output_ns.get(self._resolve_type_name(name))
267 del self._output_ns[class_node.name]
270 for field in class_node.fields[1:]:
271 node.fields.append(field)
273 def _create_type(self, type_id):
274 type_name = cgobject.type_name(type_id)
275 return Type(type_name)
277 def _introspect_type(self, type_id, symbol):
278 fundamental_type_id = cgobject.type_fundamental(type_id)
279 if (fundamental_type_id == cgobject.TYPE_ENUM or
280 fundamental_type_id == cgobject.TYPE_FLAGS):
281 self._introspect_enum(fundamental_type_id, type_id, symbol)
282 elif fundamental_type_id == cgobject.TYPE_OBJECT:
283 self._introspect_object(type_id, symbol)
284 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
285 self._introspect_interface(type_id, symbol)
286 elif fundamental_type_id == cgobject.TYPE_BOXED:
287 self._introspect_boxed(type_id, symbol)
289 print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
291 def _introspect_enum(self, ftype_id, type_id, symbol):
292 type_class = cgobject.type_class_ref(type_id)
293 if type_class is None:
297 for enum_value in type_class.get_values():
298 members.append(GLibEnumMember(enum_value.value_name,
300 enum_value.value_nick))
302 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
303 type_name = cgobject.type_name(type_id)
304 node = klass(self._strip_namespace_object(type_name),
305 members, type_name, symbol)
306 self._add_attribute(node, replace=True)
307 self._register_internal_type(type_name, node)
309 def _introspect_object(self, type_id, symbol):
310 type_name = cgobject.type_name(type_id)
311 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
312 node = GLibObject(self._strip_namespace_object(type_name),
313 self._resolve_type_name(parent_type_name),
315 self._introspect_properties(node, type_id)
316 self._introspect_signals(node, type_id)
317 self._add_attribute(node)
318 self._remove_attribute(type_name)
319 self._register_internal_type(type_name, node)
321 def _introspect_interface(self, type_id, symbol):
322 type_name = cgobject.type_name(type_id)
323 node = GLibInterface(self._strip_namespace_object(type_name),
325 self._introspect_properties(node, type_id)
326 self._introspect_signals(node, type_id)
327 self._add_attribute(node)
328 self._register_internal_type(type_name, node)
330 def _introspect_boxed(self, type_id, symbol):
331 type_name = cgobject.type_name(type_id)
332 node = GLibBoxed(self._strip_namespace_object(type_name),
334 self._add_attribute(node)
335 self._remove_attribute(type_name)
336 self._register_internal_type(type_name, node)
338 def _introspect_properties(self, node, type_id):
339 fundamental_type_id = cgobject.type_fundamental(type_id)
340 if fundamental_type_id == cgobject.TYPE_OBJECT:
341 pspecs = cgobject.object_class_list_properties(type_id)
342 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
343 pspecs = cgobject.object_interface_list_properties(type_id)
348 if pspec.owner_type != type_id:
350 node.properties.append(Property(
352 cgobject.type_name(pspec.value_type)))
354 def _introspect_signals(self, node, type_id):
355 for signal_info in cgobject.signal_list(type_id):
356 return_ = Return(cgobject.type_name(signal_info.return_type))
357 signal = GLibSignal(signal_info.signal_name, return_)
358 for i, parameter in enumerate(signal_info.get_params()):
362 name = 'p%s' % (i-1,)
363 ptype = self._create_type(parameter)
364 param = Parameter(name, ptype)
365 signal.parameters.append(param)
366 node.signals.append(signal)