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._private_internal_types = {}
54 self._noclosure = noclosure
55 self._validating = False
59 def add_library(self, libname):
61 if libname.endswith('.la'):
62 found_libname = extract_libtool(libname)
64 found_libname = find_library(libname)
66 raise ValueError("Failed to find library: %r" % (libname, ))
67 self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
69 def _print_statistics(self):
70 nodes = list(self._names.names.itervalues())
72 def count_type(otype):
73 return len([x for x in nodes
74 if isinstance(x[1], otype)])
75 objectcount = count_type(GLibObject)
76 ifacecount = count_type(GLibInterface)
77 enumcount = count_type(GLibEnum)
78 boxedcount = count_type(GLibBoxed)
79 print " %d nodes; %d objects, %d interfaces, %d enums, %d boxed" \
80 % (len(nodes), objectcount, ifacecount, enumcount, boxedcount)
83 namespace = self._transformer.parse()
84 self._namespace_name = namespace.name
87 for node in namespace.nodes:
88 self._parse_node(node)
90 # Introspection is done from within parsing
92 # Second pass, delete class structures, resolve
93 # all types we now know about
94 nodes = list(self._names.names.itervalues())
95 for (ns, node) in nodes:
97 self._resolve_node(node)
99 print "WARNING: DELETING node %s: %s" % (node.name, e)
100 self._remove_attribute(node.name)
101 # associate GtkButtonClass with GtkButton
102 if isinstance(node, Struct):
103 self._pair_class_struct(node)
104 for (ns, alias) in self._names.aliases.itervalues():
105 self._resolve_alias(alias)
107 self._print_statistics()
108 # Third pass; ensure all types are known
109 if not self._noclosure:
110 self._validate(nodes)
112 # Create a new namespace with what we found
113 namespace = Namespace(namespace.name)
114 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
115 for (ns, x) in nodes:
116 namespace.nodes.append(x)
121 def _add_attribute(self, node, replace=False):
122 node_name = node.name
123 if (not replace) and node_name in self._names.names:
125 self._names.names[node_name] = (None, node)
127 def _remove_attribute(self, name):
128 del self._names.names[name]
130 def _get_attribute(self, name):
131 node = self._names.names.get(name)
136 def _register_internal_type(self, type_name, node):
137 self._names.type_names[type_name] = (None, node)
138 self._uscore_type_names[to_underscores(type_name).lower()] = node
139 # Besides the straight underscore conversion, we also try
140 # removing the underscores from the namespace as a possible C
141 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
142 suffix = self._transformer.strip_namespace_object(type_name)
143 prefix = type_name[:-len(suffix)]
144 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
145 self._uscore_type_names[no_uscore_prefixed] = node
149 def _create_type(self, type_id):
150 ctype = cgobject.type_name(type_id)
151 type_name = type_name_from_ctype(ctype)
152 type_name = type_name.replace('*', '')
153 type_name = self._resolve_type_name(type_name)
154 return Type(type_name, ctype)
156 def _resolve_gtypename(self, gtype_name):
158 return self._transformer.gtypename_to_giname(gtype_name,
161 return Unresolved(gtype_name)
163 def _create_gobject(self, node):
164 type_name = 'G' + node.name
165 if type_name == 'GObject':
169 type_id = cgobject.type_from_name(type_name)
170 parent_type_name = cgobject.type_name(
171 cgobject.type_parent(type_id))
172 parent_gitype = self._resolve_gtypename(parent_type_name)
173 symbol = to_underscores(type_name).lower() + '_get_type'
174 node = GLibObject(node.name, parent_gitype, type_name, symbol)
175 type_id = cgobject.TYPE_OBJECT
176 self._introspect_properties(node, type_id)
177 self._introspect_signals(node, type_id)
178 self._add_attribute(node)
179 self._register_internal_type(type_name, node)
183 def _parse_node(self, node):
184 if isinstance(node, Enum):
185 self._parse_enum(node)
186 elif isinstance(node, Function):
187 self._parse_function(node)
188 elif isinstance(node, Struct):
189 self._parse_struct(node)
190 elif isinstance(node, Callback):
191 self._parse_callback(node)
192 elif isinstance(node, Alias):
193 self._parse_alias(node)
194 elif isinstance(node, Member):
195 # FIXME: atk_misc_instance singletons
197 elif isinstance(node, Union):
198 self._parse_union(node)
200 print 'GLIB Transformer: Unhandled node:', node
202 def _parse_alias(self, alias):
203 self._names.aliases[alias.name] = (None, alias)
205 def _parse_enum(self, enum):
206 self._add_attribute(enum)
208 def _parse_function(self, func):
209 if self._parse_get_type_function(func):
212 self._add_attribute(func)
214 def _parse_get_type_function(self, func):
216 if not symbol.endswith('_get_type'):
218 # GType *_get_type(void)
219 # This is a bit fishy, why do we need all these aliases?
220 if func.retval.type.name not in ['Type',
225 print ("Warning: *_get_type function returns '%r'"
226 ", not GObject.Type") % (func.retval.type.name, )
231 if not self._libraries:
232 print "Warning: No libraries loaded, cannot call %s" % (symbol, )
235 for library in self._libraries:
237 func = getattr(library, symbol)
239 except AttributeError:
242 print 'Warning: could not find symbol: %s' % symbol
243 name = symbol.replace('_get_type', '')
244 self._failed_types[name] = True
247 func.restype = cgobject.GType
250 self._introspect_type(type_id, symbol)
253 def _name_is_internal_gtype(self, giname):
255 node = self._get_attribute(giname)
256 return isinstance(node, (GLibObject, GLibInterface, GLibBoxed,
257 GLibEnum, GLibFlags))
261 def _parse_method(self, func):
262 if not func.parameters:
264 return self._parse_method_common(func, True)
266 def _parse_constructor(self, func):
267 return self._parse_method_common(func, False)
269 def _parse_method_common(self, func, is_method):
270 # Skip _get_type functions, we processed them
272 if func.symbol.endswith('_get_type'):
276 target_arg = func.retval
278 target_arg = func.parameters[0]
279 target_arg.type = self._resolve_param_type(target_arg.type)
282 # Methods require their first arg to be a known class
283 # Look at the original C type (before namespace stripping), without
284 # pointers: GtkButton -> gtk_button_, so we can figure out the
286 argtype = target_arg.type.ctype.replace('*', '')
287 name = self._transformer.strip_namespace_object(argtype)
288 name_uscore = to_underscores(name).lower()
289 name_offset = func.symbol.find(name_uscore)
292 prefix = func.symbol[:name_offset+len(name_uscore)]
294 # Constructors must have _new
295 # Take everything before that as class name
296 new_idx = func.symbol.find('_new')
299 # Constructors don't return basic types
300 if target_arg.type.name in type_names:
302 prefix = func.symbol[:new_idx]
306 def valid_matching_klass(tclass):
307 return (tclass is not None and
308 isinstance(tclass, (GLibObject, GLibBoxed,
310 not isinstance(tclass, GLibEnum))
312 # First look for an exact match;
313 klass = self._uscore_type_names.get(prefix)
314 # Now try searching for a prefix as a last resort
315 if klass is None or not valid_matching_klass(klass):
316 for key in self._uscore_type_names:
318 if key.startswith(prefix):
319 klass = self._uscore_type_names.get(key)
320 if valid_matching_klass(klass):
322 # Enums can't have ctors or methods
327 # Interfaces can't have constructors, punt to global scope
328 if isinstance(klass, GLibInterface):
330 # TODO - check that the return type is a subclass of the
331 # class from the prefix
333 self._remove_attribute(func.name)
334 # Strip namespace and object prefix: gtk_window_new -> new
335 func.name = func.symbol[len(prefix)+1:]
337 klass.methods.append(func)
339 klass.constructors.append(func)
342 def _parse_struct(self, struct):
343 # This is a hack, but GObject is a rather fundamental piece so.
344 internal_names = ["Object", 'InitiallyUnowned']
345 g_internal_names = ["G" + x for x in internal_names]
346 if (self._namespace_name == 'GObject' and
347 struct.name in internal_names):
348 self._create_gobject(struct)
350 elif struct.name in g_internal_names:
353 node = self._names.names.get(struct.name)
355 self._add_attribute(struct, replace=True)
358 node.fields = struct.fields[:]
360 def _parse_union(self, union):
361 node = self._names.names.get(union.name)
363 self._add_attribute(union, replace=True)
366 node.fields = union.fields[:]
368 def _parse_callback(self, callback):
369 self._add_attribute(callback)
371 def _strip_class_suffix(self, name):
372 if (name.endswith('Class') or
373 name.endswith('Iface')):
375 elif name.endswith('Interface'):
380 def _arg_is_failed(self, param):
381 ctype = self._transformer.ctype_of(param).replace('*', '')
382 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
383 if uscored in self._failed_types:
384 print "Warning: failed type: %r" % (param, )
388 def _pair_class_struct(self, maybe_class):
389 name = self._strip_class_suffix(maybe_class.name)
390 if name == maybe_class.name:
393 if self._arg_is_failed(maybe_class):
394 print "WARNING: deleting no-type %r" % (maybe_class.name, )
395 del self._names.names[maybe_class.name]
398 name = self._resolve_type_name(name)
399 resolved = self._transformer.strip_namespace_object(name)
400 pair_class = self._get_attribute(resolved)
401 if pair_class and isinstance(pair_class,
402 (GLibObject, GLibBoxed, GLibInterface)):
403 for field in maybe_class.fields[1:]:
404 pair_class.fields.append(field)
406 name = self._transformer.strip_namespace_object(maybe_class.name)
407 pair_class = self._get_attribute(name)
408 if pair_class and isinstance(pair_class,
409 (GLibObject, GLibBoxed, GLibInterface)):
411 del self._names.names[maybe_class.name]
415 def _introspect_type(self, type_id, symbol):
416 fundamental_type_id = cgobject.type_fundamental(type_id)
417 if (fundamental_type_id == cgobject.TYPE_ENUM or
418 fundamental_type_id == cgobject.TYPE_FLAGS):
419 self._introspect_enum(fundamental_type_id, type_id, symbol)
420 elif fundamental_type_id == cgobject.TYPE_OBJECT:
421 self._introspect_object(type_id, symbol)
422 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
423 self._introspect_interface(type_id, symbol)
424 elif fundamental_type_id == cgobject.TYPE_BOXED:
425 self._introspect_boxed(type_id, symbol)
426 elif fundamental_type_id == cgobject.TYPE_BOXED:
427 self._introspect_boxed(type_id, symbol)
428 elif fundamental_type_id == cgobject.TYPE_POINTER:
429 # FIXME: Should we do something about these?
430 # GHashTable, GValue and a few other fundamentals are
434 print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
437 def _introspect_enum(self, ftype_id, type_id, symbol):
438 type_class = cgobject.type_class_ref(type_id)
439 if type_class is None:
443 for enum_value in type_class.get_values():
444 members.append(GLibEnumMember(enum_value.value_nick,
446 enum_value.value_name,
447 enum_value.value_nick))
449 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
450 type_name = cgobject.type_name(type_id)
451 enum_name = self._transformer.strip_namespace_object(type_name)
452 node = klass(enum_name, type_name, members, symbol)
453 self._add_attribute(node, replace=True)
454 self._register_internal_type(type_name, node)
456 def _introspect_object(self, type_id, symbol):
457 type_name = cgobject.type_name(type_id)
458 # We handle this specially above; in 2.16 and below there
459 # was no g_object_get_type, for later versions we need
461 if type_name == 'GObject':
463 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
464 parent_gitype = self._resolve_gtypename(parent_type_name)
466 self._transformer.strip_namespace_object(type_name),
469 self._introspect_properties(node, type_id)
470 self._introspect_signals(node, type_id)
471 self._introspect_implemented_interfaces(node, type_id)
472 self._add_attribute(node, replace=True)
473 self._register_internal_type(type_name, node)
475 def _introspect_interface(self, type_id, symbol):
476 type_name = cgobject.type_name(type_id)
477 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
478 if parent_type_name == 'GInterface':
481 parent_gitype = self._resolve_gtypename(parent_type_name)
482 node = GLibInterface(
483 self._transformer.strip_namespace_object(type_name),
486 self._introspect_properties(node, type_id)
487 self._introspect_signals(node, type_id)
488 # GtkFileChooserEmbed is an example of a private interface, we
489 # just filter them out
490 if symbol.startswith('_'):
491 print "NOTICE: Marking %s as internal type" % (type_name, )
492 self._private_internal_types[type_name] = node
494 self._add_attribute(node, replace=True)
495 self._register_internal_type(type_name, node)
497 def _introspect_boxed(self, type_id, symbol):
498 type_name = cgobject.type_name(type_id)
499 node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
501 self._add_attribute(node, replace=True)
502 self._register_internal_type(type_name, node)
504 def _introspect_implemented_interfaces(self, node, type_id):
505 fundamental_type_id = cgobject.type_fundamental(type_id)
506 if fundamental_type_id != cgobject.TYPE_OBJECT:
508 interfaces = cgobject.type_interfaces(type_id)
510 for interface_typeid in interfaces:
511 iname = cgobject.type_name(interface_typeid)
512 gitype = self._resolve_gtypename(iname)
513 gt_interfaces.append(gitype)
514 node.interfaces = gt_interfaces
516 def _introspect_properties(self, node, type_id):
517 fundamental_type_id = cgobject.type_fundamental(type_id)
518 if fundamental_type_id == cgobject.TYPE_OBJECT:
519 pspecs = cgobject.object_class_list_properties(type_id)
520 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
521 pspecs = cgobject.object_interface_list_properties(type_id)
526 if pspec.owner_type != type_id:
528 ctype = cgobject.type_name(pspec.value_type)
529 node.properties.append(Property(
531 type_name_from_ctype(ctype),
535 def _introspect_signals(self, node, type_id):
536 for signal_info in cgobject.signal_list(type_id):
537 rtype = self._create_type(signal_info.return_type)
538 return_ = Return(rtype)
539 signal = GLibSignal(signal_info.signal_name, return_)
540 for i, parameter in enumerate(signal_info.get_params()):
544 name = 'p%s' % (i-1, )
545 ptype = self._create_type(parameter)
546 param = Parameter(name, ptype)
547 signal.parameters.append(param)
548 node.signals.append(signal)
552 def _resolve_type_name(self, type_name, ctype=None):
553 # Workaround glib bug #548689, to be included in 2.18.0
554 if type_name == "GParam":
555 type_name = "GObject.ParamSpec"
557 res = self._transformer.resolve_type_name_full
559 return res(type_name, ctype, self._names)
561 return self._transformer.resolve_type_name(type_name, ctype)
563 def _validate_type_name(self, name):
564 if name in type_names:
566 if name.find('.') >= 0:
568 if name in self._names.aliases:
570 if name in self._names.names:
574 def _validate_type(self, ptype):
575 if isinstance(ptype, Sequence):
576 etype = ptype.element_type
577 if isinstance(etype, Sequence):
578 return self._validate_type(etype)
579 return self._validate_type_name(etype)
580 return self._validate_type_name(ptype.name)
582 def _resolve_param_type_validate(self, ptype):
583 ptype = self._resolve_param_type(ptype)
584 if self._validating and not self._validate_type(ptype):
585 raise UnknownTypeError("Unknown type %r" % (ptype, ))
588 def _resolve_param_type(self, ptype):
590 return self._transformer.resolve_param_type_full(ptype,
593 return self._transformer.resolve_param_type(ptype)
596 def _resolve_node(self, node):
597 if isinstance(node, Function):
598 self._resolve_function_toplevel(node)
600 elif isinstance(node, Callback):
601 self._resolve_function(node)
602 elif isinstance(node, GLibObject):
603 self._resolve_glib_object(node)
604 elif isinstance(node, GLibInterface):
605 self._resolve_glib_interface(node)
606 elif isinstance(node, GLibBoxed):
607 self._resolve_glib_boxed(node)
608 elif isinstance(node, Struct):
609 self._resolve_struct(node)
610 elif isinstance(node, Union):
611 self._resolve_union(node)
612 elif isinstance(node, Alias):
613 self._resolve_alias(node)
615 def _resolve_function_toplevel(self, func):
616 newfunc = self._parse_constructor(func)
618 newfunc = self._parse_method(func)
620 self._resolve_function(func)
622 self._resolve_function(newfunc)
624 def _resolve_struct(self, node):
625 for field in node.fields:
626 self._resolve_field(field)
628 def _resolve_union(self, node):
629 for field in node.fields:
630 self._resolve_field(field)
632 def _force_resolve(self, item, allow_unknown=False):
633 if isinstance(item, Unresolved):
634 if item.target in self._private_internal_types:
637 return self._transformer.gtypename_to_giname(item.target,
641 print "WARNING: Skipping unknown interface %s" % \
646 if item in self._private_internal_types:
650 def _resolve_glib_interface(self, node):
651 node.parent = self._force_resolve(node.parent)
652 self._resolve_methods(node.methods)
653 self._resolve_properties(node.properties)
654 self._resolve_signals(node.signals)
656 def _resolve_glib_object(self, node):
657 node.parent = self._force_resolve(node.parent)
658 node.interfaces = filter(None,
659 [self._force_resolve(x, allow_unknown=True)
660 for x in node.interfaces])
661 self._resolve_constructors(node.constructors)
662 self._resolve_methods(node.methods)
663 self._resolve_properties(node.properties)
664 self._resolve_signals(node.signals)
666 def _resolve_glib_boxed(self, node):
667 self._resolve_constructors(node.constructors)
668 self._resolve_methods(node.methods)
670 def _resolve_constructors(self, constructors):
671 for ctor in constructors:
672 self._resolve_function(ctor)
674 def _resolve_methods(self, methods):
675 for method in methods:
676 self._resolve_function(method)
678 def _resolve_signals(self, signals):
679 for signal in signals:
680 self._resolve_function(signal)
682 def _resolve_properties(self, properties):
683 for prop in properties:
684 self._resolve_property(prop)
686 def _resolve_property(self, prop):
687 prop.type = self._resolve_param_type(prop.type)
689 def _resolve_function(self, func):
690 self._resolve_parameters(func.parameters)
691 func.retval.type = self._resolve_param_type(func.retval.type)
693 def _resolve_parameters(self, parameters):
694 for parameter in parameters:
695 parameter.type = self._resolve_param_type(parameter.type)
697 def _resolve_field(self, field):
698 if isinstance(field, Callback):
699 self._resolve_function(field)
701 field.type = self._resolve_param_type(field.type)
703 def _resolve_alias(self, alias):
704 alias.target = self._resolve_type_name(alias.target, alias.target)
708 def _validate(self, nodes):
709 nodes = list(self._names.names.itervalues())
711 self._validating = True
715 print "Type resolution; pass=%d" % (i, )
716 nodes = list(self._names.names.itervalues())
719 self._resolve_node(node)
720 except UnknownTypeError, e:
721 print "WARNING: %s: Deleting %r" % (e, node)
722 self._remove_attribute(node.name)
723 if len(nodes) == initlen:
726 self._print_statistics()
727 self._validating = False