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 Sequence, Property, Return, Struct, Type, Alias,
29 Union, type_name_from_ctype)
30 from .transformer import Names
31 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
32 GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
33 GLibBoxedUnion, GLibBoxedOther, type_names)
34 from .utils import extract_libtool, to_underscores
38 # These ones break GError conventions
39 'g_simple_async_result_new_from_error',
40 'g_simple_async_result_set_from_error',
41 'g_simple_async_result_propagate_error',
42 'g_simple_async_result_report_error_in_idle',
43 'gtk_print_operation_get_error',
46 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
47 [r'\w+_marshal_[A-Z]+__', ]]
50 class Unresolved(object):
52 def __init__(self, target):
56 class UnknownTypeError(Exception):
60 class GLibTransformer(object):
62 def __init__(self, transformer, noclosure=False):
63 self._transformer = transformer
64 self._namespace_name = None
66 self._uscore_type_names = {}
68 self._failed_types = {}
69 self._boxed_types = {}
70 self._private_internal_types = {}
71 self._noclosure = noclosure
72 self._validating = False
76 def add_library(self, libname):
78 libtool_libname = 'lib' + libname + '.la'
79 if os.path.exists(libtool_libname):
80 found_libname = extract_libtool(libtool_libname)
81 elif libname.endswith('.la'):
82 found_libname = extract_libtool(libname)
84 found_libname = find_library(libname)
86 raise ValueError("Failed to find library: %r" % (libname, ))
87 self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
89 def _print_statistics(self):
90 nodes = list(self._names.names.itervalues())
92 def count_type(otype):
93 return len([x for x in nodes
94 if isinstance(x[1], otype)])
95 objectcount = count_type(GLibObject)
96 ifacecount = count_type(GLibInterface)
97 enumcount = count_type(GLibEnum)
98 print " %d nodes; %d objects, %d interfaces, %d enumsr" \
99 % (len(nodes), objectcount, ifacecount, enumcount)
102 namespace = self._transformer.parse()
103 self._namespace_name = namespace.name
105 # First pass: parsing
106 for node in namespace.nodes:
107 self._parse_node(node)
109 # Introspection is done from within parsing
111 # Second pass: pair boxed structures
112 for boxed in self._boxed_types.itervalues():
113 self._pair_boxed_type(boxed)
114 # Third pass: delete class structures, resolve
115 # all types we now know about
116 nodes = list(self._names.names.itervalues())
117 for (ns, node) in nodes:
119 self._resolve_node(node)
121 print "WARNING: DELETING node %s: %s" % (node.name, e)
122 self._remove_attribute(node.name)
123 # associate GtkButtonClass with GtkButton
124 if isinstance(node, Struct):
125 self._pair_class_struct(node)
126 for (ns, alias) in self._names.aliases.itervalues():
127 self._resolve_alias(alias)
129 self._print_statistics()
130 # Fourth pass: ensure all types are known
131 if not self._noclosure:
132 self._validate(nodes)
134 # Create a new namespace with what we found
135 namespace = Namespace(namespace.name)
136 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
137 for (ns, x) in self._names.names.itervalues():
138 namespace.nodes.append(x)
143 def _add_attribute(self, node, replace=False):
144 node_name = node.name
145 if (not replace) and node_name in self._names.names:
147 self._names.names[node_name] = (None, node)
149 def _remove_attribute(self, name):
150 del self._names.names[name]
152 def _get_attribute(self, name):
153 node = self._names.names.get(name)
158 def _register_internal_type(self, type_name, node):
159 self._names.type_names[type_name] = (None, node)
160 self._uscore_type_names[to_underscores(type_name).lower()] = node
161 # Besides the straight underscore conversion, we also try
162 # removing the underscores from the namespace as a possible C
163 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
164 suffix = self._transformer.strip_namespace_object(type_name)
165 prefix = type_name[:-len(suffix)]
166 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
167 self._uscore_type_names[no_uscore_prefixed] = node
171 def _create_type(self, type_id):
172 ctype = cgobject.type_name(type_id)
173 type_name = type_name_from_ctype(ctype)
174 type_name = type_name.replace('*', '')
175 type_name = self._resolve_type_name(type_name)
176 return Type(type_name, ctype)
178 def _resolve_gtypename(self, gtype_name):
180 return self._transformer.gtypename_to_giname(gtype_name,
183 return Unresolved(gtype_name)
185 def _create_gobject(self, node):
186 type_name = 'G' + node.name
187 if type_name == 'GObject':
191 type_id = cgobject.type_from_name(type_name)
192 parent_type_name = cgobject.type_name(
193 cgobject.type_parent(type_id))
194 parent_gitype = self._resolve_gtypename(parent_type_name)
195 symbol = to_underscores(type_name).lower() + '_get_type'
196 node = GLibObject(node.name, parent_gitype, type_name, symbol)
197 type_id = cgobject.TYPE_OBJECT
198 self._introspect_properties(node, type_id)
199 self._introspect_signals(node, type_id)
200 self._add_attribute(node)
201 self._register_internal_type(type_name, node)
205 def _parse_node(self, node):
206 if isinstance(node, Enum):
207 self._parse_enum(node)
208 elif isinstance(node, Function):
209 self._parse_function(node)
210 elif isinstance(node, Struct):
211 self._parse_struct(node)
212 elif isinstance(node, Callback):
213 self._parse_callback(node)
214 elif isinstance(node, Alias):
215 self._parse_alias(node)
216 elif isinstance(node, Member):
217 # FIXME: atk_misc_instance singletons
219 elif isinstance(node, Union):
220 self._parse_union(node)
222 print 'GLIB Transformer: Unhandled node:', node
224 def _parse_alias(self, alias):
225 self._names.aliases[alias.name] = (None, alias)
227 def _parse_enum(self, enum):
228 self._add_attribute(enum)
230 def _parse_function(self, func):
231 if func.symbol in SYMBOL_BLACKLIST:
233 if func.symbol.startswith('_'):
235 for regexp in SYMBOL_BLACKLIST_RE:
236 if regexp.match(func.symbol):
238 if self._parse_get_type_function(func):
241 self._add_attribute(func)
243 def _parse_get_type_function(self, func):
245 if not symbol.endswith('_get_type'):
247 if self._namespace_name == 'GLib':
248 # No GObjects in GLib
250 # GType *_get_type(void)
251 # This is a bit fishy, why do we need all these aliases?
252 if func.retval.type.name not in ['Type',
258 print ("Warning: *_get_type function returns '%r'"
259 ", not GObject.Type") % (func.retval.type.name, )
264 if not self._libraries:
265 print "Warning: No libraries loaded, cannot call %s" % (symbol, )
268 for library in self._libraries:
270 func = getattr(library, symbol)
272 except AttributeError:
275 print 'Warning: could not find symbol: %s' % symbol
276 name = symbol.replace('_get_type', '')
277 self._failed_types[name] = True
280 func.restype = cgobject.GType
283 self._introspect_type(type_id, symbol)
286 def _name_is_internal_gtype(self, giname):
288 node = self._get_attribute(giname)
289 return isinstance(node, (GLibObject, GLibInterface,
290 GLibEnum, GLibFlags))
294 def _parse_method(self, func):
295 if not func.parameters:
297 return self._parse_method_common(func, True)
299 def _parse_constructor(self, func):
300 return self._parse_method_common(func, False)
302 def _parse_method_common(self, func, is_method):
303 # Skip _get_type functions, we processed them
305 if func.symbol.endswith('_get_type'):
307 if self._namespace_name == 'GLib':
308 # No GObjects in GLib
312 target_arg = func.retval
314 target_arg = func.parameters[0]
315 target_arg.type = self._resolve_param_type(target_arg.type)
318 # Methods require their first arg to be a known class
319 # Look at the original C type (before namespace stripping), without
320 # pointers: GtkButton -> gtk_button_, so we can figure out the
322 argtype = target_arg.type.ctype.replace('*', '')
323 name = self._transformer.strip_namespace_object(argtype)
324 name_uscore = to_underscores(name).lower()
325 name_offset = func.symbol.find(name_uscore)
328 prefix = func.symbol[:name_offset+len(name_uscore)]
330 # Constructors must have _new
331 # Take everything before that as class name
332 new_idx = func.symbol.find('_new')
335 # Constructors don't return basic types
336 derefed = self._transformer.follow_aliases(target_arg.type.name,
338 if derefed in type_names:
339 #print "NOTE: Rejecting constructor returning basic: %r" \
342 prefix = func.symbol[:new_idx]
346 def valid_matching_klass(tclass):
349 elif isinstance(klass, (GLibEnum, GLibFlags)):
351 elif not isinstance(tclass, (GLibObject, GLibBoxed,
357 klass = self._uscore_type_names.get(prefix)
359 #print "NOTE: No valid matching class for likely "+\
360 # "method or constructor: %r" % (func.symbol, )
362 # Enums can't have ctors or methods
363 if isinstance(klass, (GLibEnum, GLibFlags)):
366 # The _uscore_type_names member holds the plain GLibBoxed
367 # object; we want to actually use the struct/record associated
368 if isinstance(klass, GLibBoxed):
369 name = self._transformer.strip_namespace_object(klass.type_name)
370 klass = self._get_attribute(name)
373 # Interfaces can't have constructors, punt to global scope
374 if isinstance(klass, (GLibInterface, GLibBoxed)):
375 #print "NOTE: Rejecting constructor for"+\
376 # " interface type: %r" % (func.symbol, )
378 # TODO - check that the return type is a subclass of the
379 # class from the prefix
381 self._remove_attribute(func.name)
382 # Strip namespace and object prefix: gtk_window_new -> new
383 func.name = func.symbol[len(prefix)+1:]
385 klass.methods.append(func)
387 klass.constructors.append(func)
390 def _parse_struct(self, struct):
391 # This is a hack, but GObject is a rather fundamental piece so.
392 internal_names = ["Object", 'InitiallyUnowned']
393 g_internal_names = ["G" + x for x in internal_names]
394 if (self._namespace_name == 'GObject' and
395 struct.name in internal_names):
396 self._create_gobject(struct)
398 elif struct.name in g_internal_names:
401 node = self._names.names.get(struct.name)
403 self._add_attribute(struct, replace=True)
406 node.fields = struct.fields[:]
408 def _parse_union(self, union):
409 node = self._names.names.get(union.name)
411 self._add_attribute(union, replace=True)
414 node.fields = union.fields[:]
416 def _parse_callback(self, callback):
417 self._add_attribute(callback)
419 def _strip_class_suffix(self, name):
420 if (name.endswith('Class') or
421 name.endswith('Iface')):
423 elif name.endswith('Interface'):
428 def _arg_is_failed(self, param):
429 ctype = self._transformer.ctype_of(param).replace('*', '')
430 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
431 if uscored in self._failed_types:
432 print "Warning: failed type: %r" % (param, )
436 def _pair_class_struct(self, maybe_class):
437 name = self._strip_class_suffix(maybe_class.name)
438 if name == maybe_class.name:
441 if self._arg_is_failed(maybe_class):
442 print "WARNING: deleting no-type %r" % (maybe_class.name, )
443 del self._names.names[maybe_class.name]
446 name = self._resolve_type_name(name)
447 resolved = self._transformer.strip_namespace_object(name)
448 pair_class = self._get_attribute(resolved)
449 if pair_class and isinstance(pair_class,
450 (GLibObject, GLibInterface)):
451 for field in maybe_class.fields[1:]:
452 pair_class.fields.append(field)
454 name = self._transformer.strip_namespace_object(maybe_class.name)
455 pair_class = self._get_attribute(name)
456 if pair_class and isinstance(pair_class,
457 (GLibObject, GLibInterface)):
459 del self._names.names[maybe_class.name]
463 def _introspect_type(self, type_id, symbol):
464 fundamental_type_id = cgobject.type_fundamental(type_id)
465 if (fundamental_type_id == cgobject.TYPE_ENUM or
466 fundamental_type_id == cgobject.TYPE_FLAGS):
467 self._introspect_enum(fundamental_type_id, type_id, symbol)
468 elif fundamental_type_id == cgobject.TYPE_OBJECT:
469 self._introspect_object(type_id, symbol)
470 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
471 self._introspect_interface(type_id, symbol)
472 elif fundamental_type_id == cgobject.TYPE_BOXED:
473 self._introspect_boxed(type_id, symbol)
474 elif fundamental_type_id == cgobject.TYPE_BOXED:
475 self._introspect_boxed(type_id, symbol)
476 elif fundamental_type_id == cgobject.TYPE_POINTER:
477 # FIXME: Should we do something about these?
478 # GHashTable, GValue and a few other fundamentals are
482 print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
485 def _introspect_enum(self, ftype_id, type_id, symbol):
486 type_class = cgobject.type_class_ref(type_id)
487 if type_class is None:
491 for enum_value in type_class.get_values():
492 members.append(GLibEnumMember(enum_value.value_nick,
494 enum_value.value_name,
495 enum_value.value_nick))
497 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
498 type_name = cgobject.type_name(type_id)
499 enum_name = self._transformer.strip_namespace_object(type_name)
500 node = klass(enum_name, type_name, members, symbol)
501 self._add_attribute(node, replace=True)
502 self._register_internal_type(type_name, node)
504 def _introspect_object(self, type_id, symbol):
505 type_name = cgobject.type_name(type_id)
506 # We handle this specially above; in 2.16 and below there
507 # was no g_object_get_type, for later versions we need
509 if type_name == 'GObject':
511 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
512 parent_gitype = self._resolve_gtypename(parent_type_name)
514 self._transformer.strip_namespace_object(type_name),
517 self._introspect_properties(node, type_id)
518 self._introspect_signals(node, type_id)
519 self._introspect_implemented_interfaces(node, type_id)
520 self._add_attribute(node, replace=True)
521 self._register_internal_type(type_name, node)
523 def _introspect_interface(self, type_id, symbol):
524 type_name = cgobject.type_name(type_id)
525 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
526 if parent_type_name == 'GInterface':
529 parent_gitype = self._resolve_gtypename(parent_type_name)
530 node = GLibInterface(
531 self._transformer.strip_namespace_object(type_name),
534 self._introspect_properties(node, type_id)
535 self._introspect_signals(node, type_id)
536 # GtkFileChooserEmbed is an example of a private interface, we
537 # just filter them out
538 if symbol.startswith('_'):
539 print "NOTICE: Marking %s as internal type" % (type_name, )
540 self._private_internal_types[type_name] = node
542 self._add_attribute(node, replace=True)
543 self._register_internal_type(type_name, node)
545 def _introspect_boxed(self, type_id, symbol):
546 type_name = cgobject.type_name(type_id)
547 # This one doesn't go in the main namespace; we associate it with
548 # the struct or union
549 node = GLibBoxed(type_name, symbol)
550 self._boxed_types[node.type_name] = node
551 self._register_internal_type(type_name, node)
553 def _introspect_implemented_interfaces(self, node, type_id):
554 fundamental_type_id = cgobject.type_fundamental(type_id)
555 if fundamental_type_id != cgobject.TYPE_OBJECT:
557 interfaces = cgobject.type_interfaces(type_id)
559 for interface_typeid in interfaces:
560 iname = cgobject.type_name(interface_typeid)
561 gitype = self._resolve_gtypename(iname)
562 gt_interfaces.append(gitype)
563 node.interfaces = gt_interfaces
565 def _introspect_properties(self, node, type_id):
566 fundamental_type_id = cgobject.type_fundamental(type_id)
567 if fundamental_type_id == cgobject.TYPE_OBJECT:
568 pspecs = cgobject.object_class_list_properties(type_id)
569 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
570 pspecs = cgobject.object_interface_list_properties(type_id)
575 if pspec.owner_type != type_id:
577 ctype = cgobject.type_name(pspec.value_type)
578 readable = (pspec.flags & 1) != 0
579 writable = (pspec.flags & 2) != 0
580 construct = (pspec.flags & 4) != 0
581 construct_only = (pspec.flags & 8) != 0
582 node.properties.append(Property(
584 type_name_from_ctype(ctype),
585 readable, writable, construct, construct_only,
589 def _introspect_signals(self, node, type_id):
590 for signal_info in cgobject.signal_list(type_id):
591 rtype = self._create_type(signal_info.return_type)
592 return_ = Return(rtype)
593 signal = GLibSignal(signal_info.signal_name, return_)
594 for i, parameter in enumerate(signal_info.get_params()):
598 name = 'p%s' % (i-1, )
599 ptype = self._create_type(parameter)
600 param = Parameter(name, ptype)
601 signal.parameters.append(param)
602 node.signals.append(signal)
606 def _resolve_type_name(self, type_name, ctype=None):
607 # Workaround glib bug #548689, to be included in 2.18.0
608 if type_name == "GParam":
609 type_name = "GObject.ParamSpec"
611 res = self._transformer.resolve_type_name_full
613 return res(type_name, ctype, self._names)
615 return self._transformer.resolve_type_name(type_name, ctype)
617 def _validate_type_name(self, name):
618 if name in type_names:
620 if name.find('.') >= 0:
622 if name in self._names.aliases:
624 if name in self._names.names:
628 def _validate_type(self, ptype):
629 if isinstance(ptype, Sequence):
630 etype = ptype.element_type
631 if isinstance(etype, Sequence):
632 return self._validate_type(etype)
633 return self._validate_type_name(etype)
634 return self._validate_type_name(ptype.name)
636 def _resolve_param_type_validate(self, ptype):
637 ptype = self._resolve_param_type(ptype)
638 if self._validating and not self._validate_type(ptype):
639 raise UnknownTypeError("Unknown type %r" % (ptype, ))
642 def _resolve_param_type(self, ptype):
644 return self._transformer.resolve_param_type_full(ptype,
647 return self._transformer.resolve_param_type(ptype)
650 def _resolve_node(self, node):
651 if isinstance(node, Function):
652 self._resolve_function_toplevel(node)
654 elif isinstance(node, Callback):
655 self._resolve_function(node)
656 elif isinstance(node, GLibObject):
657 self._resolve_glib_object(node)
658 elif isinstance(node, GLibInterface):
659 self._resolve_glib_interface(node)
660 elif isinstance(node, Struct):
661 self._resolve_struct(node)
662 elif isinstance(node, Union):
663 self._resolve_union(node)
664 elif isinstance(node, Alias):
665 self._resolve_alias(node)
667 def _resolve_function_toplevel(self, func):
668 newfunc = self._parse_constructor(func)
670 newfunc = self._parse_method(func)
672 self._resolve_function(func)
674 self._resolve_function(newfunc)
676 def _pair_boxed_type(self, boxed):
677 name = self._transformer.strip_namespace_object(boxed.type_name)
678 pair_node = self._get_attribute(name)
680 boxed_item = GLibBoxedOther(name, boxed.type_name,
682 elif isinstance(pair_node, Struct):
683 boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
685 boxed_item.fields = pair_node.fields
686 elif isinstance(pair_node, Union):
687 boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
689 boxed_item.fields = pair_node.fields
692 self._add_attribute(boxed_item, replace=True)
694 def _resolve_struct(self, node):
695 for field in node.fields:
696 self._resolve_field(field)
698 def _resolve_union(self, node):
699 for field in node.fields:
700 self._resolve_field(field)
702 def _force_resolve(self, item, allow_unknown=False):
703 if isinstance(item, Unresolved):
704 if item.target in self._private_internal_types:
707 return self._transformer.gtypename_to_giname(item.target,
711 print "WARNING: Skipping unknown interface %s" % \
716 if item in self._private_internal_types:
720 def _resolve_glib_interface(self, node):
721 node.parent = self._force_resolve(node.parent)
722 self._resolve_methods(node.methods)
723 self._resolve_properties(node.properties)
724 self._resolve_signals(node.signals)
726 def _resolve_glib_object(self, node):
727 node.parent = self._force_resolve(node.parent)
728 node.interfaces = filter(None,
729 [self._force_resolve(x, allow_unknown=True)
730 for x in node.interfaces])
731 self._resolve_constructors(node.constructors)
732 self._resolve_methods(node.methods)
733 self._resolve_properties(node.properties)
734 self._resolve_signals(node.signals)
736 def _resolve_glib_boxed(self, node):
737 self._resolve_constructors(node.constructors)
738 self._resolve_methods(node.methods)
740 def _resolve_constructors(self, constructors):
741 for ctor in constructors:
742 self._resolve_function(ctor)
744 def _resolve_methods(self, methods):
745 for method in methods:
746 self._resolve_function(method)
748 def _resolve_signals(self, signals):
749 for signal in signals:
750 self._resolve_function(signal)
752 def _resolve_properties(self, properties):
753 for prop in properties:
754 self._resolve_property(prop)
756 def _resolve_property(self, prop):
757 prop.type = self._resolve_param_type(prop.type)
759 def _resolve_function(self, func):
760 self._resolve_parameters(func.parameters)
761 func.retval.type = self._resolve_param_type(func.retval.type)
763 def _resolve_parameters(self, parameters):
764 for parameter in parameters:
765 parameter.type = self._resolve_param_type(parameter.type)
767 def _resolve_field(self, field):
768 if isinstance(field, Callback):
769 self._resolve_function(field)
771 field.type = self._resolve_param_type(field.type)
773 def _resolve_alias(self, alias):
774 alias.target = self._resolve_type_name(alias.target, alias.target)
778 def _validate(self, nodes):
779 nodes = list(self._names.names.itervalues())
781 self._validating = True
785 print "Type resolution; pass=%d" % (i, )
786 nodes = list(self._names.names.itervalues())
789 self._resolve_node(node)
790 except UnknownTypeError, e:
791 print "WARNING: %s: Deleting %r" % (e, node)
792 self._remove_attribute(node.name)
793 if len(nodes) == initlen:
796 self._print_statistics()
797 self._validating = False