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
24 from ctypes.util import find_library
26 from . import cgobject
27 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
28 Property, Return, Struct, Type, Alias, Array,
29 Union, type_name_from_ctype,
30 default_array_types, TYPE_UINT8)
31 from .transformer import Names
32 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
33 GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
34 GLibBoxedUnion, GLibBoxedOther, type_names)
35 from .utils import extract_libtool, to_underscores, to_underscores_noprefix
37 default_array_types['guchar*'] = TYPE_UINT8
40 # These ones break GError conventions
41 'g_simple_async_result_new_from_error',
42 'g_simple_async_result_set_from_error',
43 'g_simple_async_result_propagate_error',
44 'g_simple_async_result_report_error_in_idle',
45 'gtk_print_operation_get_error',
48 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
49 [r'\w+_marshal_[A-Z]+__', ]]
52 class Unresolved(object):
54 def __init__(self, target):
58 class UnknownTypeError(Exception):
62 class GLibTransformer(object):
64 def __init__(self, transformer, noclosure=False):
65 self._transformer = transformer
66 self._transformer.set_container_types(['GList*', 'GSList*'],
68 self._namespace_name = None
70 self._uscore_type_names = {}
72 self._failed_types = {}
73 self._boxed_types = {}
74 self._private_internal_types = {}
75 self._noclosure = noclosure
76 self._validating = False
80 def add_library(self, libname):
82 libtool_libname = 'lib' + libname + '.la'
83 if os.path.exists(libtool_libname):
84 found_libname = extract_libtool(libtool_libname)
85 elif libname.endswith('.la'):
86 found_libname = extract_libtool(libname)
88 found_libname = find_library(libname)
90 raise ValueError("Failed to find library: %r" % (libname, ))
91 self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
93 def _print_statistics(self):
94 nodes = list(self._names.names.itervalues())
96 def count_type(otype):
97 return len([x for x in nodes
98 if isinstance(x[1], otype)])
99 objectcount = count_type(GLibObject)
100 ifacecount = count_type(GLibInterface)
101 enumcount = count_type(GLibEnum)
102 print " %d nodes; %d objects, %d interfaces, %d enumsr" \
103 % (len(nodes), objectcount, ifacecount, enumcount)
106 namespace = self._transformer.parse()
107 self._namespace_name = namespace.name
109 # First pass: parsing
110 for node in namespace.nodes:
111 self._parse_node(node)
113 # We don't want an alias for this - it's handled specially in
114 # the typelib compiler.
115 if namespace.name == 'GObject':
116 del self._names.aliases['Type']
118 # Introspection is done from within parsing
120 # Second pass: pair boxed structures
121 for boxed in self._boxed_types.itervalues():
122 self._pair_boxed_type(boxed)
123 # Third pass: delete class structures, resolve
124 # all types we now know about
125 nodes = list(self._names.names.itervalues())
126 for (ns, node) in nodes:
128 self._resolve_node(node)
130 print "WARNING: DELETING node %s: %s" % (node.name, e)
131 self._remove_attribute(node.name)
132 # associate GtkButtonClass with GtkButton
133 if isinstance(node, Struct):
134 self._pair_class_struct(node)
135 for (ns, alias) in self._names.aliases.itervalues():
136 self._resolve_alias(alias)
138 self._print_statistics()
139 # Fourth pass: ensure all types are known
140 if not self._noclosure:
141 self._validate(nodes)
143 # Create a new namespace with what we found
144 namespace = Namespace(namespace.name)
145 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
146 for (ns, x) in self._names.names.itervalues():
147 namespace.nodes.append(x)
152 def _add_attribute(self, node, replace=False):
153 node_name = node.name
154 if (not replace) and node_name in self._names.names:
156 self._names.names[node_name] = (None, node)
158 def _remove_attribute(self, name):
159 del self._names.names[name]
161 def _get_attribute(self, name):
162 node = self._names.names.get(name)
167 def _register_internal_type(self, type_name, node):
168 self._names.type_names[type_name] = (None, node)
169 uscored = to_underscores(type_name).lower()
170 self._uscore_type_names[uscored] = node
171 # Besides the straight underscore conversion, we also try
172 # removing the underscores from the namespace as a possible C
173 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
174 suffix = self._transformer.strip_namespace_object(type_name)
175 prefix = type_name[:-len(suffix)]
176 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
177 self._uscore_type_names[no_uscore_prefixed] = node
181 def _type_from_gtype(self, type_id):
182 ctype = cgobject.type_name(type_id)
183 type_name = type_name_from_ctype(ctype)
184 type_name = type_name.replace('*', '')
185 type_name = self._resolve_type_name(type_name)
186 return Type(type_name, ctype)
188 def _resolve_gtypename(self, gtype_name):
190 return self._transformer.gtypename_to_giname(gtype_name,
193 return Unresolved(gtype_name)
195 def _create_gobject(self, node):
196 type_name = 'G' + node.name
197 if type_name == 'GObject':
201 type_id = cgobject.type_from_name(type_name)
202 parent_type_name = cgobject.type_name(
203 cgobject.type_parent(type_id))
204 parent_gitype = self._resolve_gtypename(parent_type_name)
205 symbol = to_underscores(type_name).lower() + '_get_type'
206 node = GLibObject(node.name, parent_gitype, type_name, symbol)
207 type_id = cgobject.TYPE_OBJECT
208 self._introspect_properties(node, type_id)
209 self._introspect_signals(node, type_id)
210 self._add_attribute(node)
211 self._register_internal_type(type_name, node)
215 def _parse_node(self, node):
216 if isinstance(node, Enum):
217 self._parse_enum(node)
218 elif isinstance(node, Function):
219 self._parse_function(node)
220 elif isinstance(node, Struct):
221 self._parse_struct(node)
222 elif isinstance(node, Callback):
223 self._parse_callback(node)
224 elif isinstance(node, Alias):
225 self._parse_alias(node)
226 elif isinstance(node, Member):
227 # FIXME: atk_misc_instance singletons
229 elif isinstance(node, Union):
230 self._parse_union(node)
232 print 'GLIB Transformer: Unhandled node:', node
234 def _parse_alias(self, alias):
235 self._names.aliases[alias.name] = (None, alias)
237 def _parse_enum(self, enum):
238 self._add_attribute(enum)
240 def _parse_function(self, func):
241 if func.symbol in SYMBOL_BLACKLIST:
243 if func.symbol.startswith('_'):
245 for regexp in SYMBOL_BLACKLIST_RE:
246 if regexp.match(func.symbol):
248 if self._parse_get_type_function(func):
251 self._add_attribute(func)
253 def _parse_get_type_function(self, func):
255 if not symbol.endswith('_get_type'):
257 if self._namespace_name == 'GLib':
258 # No GObjects in GLib
260 # GType *_get_type(void)
261 if func.retval.type.name not in ['Type',
265 print ("Warning: *_get_type function returns '%r'"
266 ", not GObject.Type") % (func.retval.type.name, )
271 if not self._libraries:
272 print "Warning: No libraries loaded, cannot call %s" % (symbol, )
275 for library in self._libraries:
277 func = getattr(library, symbol)
279 except AttributeError:
282 print 'Warning: could not find symbol: %s' % symbol
283 name = symbol.replace('_get_type', '')
284 self._failed_types[name] = True
287 func.restype = cgobject.GType
290 self._introspect_type(type_id, symbol)
293 def _name_is_internal_gtype(self, giname):
295 node = self._get_attribute(giname)
296 return isinstance(node, (GLibObject, GLibInterface,
297 GLibEnum, GLibFlags))
301 def _parse_method(self, func):
302 if not func.parameters:
304 return self._parse_method_common(func, True)
306 def _parse_constructor(self, func):
307 return self._parse_method_common(func, False)
309 def _parse_method_common(self, func, is_method):
310 # Skip _get_type functions, we processed them
312 if func.symbol.endswith('_get_type'):
314 if self._namespace_name == 'GLib':
315 # No GObjects in GLib
319 target_arg = func.retval
321 target_arg = func.parameters[0]
322 target_arg.type = self._resolve_param_type(target_arg.type)
325 # Methods require their first arg to be a known class
326 # Look at the original C type (before namespace stripping), without
327 # pointers: GtkButton -> gtk_button_, so we can figure out the
329 argtype = target_arg.type.ctype.replace('*', '')
330 name = self._transformer.strip_namespace_object(argtype)
331 name_uscore = to_underscores_noprefix(name).lower()
332 name_offset = func.symbol.find(name_uscore)
335 prefix = func.symbol[:name_offset+len(name_uscore)]
337 # Constructors must have _new
338 # Take everything before that as class name
339 new_idx = func.symbol.find('_new')
342 # Constructors don't return basic types
343 derefed = self._transformer.follow_aliases(target_arg.type.name,
345 if derefed in type_names:
346 #print "NOTE: Rejecting constructor returning basic: %r" \
349 prefix = func.symbol[:new_idx]
353 def valid_matching_klass(tclass):
356 elif isinstance(klass, (GLibEnum, GLibFlags)):
358 elif not isinstance(tclass, (GLibObject, GLibBoxed,
364 klass = self._uscore_type_names.get(prefix)
366 #print "NOTE: No valid matching class for likely "+\
367 # "method or constructor: %r" % (func.symbol, )
369 # Enums can't have ctors or methods
370 if isinstance(klass, (GLibEnum, GLibFlags)):
373 # The _uscore_type_names member holds the plain GLibBoxed
374 # object; we want to actually use the struct/record associated
375 if isinstance(klass, GLibBoxed):
376 name = self._transformer.strip_namespace_object(klass.type_name)
377 klass = self._get_attribute(name)
380 # Interfaces can't have constructors, punt to global scope
381 if isinstance(klass, (GLibInterface, GLibBoxed)):
382 #print "NOTE: Rejecting constructor for"+\
383 # " interface type: %r" % (func.symbol, )
385 # TODO - check that the return type is a subclass of the
386 # class from the prefix
387 # But for now, ensure that constructor returns are always
388 # the most concrete class
389 func.retval.type = Type(klass.name, klass.ctype+'*')
391 self._remove_attribute(func.name)
392 # Strip namespace and object prefix: gtk_window_new -> new
393 func.name = func.symbol[len(prefix)+1:]
395 klass.methods.append(func)
397 klass.constructors.append(func)
400 def _parse_struct(self, struct):
401 # This is a hack, but GObject is a rather fundamental piece so.
402 internal_names = ["Object", 'InitiallyUnowned']
403 g_internal_names = ["G" + x for x in internal_names]
404 if (self._namespace_name == 'GObject' and
405 struct.name in internal_names):
406 self._create_gobject(struct)
408 elif struct.name in g_internal_names:
411 node = self._names.names.get(struct.name)
413 self._add_attribute(struct, replace=True)
416 node.fields = struct.fields[:]
418 def _parse_union(self, union):
419 node = self._names.names.get(union.name)
421 self._add_attribute(union, replace=True)
424 node.fields = union.fields[:]
426 def _parse_callback(self, callback):
427 self._add_attribute(callback)
429 def _strip_class_suffix(self, name):
430 if (name.endswith('Class') or
431 name.endswith('Iface')):
433 elif name.endswith('Interface'):
438 def _arg_is_failed(self, param):
439 ctype = self._transformer.ctype_of(param).replace('*', '')
440 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
441 if uscored in self._failed_types:
442 print "Warning: failed type: %r" % (param, )
446 def _pair_class_struct(self, maybe_class):
447 name = self._strip_class_suffix(maybe_class.name)
448 if name == maybe_class.name:
451 if self._arg_is_failed(maybe_class):
452 print "WARNING: deleting no-type %r" % (maybe_class.name, )
453 del self._names.names[maybe_class.name]
456 name = self._resolve_type_name(name)
457 resolved = self._transformer.strip_namespace_object(name)
458 pair_class = self._get_attribute(resolved)
459 if pair_class and isinstance(pair_class,
460 (GLibObject, GLibInterface)):
461 for field in maybe_class.fields[1:]:
462 pair_class.fields.append(field)
464 name = self._transformer.strip_namespace_object(maybe_class.name)
465 pair_class = self._get_attribute(name)
466 if pair_class and isinstance(pair_class,
467 (GLibObject, GLibInterface)):
469 del self._names.names[maybe_class.name]
473 def _introspect_type(self, type_id, symbol):
474 fundamental_type_id = cgobject.type_fundamental(type_id)
475 if (fundamental_type_id == cgobject.TYPE_ENUM or
476 fundamental_type_id == cgobject.TYPE_FLAGS):
477 self._introspect_enum(fundamental_type_id, type_id, symbol)
478 elif fundamental_type_id == cgobject.TYPE_OBJECT:
479 self._introspect_object(type_id, symbol)
480 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
481 self._introspect_interface(type_id, symbol)
482 elif fundamental_type_id == cgobject.TYPE_BOXED:
483 self._introspect_boxed(type_id, symbol)
484 elif fundamental_type_id == cgobject.TYPE_BOXED:
485 self._introspect_boxed(type_id, symbol)
486 elif fundamental_type_id == cgobject.TYPE_POINTER:
487 # FIXME: Should we do something about these?
488 # GHashTable, GValue and a few other fundamentals are
492 print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
495 def _introspect_enum(self, ftype_id, type_id, symbol):
496 type_class = cgobject.type_class_ref(type_id)
497 if type_class is None:
501 for enum_value in type_class.get_values():
502 members.append(GLibEnumMember(enum_value.value_nick,
504 enum_value.value_name,
505 enum_value.value_nick))
507 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
508 type_name = cgobject.type_name(type_id)
509 enum_name = self._transformer.strip_namespace_object(type_name)
510 node = klass(enum_name, type_name, members, symbol)
511 self._add_attribute(node, replace=True)
512 self._register_internal_type(type_name, node)
514 def _introspect_object(self, type_id, symbol):
515 type_name = cgobject.type_name(type_id)
516 # We handle this specially above; in 2.16 and below there
517 # was no g_object_get_type, for later versions we need
519 if type_name == 'GObject':
521 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
522 parent_gitype = self._resolve_gtypename(parent_type_name)
524 self._transformer.strip_namespace_object(type_name),
527 self._introspect_properties(node, type_id)
528 self._introspect_signals(node, type_id)
529 self._introspect_implemented_interfaces(node, type_id)
530 self._add_attribute(node, replace=True)
531 self._register_internal_type(type_name, node)
533 def _introspect_interface(self, type_id, symbol):
534 type_name = cgobject.type_name(type_id)
535 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
536 if parent_type_name == 'GInterface':
539 parent_gitype = self._resolve_gtypename(parent_type_name)
540 node = GLibInterface(
541 self._transformer.strip_namespace_object(type_name),
544 self._introspect_properties(node, type_id)
545 self._introspect_signals(node, type_id)
546 # GtkFileChooserEmbed is an example of a private interface, we
547 # just filter them out
548 if symbol.startswith('_'):
549 print "NOTICE: Marking %s as internal type" % (type_name, )
550 self._private_internal_types[type_name] = node
552 self._add_attribute(node, replace=True)
553 self._register_internal_type(type_name, node)
555 def _introspect_boxed(self, type_id, symbol):
556 type_name = cgobject.type_name(type_id)
557 # This one doesn't go in the main namespace; we associate it with
558 # the struct or union
559 node = GLibBoxed(type_name, symbol)
560 self._boxed_types[node.type_name] = node
561 self._register_internal_type(type_name, node)
563 def _introspect_implemented_interfaces(self, node, type_id):
564 fundamental_type_id = cgobject.type_fundamental(type_id)
565 if fundamental_type_id != cgobject.TYPE_OBJECT:
567 interfaces = cgobject.type_interfaces(type_id)
569 for interface_typeid in interfaces:
570 iname = cgobject.type_name(interface_typeid)
571 gitype = self._resolve_gtypename(iname)
572 gt_interfaces.append(gitype)
573 node.interfaces = gt_interfaces
575 def _introspect_properties(self, node, type_id):
576 fundamental_type_id = cgobject.type_fundamental(type_id)
577 if fundamental_type_id == cgobject.TYPE_OBJECT:
578 pspecs = cgobject.object_class_list_properties(type_id)
579 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
580 pspecs = cgobject.object_interface_list_properties(type_id)
585 if pspec.owner_type != type_id:
587 ctype = cgobject.type_name(pspec.value_type)
588 readable = (pspec.flags & 1) != 0
589 writable = (pspec.flags & 2) != 0
590 construct = (pspec.flags & 4) != 0
591 construct_only = (pspec.flags & 8) != 0
592 node.properties.append(Property(
594 type_name_from_ctype(ctype),
595 readable, writable, construct, construct_only,
599 def _introspect_signals(self, node, type_id):
600 for signal_info in cgobject.signal_list(type_id):
601 rtype = self._type_from_gtype(signal_info.return_type)
602 return_ = Return(rtype)
603 signal = GLibSignal(signal_info.signal_name, return_)
604 for i, parameter in enumerate(signal_info.get_params()):
608 name = 'p%s' % (i-1, )
609 ptype = self._type_from_gtype(parameter)
610 param = Parameter(name, ptype)
611 signal.parameters.append(param)
612 node.signals.append(signal)
616 def _resolve_type_name(self, type_name, ctype=None):
617 # Workaround glib bug #548689, to be included in 2.18.0
618 if type_name == "GParam":
619 type_name = "GObject.ParamSpec"
621 res = self._transformer.resolve_type_name_full
623 return res(type_name, ctype, self._names)
625 return self._transformer.resolve_type_name(type_name, ctype)
627 def _validate_type_name(self, name):
628 if name in type_names:
630 if name.find('.') >= 0:
632 if name in self._names.aliases:
634 if name in self._names.names:
638 def _validate_type(self, ptype):
639 if isinstance(ptype, Array):
640 etype = ptype.element_type
641 if isinstance(etype, Array):
642 return self._validate_type(etype)
643 return self._validate_type_name(etype)
644 return self._validate_type_name(ptype.name)
646 def _resolve_param_type_validate(self, ptype):
647 ptype = self._resolve_param_type(ptype)
648 if self._validating and not self._validate_type(ptype):
649 raise UnknownTypeError("Unknown type %r" % (ptype, ))
652 def _resolve_param_type(self, ptype):
654 return self._transformer.resolve_param_type_full(ptype,
657 return self._transformer.resolve_param_type(ptype)
660 def _resolve_node(self, node):
661 if isinstance(node, Function):
662 self._resolve_function_toplevel(node)
664 elif isinstance(node, Callback):
665 self._resolve_function(node)
666 elif isinstance(node, GLibObject):
667 self._resolve_glib_object(node)
668 elif isinstance(node, GLibInterface):
669 self._resolve_glib_interface(node)
670 elif isinstance(node, Struct):
671 self._resolve_struct(node)
672 elif isinstance(node, Union):
673 self._resolve_union(node)
674 elif isinstance(node, Alias):
675 self._resolve_alias(node)
677 def _resolve_function_toplevel(self, func):
678 newfunc = self._parse_constructor(func)
680 newfunc = self._parse_method(func)
682 self._resolve_function(func)
684 self._resolve_function(newfunc)
686 def _pair_boxed_type(self, boxed):
687 name = self._transformer.strip_namespace_object(boxed.type_name)
688 pair_node = self._get_attribute(name)
690 boxed_item = GLibBoxedOther(name, boxed.type_name,
692 elif isinstance(pair_node, Struct):
693 boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
695 boxed_item.fields = pair_node.fields
696 elif isinstance(pair_node, Union):
697 boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
699 boxed_item.fields = pair_node.fields
702 self._add_attribute(boxed_item, replace=True)
704 def _resolve_struct(self, node):
705 for field in node.fields:
706 self._resolve_field(field)
708 def _resolve_union(self, node):
709 for field in node.fields:
710 self._resolve_field(field)
712 def _force_resolve(self, item, allow_unknown=False):
713 if isinstance(item, Unresolved):
714 if item.target in self._private_internal_types:
717 return self._transformer.gtypename_to_giname(item.target,
721 print "WARNING: Skipping unknown interface %s" % \
726 if item in self._private_internal_types:
730 def _resolve_glib_interface(self, node):
731 node.parent = self._force_resolve(node.parent)
732 self._resolve_methods(node.methods)
733 self._resolve_properties(node.properties)
734 self._resolve_signals(node.signals)
736 def _resolve_glib_object(self, node):
737 node.parent = self._force_resolve(node.parent)
738 node.interfaces = filter(None,
739 [self._force_resolve(x, allow_unknown=True)
740 for x in node.interfaces])
741 self._resolve_constructors(node.constructors)
742 self._resolve_methods(node.methods)
743 self._resolve_properties(node.properties)
744 self._resolve_signals(node.signals)
746 def _resolve_glib_boxed(self, node):
747 self._resolve_constructors(node.constructors)
748 self._resolve_methods(node.methods)
750 def _resolve_constructors(self, constructors):
751 for ctor in constructors:
752 self._resolve_function(ctor)
754 def _resolve_methods(self, methods):
755 for method in methods:
756 self._resolve_function(method)
758 def _resolve_signals(self, signals):
759 for signal in signals:
760 self._resolve_function(signal)
762 def _resolve_properties(self, properties):
763 for prop in properties:
764 self._resolve_property(prop)
766 def _resolve_property(self, prop):
767 prop.type = self._resolve_param_type(prop.type)
769 def _resolve_function(self, func):
770 self._resolve_parameters(func.parameters)
771 func.retval.type = self._resolve_param_type(func.retval.type)
773 def _resolve_parameters(self, parameters):
774 for parameter in parameters:
775 parameter.type = self._resolve_param_type(parameter.type)
777 def _resolve_field(self, field):
778 if isinstance(field, Callback):
779 self._resolve_function(field)
781 field.type = self._resolve_param_type(field.type)
783 def _resolve_alias(self, alias):
784 alias.target = self._resolve_type_name(alias.target, alias.target)
788 def _validate(self, nodes):
789 nodes = list(self._names.names.itervalues())
791 self._validating = True
795 print "Type resolution; pass=%d" % (i, )
796 nodes = list(self._names.names.itervalues())
799 self._resolve_node(node)
800 except UnknownTypeError, e:
801 print "WARNING: %s: Deleting %r" % (e, node)
802 self._remove_attribute(node.name)
803 if len(nodes) == initlen:
806 self._print_statistics()
807 self._validating = False