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
22 from ctypes.util import find_library
24 from . import cgobject
25 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
26 Sequence, Property, Return, Struct, Type, Alias,
27 Union, type_name_from_ctype)
28 from .transformer import Names
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30 GLibInterface, GLibObject, GLibSignal, type_names)
31 from .utils import extract_libtool, to_underscores
34 class Unresolved(object):
36 def __init__(self, target):
40 class UnknownTypeError(Exception):
44 class GLibTransformer(object):
46 def __init__(self, transformer, noclosure=False):
47 self._transformer = transformer
48 self._namespace_name = None
50 self._uscore_type_names = {}
52 self._failed_types = {}
53 self._noclosure = noclosure
54 self._validating = False
58 def add_library(self, libname):
60 if libname.endswith('.la'):
61 found_libname = extract_libtool(libname)
63 found_libname = find_library(libname)
65 raise ValueError("Failed to find library: %r" % (libname, ))
66 self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
69 namespace = self._transformer.parse()
70 self._namespace_name = namespace.name
73 for node in namespace.nodes:
74 self._parse_node(node)
76 # Introspection is done from within parsing
78 # Second pass, delete class structures, resolve
79 # all types we now know about
80 nodes = list(self._names.names.itervalues())
81 for (ns, node) in nodes:
82 self._resolve_node(node)
83 # associate GtkButtonClass with GtkButton
84 if isinstance(node, Struct):
85 self._pair_class_struct(node)
86 for (ns, alias) in self._names.aliases.itervalues():
87 self._resolve_alias(alias)
89 # Third pass; ensure all types are known
90 if not self._noclosure:
93 # Create a new namespace with what we found
94 namespace = Namespace(namespace.name)
95 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
96 for (ns, x) in self._names.names.itervalues():
97 namespace.nodes.append(x)
102 def _add_attribute(self, node, replace=False):
103 node_name = node.name
104 if (not replace) and node_name in self._names.names:
106 self._names.names[node_name] = (None, node)
108 def _remove_attribute(self, name):
109 del self._names.names[name]
111 def _get_attribute(self, name):
112 node = self._names.names.get(name)
117 def _register_internal_type(self, type_name, node):
118 self._names.type_names[type_name] = (None, node)
119 self._uscore_type_names[to_underscores(type_name).lower()] = node
120 # Besides the straight underscore conversion, we also try
121 # removing the underscores from the namespace as a possible C
122 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
123 suffix = self._transformer.strip_namespace_object(type_name)
124 prefix = type_name[:-len(suffix)]
125 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
126 self._uscore_type_names[no_uscore_prefixed] = node
130 def _create_type(self, type_id):
131 ctype = cgobject.type_name(type_id)
132 type_name = type_name_from_ctype(ctype)
133 type_name = type_name.replace('*', '')
134 type_name = self._resolve_type_name(type_name)
135 return Type(type_name, ctype)
137 def _resolve_gtypename(self, gtype_name):
139 return self._transformer.gtypename_to_giname(gtype_name,
142 return Unresolved(gtype_name)
144 def _create_gobject(self, node):
145 type_name = 'G' + node.name
146 if type_name == 'GObject':
150 type_id = cgobject.type_from_name(type_name)
151 parent_type_name = cgobject.type_name(
152 cgobject.type_parent(type_id))
153 parent_gitype = self._resolve_gtypename(parent_type_name)
154 symbol = to_underscores(type_name).lower() + '_get_type'
155 node = GLibObject(node.name, parent_gitype, type_name, symbol)
156 type_id = cgobject.TYPE_OBJECT
157 self._introspect_properties(node, type_id)
158 self._introspect_signals(node, type_id)
159 self._add_attribute(node)
160 self._register_internal_type(type_name, node)
164 def _parse_node(self, node):
165 if isinstance(node, Enum):
166 self._parse_enum(node)
167 elif isinstance(node, Function):
168 self._parse_function(node)
169 elif isinstance(node, Struct):
170 self._parse_struct(node)
171 elif isinstance(node, Callback):
172 self._parse_callback(node)
173 elif isinstance(node, Alias):
174 self._parse_alias(node)
175 elif isinstance(node, Member):
176 # FIXME: atk_misc_instance singletons
178 elif isinstance(node, Union):
179 self._parse_union(node)
181 print 'GLIB Transformer: Unhandled node:', node
183 def _parse_alias(self, alias):
184 self._names.aliases[alias.name] = (None, alias)
186 def _parse_enum(self, enum):
187 self._add_attribute(enum)
189 def _parse_function(self, func):
190 if self._parse_get_type_function(func):
193 self._add_attribute(func)
195 def _parse_get_type_function(self, func):
197 if not symbol.endswith('_get_type'):
199 # GType *_get_type(void)
200 # This is a bit fishy, why do we need all these aliases?
201 if func.retval.type.name not in ['Type',
206 print ("Warning: *_get_type function returns '%r'"
207 ", not GObject.Type") % (func.retval.type.name, )
212 if not self._libraries:
213 print "Warning: No libraries loaded, cannot call %s" % (symbol, )
216 for library in self._libraries:
218 func = getattr(library, symbol)
220 except AttributeError:
223 print 'Warning: could not find symbol: %s' % symbol
224 name = symbol.replace('_get_type', '')
225 self._failed_types[name] = True
228 func.restype = cgobject.GType
231 self._introspect_type(type_id, symbol)
234 def _name_is_internal_gtype(self, giname):
236 node = self._get_attribute(giname)
237 return isinstance(node, (GLibObject, GLibInterface, GLibBoxed,
238 GLibEnum, GLibFlags))
242 def _parse_method(self, func):
243 if not func.parameters:
245 return self._parse_method_common(func, True)
247 def _parse_constructor(self, func):
248 return self._parse_method_common(func, False)
250 def _parse_method_common(self, func, is_method):
252 target_arg = func.retval
254 target_arg = func.parameters[0]
255 target_arg.type = self._resolve_param_type(target_arg.type)
258 # Methods require their first arg to be a known class
259 # Look at the original C type (before namespace stripping), without
260 # pointers: GtkButton -> gtk_button_, so we can figure out the
262 argtype = target_arg.type.ctype.replace('*', '')
263 name = self._transformer.strip_namespace_object(argtype)
264 name_uscore = to_underscores(name).lower()
265 name_offset = func.symbol.find(name_uscore)
268 prefix = func.symbol[:name_offset+len(name_uscore)]
270 # Constructors must have _new
271 # Take everything before that as class name
272 new_idx = func.symbol.find('_new')
275 # Constructors don't return basic types
276 if target_arg.type.name in type_names:
278 prefix = func.symbol[:new_idx]
281 for key in self._uscore_type_names:
283 if key.startswith(prefix):
284 klass = self._uscore_type_names.get(key)
285 if (klass is not None and
286 isinstance(klass, (GLibObject, GLibBoxed,
294 # Interfaces can't have constructors, punt to global scope
295 if isinstance(klass, GLibInterface):
297 # TODO - check that the return type is a subclass of the
298 # class from the prefix
300 self._remove_attribute(func.name)
301 # Strip namespace and object prefix: gtk_window_new -> new
302 func.name = func.symbol[len(prefix)+1:]
304 klass.methods.append(func)
306 klass.constructors.append(func)
309 def _parse_struct(self, struct):
310 # This is a hack, but GObject is a rather fundamental piece so.
311 internal_names = ["Object", 'InitiallyUnowned']
312 g_internal_names = ["G" + x for x in internal_names]
313 if (self._namespace_name == 'GObject' and
314 struct.name in internal_names):
315 self._create_gobject(struct)
317 elif struct.name in g_internal_names:
320 node = self._names.names.get(struct.name)
322 self._add_attribute(struct, replace=True)
325 node.fields = struct.fields[:]
327 def _parse_union(self, union):
328 node = self._names.names.get(union.name)
330 self._add_attribute(union, replace=True)
333 node.fields = union.fields[:]
335 def _parse_callback(self, callback):
336 self._add_attribute(callback)
338 def _strip_class_suffix(self, name):
339 if (name.endswith('Class') or
340 name.endswith('Iface')):
342 elif name.endswith('Interface'):
347 def _arg_is_failed(self, param):
348 ctype = self._transformer.ctype_of(param).replace('*', '')
349 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
350 if uscored in self._failed_types:
351 print "Warning: failed type: %r" % (param, )
355 def _pair_class_struct(self, maybe_class):
356 name = self._strip_class_suffix(maybe_class.name)
357 if name == maybe_class.name:
360 if self._arg_is_failed(maybe_class):
361 print "WARNING: deleting no-type %r" % (maybe_class.name, )
362 del self._names.names[maybe_class.name]
365 name = self._resolve_type_name(name)
366 resolved = self._transformer.strip_namespace_object(name)
367 pair_class = self._get_attribute(resolved)
368 if pair_class and isinstance(pair_class,
369 (GLibObject, GLibBoxed, GLibInterface)):
370 for field in maybe_class.fields[1:]:
371 pair_class.fields.append(field)
373 name = self._transformer.strip_namespace_object(maybe_class.name)
374 pair_class = self._get_attribute(name)
375 if pair_class and isinstance(pair_class,
376 (GLibObject, GLibBoxed, GLibInterface)):
378 del self._names.names[maybe_class.name]
382 def _introspect_type(self, type_id, symbol):
383 fundamental_type_id = cgobject.type_fundamental(type_id)
384 if (fundamental_type_id == cgobject.TYPE_ENUM or
385 fundamental_type_id == cgobject.TYPE_FLAGS):
386 self._introspect_enum(fundamental_type_id, type_id, symbol)
387 elif fundamental_type_id == cgobject.TYPE_OBJECT:
388 self._introspect_object(type_id, symbol)
389 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
390 self._introspect_interface(type_id, symbol)
391 elif fundamental_type_id == cgobject.TYPE_BOXED:
392 self._introspect_boxed(type_id, symbol)
393 elif fundamental_type_id == cgobject.TYPE_BOXED:
394 self._introspect_boxed(type_id, symbol)
395 elif fundamental_type_id == cgobject.TYPE_POINTER:
396 # FIXME: Should we do something about these?
397 # GHashTable, GValue and a few other fundamentals are
401 print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
404 def _introspect_enum(self, ftype_id, type_id, symbol):
405 type_class = cgobject.type_class_ref(type_id)
406 if type_class is None:
410 for enum_value in type_class.get_values():
411 members.append(GLibEnumMember(enum_value.value_nick,
413 enum_value.value_name,
414 enum_value.value_nick))
416 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
417 type_name = cgobject.type_name(type_id)
418 enum_name = self._transformer.strip_namespace_object(type_name)
419 node = klass(enum_name, type_name, members, symbol)
420 self._add_attribute(node, replace=True)
421 self._register_internal_type(type_name, node)
423 def _introspect_object(self, type_id, symbol):
424 type_name = cgobject.type_name(type_id)
425 # We handle this specially above; in 2.16 and below there
426 # was no g_object_get_type, for later versions we need
428 if type_name == 'GObject':
430 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
431 parent_gitype = self._resolve_gtypename(parent_type_name)
433 self._transformer.strip_namespace_object(type_name),
436 self._introspect_properties(node, type_id)
437 self._introspect_signals(node, type_id)
438 self._add_attribute(node, replace=True)
439 self._register_internal_type(type_name, node)
441 def _introspect_interface(self, type_id, symbol):
442 type_name = cgobject.type_name(type_id)
443 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
444 if parent_type_name == 'GInterface':
447 parent_gitype = self._resolve_gtypename(parent_type_name)
448 node = GLibInterface(
449 self._transformer.strip_namespace_object(type_name),
452 self._introspect_properties(node, type_id)
453 self._introspect_signals(node, type_id)
454 self._add_attribute(node, replace=True)
455 self._register_internal_type(type_name, node)
457 def _introspect_boxed(self, type_id, symbol):
458 type_name = cgobject.type_name(type_id)
459 node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
461 self._add_attribute(node, replace=True)
462 self._register_internal_type(type_name, node)
464 def _introspect_properties(self, node, type_id):
465 fundamental_type_id = cgobject.type_fundamental(type_id)
466 if fundamental_type_id == cgobject.TYPE_OBJECT:
467 pspecs = cgobject.object_class_list_properties(type_id)
468 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
469 pspecs = cgobject.object_interface_list_properties(type_id)
474 if pspec.owner_type != type_id:
476 ctype = cgobject.type_name(pspec.value_type)
477 node.properties.append(Property(
479 type_name_from_ctype(ctype),
483 def _introspect_signals(self, node, type_id):
484 for signal_info in cgobject.signal_list(type_id):
485 rtype = self._create_type(signal_info.return_type)
486 return_ = Return(rtype)
487 signal = GLibSignal(signal_info.signal_name, return_)
488 for i, parameter in enumerate(signal_info.get_params()):
492 name = 'p%s' % (i-1, )
493 ptype = self._create_type(parameter)
494 param = Parameter(name, ptype)
495 signal.parameters.append(param)
496 node.signals.append(signal)
500 def _resolve_type_name(self, type_name, ctype=None):
501 # Workaround glib bug #548689, to be included in 2.18.0
502 if type_name == "GParam":
503 type_name = "GObject.ParamSpec"
505 res = self._transformer.resolve_type_name_full
507 return res(type_name, ctype, self._names)
509 return self._transformer.resolve_type_name(type_name, ctype)
511 def _validate_type_name(self, name):
512 if name in type_names:
514 if name.find('.') >= 0:
516 if name in self._names.aliases:
518 if name in self._names.names:
522 def _validate_type(self, ptype):
523 if isinstance(ptype, Sequence):
524 etype = ptype.element_type
525 if isinstance(etype, Sequence):
526 return self._validate_type(etype)
527 return self._validate_type_name(etype)
528 return self._validate_type_name(ptype.name)
530 def _resolve_param_type_validate(self, ptype):
531 ptype = self._resolve_param_type(ptype)
532 if self._validating and not self._validate_type(ptype):
533 raise UnknownTypeError("Unknown type %r" % (ptype, ))
536 def _resolve_param_type(self, ptype):
538 return self._transformer.resolve_param_type_full(ptype,
541 return self._transformer.resolve_param_type(ptype)
544 def _resolve_node(self, node):
545 if isinstance(node, Function):
546 self._resolve_function_toplevel(node)
548 elif isinstance(node, Callback):
549 self._resolve_function(node)
550 elif isinstance(node, GLibObject):
551 self._resolve_glib_object(node)
552 elif isinstance(node, GLibInterface):
553 self._resolve_glib_interface(node)
554 elif isinstance(node, GLibBoxed):
555 self._resolve_glib_boxed(node)
556 elif isinstance(node, Struct):
557 self._resolve_struct(node)
558 elif isinstance(node, Union):
559 self._resolve_union(node)
560 elif isinstance(node, Alias):
561 self._resolve_alias(node)
563 def _resolve_function_toplevel(self, func):
564 newfunc = self._parse_constructor(func)
566 newfunc = self._parse_method(func)
568 self._resolve_function(func)
570 self._resolve_function(newfunc)
572 def _resolve_struct(self, node):
573 for field in node.fields:
574 self._resolve_field(field)
576 def _resolve_union(self, node):
577 for field in node.fields:
578 self._resolve_field(field)
580 def _resolve_parent(self, node):
581 if not isinstance(node, (GLibInterface, GLibObject)):
583 if isinstance(node.parent, Unresolved):
585 self._transformer.gtypename_to_giname(node.parent.target,
588 def _resolve_glib_interface(self, node):
589 self._resolve_parent(node)
590 self._resolve_methods(node.methods)
591 self._resolve_properties(node.properties)
592 self._resolve_signals(node.signals)
594 def _resolve_glib_object(self, node):
595 self._resolve_parent(node)
596 self._resolve_constructors(node.constructors)
597 self._resolve_methods(node.methods)
598 self._resolve_properties(node.properties)
599 self._resolve_signals(node.signals)
601 def _resolve_glib_boxed(self, node):
602 self._resolve_constructors(node.constructors)
603 self._resolve_methods(node.methods)
605 def _resolve_constructors(self, constructors):
606 for ctor in constructors:
607 self._resolve_function(ctor)
609 def _resolve_methods(self, methods):
610 for method in methods:
611 self._resolve_function(method)
613 def _resolve_signals(self, signals):
614 for signal in signals:
615 self._resolve_function(signal)
617 def _resolve_properties(self, properties):
618 for prop in properties:
619 self._resolve_property(prop)
621 def _resolve_property(self, prop):
622 prop.type = self._resolve_param_type(prop.type)
624 def _resolve_function(self, func):
625 self._resolve_parameters(func.parameters)
626 func.retval.type = self._resolve_param_type(func.retval.type)
628 def _resolve_parameters(self, parameters):
629 for parameter in parameters:
630 parameter.type = self._resolve_param_type(parameter.type)
632 def _resolve_field(self, field):
633 if isinstance(field, Callback):
634 self._resolve_function(field)
636 field.type = self._resolve_param_type(field.type)
638 def _resolve_alias(self, alias):
639 alias.target = self._resolve_type_name(alias.target, alias.target)
643 def _validate(self, nodes):
644 nodes = list(self._names.names.itervalues())
646 self._validating = True
649 print "Type resolution; pass=%d (%d nodes)" % (i, initlen)
650 nodes = list(self._names.names.itervalues())
653 self._resolve_node(node)
654 except UnknownTypeError, e:
655 print "WARNING: %s: Deleting %r" % (e, node)
656 self._remove_attribute(node.name)
657 if len(nodes) == initlen:
660 self._validating = False