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, type_names)
33 from .utils import extract_libtool, to_underscores
37 # These ones break GError conventions
38 'g_simple_async_result_new_from_error',
39 'g_simple_async_result_set_from_error',
40 'g_simple_async_result_propagate_error',
41 'g_simple_async_result_report_error_in_idle',
42 'gtk_print_operation_get_error',
45 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
46 [r'\w+_marshal_[A-Z]+__', ]]
49 class Unresolved(object):
51 def __init__(self, target):
55 class UnknownTypeError(Exception):
59 class GLibTransformer(object):
61 def __init__(self, transformer, noclosure=False):
62 self._transformer = transformer
63 self._namespace_name = None
65 self._uscore_type_names = {}
67 self._failed_types = {}
68 self._private_internal_types = {}
69 self._noclosure = noclosure
70 self._validating = False
74 def add_library(self, libname):
76 libtool_libname = 'lib' + libname + '.la'
77 if os.path.exists(libtool_libname):
78 found_libname = extract_libtool(libtool_libname)
79 elif libname.endswith('.la'):
80 found_libname = extract_libtool(libname)
82 found_libname = find_library(libname)
84 raise ValueError("Failed to find library: %r" % (libname, ))
85 self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
87 def _print_statistics(self):
88 nodes = list(self._names.names.itervalues())
90 def count_type(otype):
91 return len([x for x in nodes
92 if isinstance(x[1], otype)])
93 objectcount = count_type(GLibObject)
94 ifacecount = count_type(GLibInterface)
95 enumcount = count_type(GLibEnum)
96 boxedcount = count_type(GLibBoxed)
97 print " %d nodes; %d objects, %d interfaces, %d enums, %d boxed" \
98 % (len(nodes), objectcount, ifacecount, enumcount, boxedcount)
101 namespace = self._transformer.parse()
102 self._namespace_name = namespace.name
104 # First pass, parsing
105 for node in namespace.nodes:
106 self._parse_node(node)
108 # Introspection is done from within parsing
110 # Second pass, delete class structures, resolve
111 # all types we now know about
112 nodes = list(self._names.names.itervalues())
113 for (ns, node) in nodes:
115 self._resolve_node(node)
117 print "WARNING: DELETING node %s: %s" % (node.name, e)
118 self._remove_attribute(node.name)
119 # associate GtkButtonClass with GtkButton
120 if isinstance(node, Struct):
121 self._pair_class_struct(node)
122 for (ns, alias) in self._names.aliases.itervalues():
123 self._resolve_alias(alias)
125 self._print_statistics()
126 # Third pass; ensure all types are known
127 if not self._noclosure:
128 self._validate(nodes)
130 # Create a new namespace with what we found
131 namespace = Namespace(namespace.name)
132 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
133 for (ns, x) in self._names.names.itervalues():
134 namespace.nodes.append(x)
135 print "Scan complete."
140 def _add_attribute(self, node, replace=False):
141 node_name = node.name
142 if (not replace) and node_name in self._names.names:
144 self._names.names[node_name] = (None, node)
146 def _remove_attribute(self, name):
147 del self._names.names[name]
149 def _get_attribute(self, name):
150 node = self._names.names.get(name)
155 def _register_internal_type(self, type_name, node):
156 self._names.type_names[type_name] = (None, node)
157 self._uscore_type_names[to_underscores(type_name).lower()] = node
158 # Besides the straight underscore conversion, we also try
159 # removing the underscores from the namespace as a possible C
160 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
161 suffix = self._transformer.strip_namespace_object(type_name)
162 prefix = type_name[:-len(suffix)]
163 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
164 self._uscore_type_names[no_uscore_prefixed] = node
168 def _create_type(self, type_id):
169 ctype = cgobject.type_name(type_id)
170 type_name = type_name_from_ctype(ctype)
171 type_name = type_name.replace('*', '')
172 type_name = self._resolve_type_name(type_name)
173 return Type(type_name, ctype)
175 def _resolve_gtypename(self, gtype_name):
177 return self._transformer.gtypename_to_giname(gtype_name,
180 return Unresolved(gtype_name)
182 def _create_gobject(self, node):
183 type_name = 'G' + node.name
184 if type_name == 'GObject':
188 type_id = cgobject.type_from_name(type_name)
189 parent_type_name = cgobject.type_name(
190 cgobject.type_parent(type_id))
191 parent_gitype = self._resolve_gtypename(parent_type_name)
192 symbol = to_underscores(type_name).lower() + '_get_type'
193 node = GLibObject(node.name, parent_gitype, type_name, symbol)
194 type_id = cgobject.TYPE_OBJECT
195 self._introspect_properties(node, type_id)
196 self._introspect_signals(node, type_id)
197 self._add_attribute(node)
198 self._register_internal_type(type_name, node)
202 def _parse_node(self, node):
203 if isinstance(node, Enum):
204 self._parse_enum(node)
205 elif isinstance(node, Function):
206 self._parse_function(node)
207 elif isinstance(node, Struct):
208 self._parse_struct(node)
209 elif isinstance(node, Callback):
210 self._parse_callback(node)
211 elif isinstance(node, Alias):
212 self._parse_alias(node)
213 elif isinstance(node, Member):
214 # FIXME: atk_misc_instance singletons
216 elif isinstance(node, Union):
217 self._parse_union(node)
219 print 'GLIB Transformer: Unhandled node:', node
221 def _parse_alias(self, alias):
222 self._names.aliases[alias.name] = (None, alias)
224 def _parse_enum(self, enum):
225 self._add_attribute(enum)
227 def _parse_function(self, func):
228 if func.symbol in SYMBOL_BLACKLIST:
230 for regexp in SYMBOL_BLACKLIST_RE:
231 if regexp.match(func.symbol):
233 if self._parse_get_type_function(func):
236 self._add_attribute(func)
238 def _parse_get_type_function(self, func):
240 if not symbol.endswith('_get_type'):
242 if self._namespace_name == 'GLib':
243 # No GObjects in GLib
245 # GType *_get_type(void)
246 # This is a bit fishy, why do we need all these aliases?
247 if func.retval.type.name not in ['Type',
252 print ("Warning: *_get_type function returns '%r'"
253 ", not GObject.Type") % (func.retval.type.name, )
258 if not self._libraries:
259 print "Warning: No libraries loaded, cannot call %s" % (symbol, )
262 for library in self._libraries:
264 func = getattr(library, symbol)
266 except AttributeError:
269 print 'Warning: could not find symbol: %s' % symbol
270 name = symbol.replace('_get_type', '')
271 self._failed_types[name] = True
274 func.restype = cgobject.GType
277 self._introspect_type(type_id, symbol)
280 def _name_is_internal_gtype(self, giname):
282 node = self._get_attribute(giname)
283 return isinstance(node, (GLibObject, GLibInterface, GLibBoxed,
284 GLibEnum, GLibFlags))
288 def _parse_method(self, func):
289 if not func.parameters:
291 return self._parse_method_common(func, True)
293 def _parse_constructor(self, func):
294 return self._parse_method_common(func, False)
296 def _parse_method_common(self, func, is_method):
297 # Skip _get_type functions, we processed them
299 if func.symbol.endswith('_get_type'):
301 if self._namespace_name == 'GLib':
302 # No GObjects in GLib
306 target_arg = func.retval
308 target_arg = func.parameters[0]
309 target_arg.type = self._resolve_param_type(target_arg.type)
312 # Methods require their first arg to be a known class
313 # Look at the original C type (before namespace stripping), without
314 # pointers: GtkButton -> gtk_button_, so we can figure out the
316 argtype = target_arg.type.ctype.replace('*', '')
317 name = self._transformer.strip_namespace_object(argtype)
318 name_uscore = to_underscores(name).lower()
319 name_offset = func.symbol.find(name_uscore)
322 prefix = func.symbol[:name_offset+len(name_uscore)]
324 # Constructors must have _new
325 # Take everything before that as class name
326 new_idx = func.symbol.find('_new')
329 # Constructors don't return basic types
330 derefed = self._transformer.follow_aliases(target_arg.type.name,
332 if derefed in type_names:
333 #print "NOTE: Rejecting constructor returning basic: %r" \
336 prefix = func.symbol[:new_idx]
340 def valid_matching_klass(tclass):
343 elif isinstance(klass, (GLibEnum, GLibFlags)):
345 elif not isinstance(tclass, (GLibObject, GLibBoxed,
351 # First look for an exact match;
352 klass = self._uscore_type_names.get(prefix)
353 # Now try searching for a prefix as a last resort
354 if klass is None or not valid_matching_klass(klass):
355 for key in self._uscore_type_names:
357 if key.startswith(prefix):
358 klass = self._uscore_type_names.get(key)
359 if valid_matching_klass(klass):
362 #print "NOTE: No valid matching class for likely "+\
363 # "method or constructor: %r" % (func.symbol, )
365 # Enums can't have ctors or methods
366 if isinstance(klass, (GLibEnum, GLibFlags)):
370 # Interfaces can't have constructors, punt to global scope
371 if isinstance(klass, GLibInterface):
372 #print "NOTE: Rejecting constructor for"+\
373 # " interface type: %r" % (func.symbol, )
375 # TODO - check that the return type is a subclass of the
376 # class from the prefix
378 self._remove_attribute(func.name)
379 # Strip namespace and object prefix: gtk_window_new -> new
380 func.name = func.symbol[len(prefix)+1:]
382 klass.methods.append(func)
384 klass.constructors.append(func)
387 def _parse_struct(self, struct):
388 # This is a hack, but GObject is a rather fundamental piece so.
389 internal_names = ["Object", 'InitiallyUnowned']
390 g_internal_names = ["G" + x for x in internal_names]
391 if (self._namespace_name == 'GObject' and
392 struct.name in internal_names):
393 self._create_gobject(struct)
395 elif struct.name in g_internal_names:
398 node = self._names.names.get(struct.name)
400 self._add_attribute(struct, replace=True)
403 node.fields = struct.fields[:]
405 def _parse_union(self, union):
406 node = self._names.names.get(union.name)
408 self._add_attribute(union, replace=True)
411 node.fields = union.fields[:]
413 def _parse_callback(self, callback):
414 self._add_attribute(callback)
416 def _strip_class_suffix(self, name):
417 if (name.endswith('Class') or
418 name.endswith('Iface')):
420 elif name.endswith('Interface'):
425 def _arg_is_failed(self, param):
426 ctype = self._transformer.ctype_of(param).replace('*', '')
427 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
428 if uscored in self._failed_types:
429 print "Warning: failed type: %r" % (param, )
433 def _pair_class_struct(self, maybe_class):
434 name = self._strip_class_suffix(maybe_class.name)
435 if name == maybe_class.name:
438 if self._arg_is_failed(maybe_class):
439 print "WARNING: deleting no-type %r" % (maybe_class.name, )
440 del self._names.names[maybe_class.name]
443 name = self._resolve_type_name(name)
444 resolved = self._transformer.strip_namespace_object(name)
445 pair_class = self._get_attribute(resolved)
446 if pair_class and isinstance(pair_class,
447 (GLibObject, GLibBoxed, GLibInterface)):
448 for field in maybe_class.fields[1:]:
449 pair_class.fields.append(field)
451 name = self._transformer.strip_namespace_object(maybe_class.name)
452 pair_class = self._get_attribute(name)
453 if pair_class and isinstance(pair_class,
454 (GLibObject, GLibBoxed, GLibInterface)):
456 del self._names.names[maybe_class.name]
460 def _introspect_type(self, type_id, symbol):
461 fundamental_type_id = cgobject.type_fundamental(type_id)
462 if (fundamental_type_id == cgobject.TYPE_ENUM or
463 fundamental_type_id == cgobject.TYPE_FLAGS):
464 self._introspect_enum(fundamental_type_id, type_id, symbol)
465 elif fundamental_type_id == cgobject.TYPE_OBJECT:
466 self._introspect_object(type_id, symbol)
467 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
468 self._introspect_interface(type_id, symbol)
469 elif fundamental_type_id == cgobject.TYPE_BOXED:
470 self._introspect_boxed(type_id, symbol)
471 elif fundamental_type_id == cgobject.TYPE_BOXED:
472 self._introspect_boxed(type_id, symbol)
473 elif fundamental_type_id == cgobject.TYPE_POINTER:
474 # FIXME: Should we do something about these?
475 # GHashTable, GValue and a few other fundamentals are
479 print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
482 def _introspect_enum(self, ftype_id, type_id, symbol):
483 type_class = cgobject.type_class_ref(type_id)
484 if type_class is None:
488 for enum_value in type_class.get_values():
489 members.append(GLibEnumMember(enum_value.value_nick,
491 enum_value.value_name,
492 enum_value.value_nick))
494 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
495 type_name = cgobject.type_name(type_id)
496 enum_name = self._transformer.strip_namespace_object(type_name)
497 node = klass(enum_name, type_name, members, symbol)
498 self._add_attribute(node, replace=True)
499 self._register_internal_type(type_name, node)
501 def _introspect_object(self, type_id, symbol):
502 type_name = cgobject.type_name(type_id)
503 # We handle this specially above; in 2.16 and below there
504 # was no g_object_get_type, for later versions we need
506 if type_name == 'GObject':
508 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
509 parent_gitype = self._resolve_gtypename(parent_type_name)
511 self._transformer.strip_namespace_object(type_name),
514 self._introspect_properties(node, type_id)
515 self._introspect_signals(node, type_id)
516 self._introspect_implemented_interfaces(node, type_id)
517 self._add_attribute(node, replace=True)
518 self._register_internal_type(type_name, node)
520 def _introspect_interface(self, type_id, symbol):
521 type_name = cgobject.type_name(type_id)
522 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
523 if parent_type_name == 'GInterface':
526 parent_gitype = self._resolve_gtypename(parent_type_name)
527 node = GLibInterface(
528 self._transformer.strip_namespace_object(type_name),
531 self._introspect_properties(node, type_id)
532 self._introspect_signals(node, type_id)
533 # GtkFileChooserEmbed is an example of a private interface, we
534 # just filter them out
535 if symbol.startswith('_'):
536 print "NOTICE: Marking %s as internal type" % (type_name, )
537 self._private_internal_types[type_name] = node
539 self._add_attribute(node, replace=True)
540 self._register_internal_type(type_name, node)
542 def _introspect_boxed(self, type_id, symbol):
543 type_name = cgobject.type_name(type_id)
544 node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
546 self._add_attribute(node, replace=True)
547 self._register_internal_type(type_name, node)
549 def _introspect_implemented_interfaces(self, node, type_id):
550 fundamental_type_id = cgobject.type_fundamental(type_id)
551 if fundamental_type_id != cgobject.TYPE_OBJECT:
553 interfaces = cgobject.type_interfaces(type_id)
555 for interface_typeid in interfaces:
556 iname = cgobject.type_name(interface_typeid)
557 gitype = self._resolve_gtypename(iname)
558 gt_interfaces.append(gitype)
559 node.interfaces = gt_interfaces
561 def _introspect_properties(self, node, type_id):
562 fundamental_type_id = cgobject.type_fundamental(type_id)
563 if fundamental_type_id == cgobject.TYPE_OBJECT:
564 pspecs = cgobject.object_class_list_properties(type_id)
565 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
566 pspecs = cgobject.object_interface_list_properties(type_id)
571 if pspec.owner_type != type_id:
573 ctype = cgobject.type_name(pspec.value_type)
574 node.properties.append(Property(
576 type_name_from_ctype(ctype),
580 def _introspect_signals(self, node, type_id):
581 for signal_info in cgobject.signal_list(type_id):
582 rtype = self._create_type(signal_info.return_type)
583 return_ = Return(rtype)
584 signal = GLibSignal(signal_info.signal_name, return_)
585 for i, parameter in enumerate(signal_info.get_params()):
589 name = 'p%s' % (i-1, )
590 ptype = self._create_type(parameter)
591 param = Parameter(name, ptype)
592 signal.parameters.append(param)
593 node.signals.append(signal)
597 def _resolve_type_name(self, type_name, ctype=None):
598 # Workaround glib bug #548689, to be included in 2.18.0
599 if type_name == "GParam":
600 type_name = "GObject.ParamSpec"
602 res = self._transformer.resolve_type_name_full
604 return res(type_name, ctype, self._names)
606 return self._transformer.resolve_type_name(type_name, ctype)
608 def _validate_type_name(self, name):
609 if name in type_names:
611 if name.find('.') >= 0:
613 if name in self._names.aliases:
615 if name in self._names.names:
619 def _validate_type(self, ptype):
620 if isinstance(ptype, Sequence):
621 etype = ptype.element_type
622 if isinstance(etype, Sequence):
623 return self._validate_type(etype)
624 return self._validate_type_name(etype)
625 return self._validate_type_name(ptype.name)
627 def _resolve_param_type_validate(self, ptype):
628 ptype = self._resolve_param_type(ptype)
629 if self._validating and not self._validate_type(ptype):
630 raise UnknownTypeError("Unknown type %r" % (ptype, ))
633 def _resolve_param_type(self, ptype):
635 return self._transformer.resolve_param_type_full(ptype,
638 return self._transformer.resolve_param_type(ptype)
641 def _resolve_node(self, node):
642 if isinstance(node, Function):
643 self._resolve_function_toplevel(node)
645 elif isinstance(node, Callback):
646 self._resolve_function(node)
647 elif isinstance(node, GLibObject):
648 self._resolve_glib_object(node)
649 elif isinstance(node, GLibInterface):
650 self._resolve_glib_interface(node)
651 elif isinstance(node, GLibBoxed):
652 self._resolve_glib_boxed(node)
653 elif isinstance(node, Struct):
654 self._resolve_struct(node)
655 elif isinstance(node, Union):
656 self._resolve_union(node)
657 elif isinstance(node, Alias):
658 self._resolve_alias(node)
660 def _resolve_function_toplevel(self, func):
661 newfunc = self._parse_constructor(func)
663 newfunc = self._parse_method(func)
665 self._resolve_function(func)
667 self._resolve_function(newfunc)
669 def _resolve_struct(self, node):
670 for field in node.fields:
671 self._resolve_field(field)
673 def _resolve_union(self, node):
674 for field in node.fields:
675 self._resolve_field(field)
677 def _force_resolve(self, item, allow_unknown=False):
678 if isinstance(item, Unresolved):
679 if item.target in self._private_internal_types:
682 return self._transformer.gtypename_to_giname(item.target,
686 print "WARNING: Skipping unknown interface %s" % \
691 if item in self._private_internal_types:
695 def _resolve_glib_interface(self, node):
696 node.parent = self._force_resolve(node.parent)
697 self._resolve_methods(node.methods)
698 self._resolve_properties(node.properties)
699 self._resolve_signals(node.signals)
701 def _resolve_glib_object(self, node):
702 node.parent = self._force_resolve(node.parent)
703 node.interfaces = filter(None,
704 [self._force_resolve(x, allow_unknown=True)
705 for x in node.interfaces])
706 self._resolve_constructors(node.constructors)
707 self._resolve_methods(node.methods)
708 self._resolve_properties(node.properties)
709 self._resolve_signals(node.signals)
711 def _resolve_glib_boxed(self, node):
712 self._resolve_constructors(node.constructors)
713 self._resolve_methods(node.methods)
715 def _resolve_constructors(self, constructors):
716 for ctor in constructors:
717 self._resolve_function(ctor)
719 def _resolve_methods(self, methods):
720 for method in methods:
721 self._resolve_function(method)
723 def _resolve_signals(self, signals):
724 for signal in signals:
725 self._resolve_function(signal)
727 def _resolve_properties(self, properties):
728 for prop in properties:
729 self._resolve_property(prop)
731 def _resolve_property(self, prop):
732 prop.type = self._resolve_param_type(prop.type)
734 def _resolve_function(self, func):
735 self._resolve_parameters(func.parameters)
736 func.retval.type = self._resolve_param_type(func.retval.type)
738 def _resolve_parameters(self, parameters):
739 for parameter in parameters:
740 parameter.type = self._resolve_param_type(parameter.type)
742 def _resolve_field(self, field):
743 if isinstance(field, Callback):
744 self._resolve_function(field)
746 field.type = self._resolve_param_type(field.type)
748 def _resolve_alias(self, alias):
749 alias.target = self._resolve_type_name(alias.target, alias.target)
753 def _validate(self, nodes):
754 nodes = list(self._names.names.itervalues())
756 self._validating = True
760 print "Type resolution; pass=%d" % (i, )
761 nodes = list(self._names.names.itervalues())
764 self._resolve_node(node)
765 except UnknownTypeError, e:
766 print "WARNING: %s: Deleting %r" % (e, node)
767 self._remove_attribute(node.name)
768 if len(nodes) == initlen:
771 self._print_statistics()
772 self._validating = False