Fix inconsistencies with PEP8
[gnome.gobject-introspection] / giscanner / glibtransformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library 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 GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import os
22 import sys
23 import re
24 import tempfile
25 import shutil
26 import subprocess
27
28 from .ast import (Callback, Constant, Enum, Function, Member, Namespace,
29                   Parameter, Property, Return, Struct, Type, Alias,
30                   Union, Field, type_name_from_ctype,
31                   default_array_types, TYPE_UINT8, PARAM_DIRECTION_IN)
32 from .transformer import Names
33 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
34                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
35                       GLibBoxedUnion, GLibBoxedOther, type_names)
36 from .utils import to_underscores, to_underscores_noprefix
37
38 default_array_types['guchar*'] = TYPE_UINT8
39
40 # GParamFlags
41 G_PARAM_READABLE = 1 << 0
42 G_PARAM_WRITABLE = 1 << 1
43 G_PARAM_CONSTRUCT = 1 << 2
44 G_PARAM_CONSTRUCT_ONLY = 1 << 3
45 G_PARAM_LAX_VALIDATION = 1 << 4
46 G_PARAM_STATIC_NAME = 1 << 5
47 G_PARAM_STATIC_NICK = 1 << 6
48 G_PARAM_STATIC_BLURB = 1 << 7
49
50 SYMBOL_BLACKLIST = [
51     # These ones break GError conventions
52     'g_simple_async_result_new_from_error',
53     'g_simple_async_result_set_from_error',
54     'g_simple_async_result_propagate_error',
55     'g_simple_async_result_report_error_in_idle',
56     'gtk_print_operation_get_error',
57 ]
58
59 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
60                            [r'\w+_marshal_[A-Z]+__', ]]
61
62
63 class IntrospectionBinary(object):
64
65     def __init__(self, args, tmpdir=None):
66         self.args = args
67         if tmpdir is None:
68             self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
69         else:
70             self.tmpdir = tmpdir
71
72
73 class Unresolved(object):
74
75     def __init__(self, target):
76         self.target = target
77
78
79 class UnknownTypeError(Exception):
80     pass
81
82
83 class GLibTransformer(object):
84
85     def __init__(self, transformer, noclosure=False):
86         self._transformer = transformer
87         self._noclosure = noclosure
88         self._transformer.set_container_types(['GList*', 'GSList*'],
89                                               ['GHashTable*'])
90         self._namespace_name = None
91         self._names = Names()
92         self._uscore_type_names = {}
93         self._binary = None
94         self._get_type_functions = []
95         self._gtype_data = {}
96         self._failed_types = {}
97         self._boxed_types = {}
98         self._private_internal_types = {}
99         self._validating = False
100
101     # Public API
102     def set_introspection_binary(self, binary):
103         self._binary = binary
104
105     def _print_statistics(self):
106         nodes = list(self._names.names.itervalues())
107
108         def count_type(otype):
109             return len([x for x in nodes
110                         if isinstance(x[1], otype)])
111         objectcount = count_type(GLibObject)
112         ifacecount = count_type(GLibInterface)
113         enumcount = count_type(GLibEnum)
114         print " %d nodes; %d objects, %d interfaces, %d enums" \
115             % (len(nodes), objectcount, ifacecount, enumcount)
116
117     def parse(self):
118         namespace = self._transformer.parse()
119         self._namespace_name = namespace.name
120
121         # First pass: parsing
122         for node in namespace.nodes:
123             self._parse_node(node)
124
125         # We don't want an alias for this - it's handled specially in
126         # the typelib compiler.
127         if namespace.name == 'GObject':
128             del self._names.aliases['Type']
129
130         # Get all the GObject data by passing our list of get_type
131         # functions to the compiled binary
132
133         self._execute_binary()
134
135         # Introspection is done from within parsing
136
137         # Second pass: pair boxed structures
138         for boxed in self._boxed_types.itervalues():
139             self._pair_boxed_type(boxed)
140         # Third pass: delete class structures, resolve
141         # all types we now know about
142         nodes = list(self._names.names.itervalues())
143         for (ns, node) in nodes:
144             try:
145                 self._resolve_node(node)
146             except KeyError, e:
147                 print "WARNING: DELETING node %s: %s" % (node.name, e)
148                 self._remove_attribute(node.name)
149             # associate GtkButtonClass with GtkButton
150             if isinstance(node, Struct):
151                 self._pair_class_struct(node)
152         for (ns, alias) in self._names.aliases.itervalues():
153             self._resolve_alias(alias)
154
155         self._print_statistics()
156         # Fourth pass: ensure all types are known
157         if not self._noclosure:
158             self._validate(nodes)
159
160         # Create a new namespace with what we found
161         namespace = Namespace(namespace.name, namespace.version)
162         namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
163         for (ns, x) in self._names.names.itervalues():
164             namespace.nodes.append(x)
165         return namespace
166
167     # Private
168     def _add_attribute(self, node, replace=False):
169         node_name = node.name
170         if (not replace) and node_name in self._names.names:
171             return
172         self._names.names[node_name] = (None, node)
173
174     def _remove_attribute(self, name):
175         del self._names.names[name]
176
177     def _get_attribute(self, name):
178         node = self._names.names.get(name)
179         if node:
180             return node[1]
181         return None
182
183     def _lookup_node(self, name):
184         if name in type_names:
185             return None
186         node = self._get_attribute(name)
187         if node is None:
188             node = self._transformer.get_names().names.get(name)
189             if node:
190                 return node[1]
191         return node
192
193     def _register_internal_type(self, type_name, node):
194         self._names.type_names[type_name] = (None, node)
195         uscored = to_underscores(type_name).lower()
196         self._uscore_type_names[uscored] = node
197         # Besides the straight underscore conversion, we also try
198         # removing the underscores from the namespace as a possible C
199         # mapping; e.g. it's webkit_web_view, not web_kit_web_view
200         suffix = self._transformer.remove_prefix(type_name)
201         prefix = type_name[:-len(suffix)]
202         no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
203         self._uscore_type_names[no_uscore_prefixed] = node
204
205     # Helper functions
206     def _resolve_gtypename(self, gtype_name):
207         try:
208             return self._transformer.gtypename_to_giname(gtype_name,
209                                                          self._names)
210         except KeyError, e:
211             return Unresolved(gtype_name)
212
213     def _execute_binary(self):
214         in_path = os.path.join(self._binary.tmpdir, 'types.txt')
215         f = open(in_path, 'w')
216         for func in self._get_type_functions:
217             f.write(func)
218             f.write('\n')
219         f.close()
220         out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
221
222         args = []
223         args.extend(self._binary.args)
224         args.append('--introspect-dump=%s,%s' % (in_path, out_path))
225
226         # Invoke the binary, having written our get_type functions to types.txt
227         subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
228         self._read_introspect_dump(out_path)
229
230         # Clean up temporaries
231         shutil.rmtree(self._binary.tmpdir)
232
233     def _read_introspect_dump(self, xmlpath):
234         from xml.etree.cElementTree import parse
235         tree = parse(xmlpath)
236         root = tree.getroot()
237         for child in root:
238             self._gtype_data[child.attrib['name']] = child
239         for child in root:
240             self._introspect_type(child)
241
242     def _create_gobject(self, node):
243         type_name = 'G' + node.name
244         if type_name == 'GObject':
245             parent_gitype = None
246             symbol = 'intern'
247         elif type_name == 'GInitiallyUnowned':
248             parent_type_name = 'GObject'
249             parent_gitype = self._resolve_gtypename(parent_type_name)
250             symbol = 'g_initially_unowned_get_type'
251         node = GLibObject(node.name, parent_gitype, type_name, symbol, True)
252         self._add_attribute(node)
253         self._register_internal_type(type_name, node)
254
255     # Parser
256     def _parse_node(self, node):
257         if isinstance(node, Enum):
258             self._parse_enum(node)
259         elif isinstance(node, Function):
260             self._parse_function(node)
261         elif isinstance(node, Struct):
262             self._parse_struct(node)
263         elif isinstance(node, Callback):
264             self._parse_callback(node)
265         elif isinstance(node, Alias):
266             self._parse_alias(node)
267         elif isinstance(node, Member):
268             # FIXME: atk_misc_instance singletons
269             pass
270         elif isinstance(node, Union):
271             self._parse_union(node)
272         elif isinstance(node, Constant):
273             self._parse_constant(node)
274         else:
275             print 'GLIB Transformer: Unhandled node:', node
276
277     def _parse_alias(self, alias):
278         self._names.aliases[alias.name] = (None, alias)
279
280     def _parse_enum(self, enum):
281         self._add_attribute(enum)
282
283     def _parse_constant(self, constant):
284         self._add_attribute(constant)
285
286     def _parse_function(self, func):
287         if func.symbol in SYMBOL_BLACKLIST:
288             return
289         if func.symbol.startswith('_'):
290             return
291         for regexp in SYMBOL_BLACKLIST_RE:
292             if regexp.match(func.symbol):
293                 return
294         if self._parse_get_type_function(func):
295             return
296
297         self._add_attribute(func)
298
299     def _parse_get_type_function(self, func):
300         symbol = func.symbol
301         if not symbol.endswith('_get_type'):
302             return False
303         if self._namespace_name == 'GLib':
304             # No GObjects in GLib
305             return False
306         if func.parameters:
307             return False
308         # GType *_get_type(void)
309         if func.retval.type.name not in ['Type',
310                                          'GType',
311                                          'GObject.Type',
312                                          'Gtk.Type']:
313             print ("Warning: *_get_type function returns '%r'"
314                    ", not GObject.Type") % (func.retval.type.name, )
315             return False
316
317         self._get_type_functions.append(symbol)
318         return True
319
320     def _name_is_internal_gtype(self, giname):
321         try:
322             node = self._get_attribute(giname)
323             return isinstance(node, (GLibObject, GLibInterface,
324                                      GLibBoxed, GLibEnum, GLibFlags))
325         except KeyError, e:
326             return False
327
328     def _parse_static_method(self, func):
329         components = func.symbol.split('_')
330         if len(components) < 2:
331             return None
332         target_klass = None
333         prefix_components = None
334         methname = None
335         for i in xrange(1, len(components)-1):
336             prefix_components = '_'.join(components[0:-i])
337             methname = '_'.join(components[-i:])
338             target_klass = self._uscore_type_names.get(prefix_components)
339             if target_klass and isinstance(target_klass, GLibObject):
340                 break
341             target_klass = None
342         if not target_klass:
343             return None
344         self._remove_attribute(func.name)
345         func.name = methname
346         target_klass.static_methods.append(func)
347         return func
348
349     def _parse_method(self, func):
350         if not func.parameters:
351             return False
352         return self._parse_method_common(func, True)
353
354     def _parse_constructor(self, func):
355         return self._parse_method_common(func, False)
356
357     def _parse_method_common(self, func, is_method):
358         # Skip _get_type functions, we processed them
359         # already
360         if func.symbol.endswith('_get_type'):
361             return None
362         if self._namespace_name == 'GLib':
363             # No GObjects in GLib
364             return None
365
366         if not is_method:
367             target_arg = func.retval
368         else:
369             target_arg = func.parameters[0]
370
371         if is_method:
372             # Methods require their first arg to be a known class
373             # Look at the original C type (before namespace stripping), without
374             # pointers: GtkButton -> gtk_button_, so we can figure out the
375             # method name
376             argtype = target_arg.type.ctype.replace('*', '')
377             name = self._transformer.remove_prefix(argtype)
378             name_uscore = to_underscores_noprefix(name).lower()
379             name_offset = func.symbol.find(name_uscore)
380             if name_offset < 0:
381                 return None
382             prefix = func.symbol[:name_offset+len(name_uscore)]
383         else:
384             # Constructors must have _new
385             # Take everything before that as class name
386             new_idx = func.symbol.find('_new')
387             if new_idx < 0:
388                 return None
389             # Constructors don't return basic types
390             derefed = self._transformer.follow_aliases(target_arg.type.name,
391                                                        self._names)
392             if derefed in type_names:
393                 #print "NOTE: Rejecting constructor returning basic: %r" \
394                 #    % (func.symbol, )
395                 return None
396             prefix = func.symbol[:new_idx]
397
398         klass = self._uscore_type_names.get(prefix)
399         if klass is None:
400             #print "NOTE: No valid matching class for likely "+\
401             #    "method or constructor: %r" % (func.symbol, )
402             return None
403         # Enums can't have ctors or methods
404         if isinstance(klass, (GLibEnum, GLibFlags)):
405             return None
406
407         # The _uscore_type_names member holds the plain GLibBoxed
408         # object; we want to actually use the struct/record associated
409         if isinstance(klass, GLibBoxed):
410             name = self._transformer.remove_prefix(klass.type_name)
411             klass = self._get_attribute(name)
412
413         if not is_method:
414             # Interfaces can't have constructors, punt to global scope
415             if isinstance(klass, GLibInterface):
416                 #print "NOTE: Rejecting constructor for"+\
417                 #    " interface type: %r" % (func.symbol, )
418                 return None
419             # TODO - check that the return type is a subclass of the
420             # class from the prefix
421             # But for now, ensure that constructor returns are always
422             # the most concrete class
423             name = self._transformer.remove_prefix(klass.type_name)
424             func.retval.type = Type(name, func.retval.type.ctype)
425
426         self._remove_attribute(func.name)
427         # Strip namespace and object prefix: gtk_window_new -> new
428         func.name = func.symbol[len(prefix)+1:]
429         if is_method:
430             # We don't need the "this" parameter
431             del func.parameters[0]
432             klass.methods.append(func)
433         else:
434             klass.constructors.append(func)
435         return func
436
437     def _parse_struct(self, struct):
438         # This is a hack, but GObject is a rather fundamental piece so.
439         internal_names = ["Object", 'InitiallyUnowned']
440         g_internal_names = ["G" + x for x in internal_names]
441         if (self._namespace_name == 'GObject' and
442             struct.name in internal_names):
443             self._create_gobject(struct)
444             return
445         elif struct.name in g_internal_names:
446             # Avoid duplicates
447             return
448         node = self._names.names.get(struct.name)
449         if node is None:
450             self._add_attribute(struct, replace=True)
451             return
452         (ns, node) = node
453         node.fields = struct.fields[:]
454
455     def _parse_union(self, union):
456         node = self._names.names.get(union.name)
457         if node is None:
458             self._add_attribute(union, replace=True)
459             return
460         (ns, node) = node
461         node.fields = union.fields[:]
462
463     def _parse_callback(self, callback):
464         self._add_attribute(callback)
465
466     def _strip_class_suffix(self, name):
467         if (name.endswith('Class') or
468             name.endswith('Iface')):
469             return name[:-5]
470         elif name.endswith('Interface'):
471             return name[:-9]
472         else:
473             return name
474
475     def _arg_is_failed(self, param):
476         ctype = self._transformer.ctype_of(param).replace('*', '')
477         uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
478         if uscored in self._failed_types:
479             print "Warning: failed type: %r" % (param, )
480             return True
481         return False
482
483     def _pair_class_struct(self, maybe_class):
484         name = self._strip_class_suffix(maybe_class.name)
485         if name == maybe_class.name:
486             return
487
488         if self._arg_is_failed(maybe_class):
489             print "WARNING: deleting no-type %r" % (maybe_class.name, )
490             del self._names.names[maybe_class.name]
491             return
492
493         # Object class fields are assumed to be read-only
494         # (see also _introspect_object and transformer.py)
495         for field in maybe_class.fields:
496             if isinstance(field, Field):
497                 field.writable = False
498
499         name = self._resolve_type_name(name)
500         resolved = self._transformer.remove_prefix(name)
501         pair_class = self._get_attribute(resolved)
502         if pair_class and isinstance(pair_class,
503                                      (GLibObject, GLibInterface)):
504             for field in maybe_class.fields[1:]:
505                 pair_class.fields.append(field)
506             return
507         name = self._transformer.remove_prefix(maybe_class.name)
508         pair_class = self._get_attribute(name)
509         if pair_class and isinstance(pair_class,
510                                      (GLibObject, GLibInterface)):
511
512             del self._names.names[maybe_class.name]
513
514     # Introspection
515     def _introspect_type(self, xmlnode):
516         if xmlnode.tag in ('enum', 'flags'):
517             self._introspect_enum(xmlnode)
518         elif xmlnode.tag == 'class':
519             self._introspect_object(xmlnode)
520         elif xmlnode.tag == 'interface':
521             self._introspect_interface(xmlnode)
522         elif xmlnode.tag == 'boxed':
523             self._introspect_boxed(xmlnode)
524         else:
525             raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
526
527     def _introspect_enum(self, node):
528         members = []
529         for member in node.findall('member'):
530             members.append(GLibEnumMember(member.attrib['nick'],
531                                           member.attrib['value'],
532                                           member.attrib['name'],
533                                           member.attrib['nick']))
534
535         klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
536         type_name = node.attrib['name']
537         enum_name = self._transformer.remove_prefix(type_name)
538         node = klass(enum_name, type_name, members, node.attrib['get-type'])
539         self._add_attribute(node, replace=True)
540         self._register_internal_type(type_name, node)
541
542     def _introspect_object(self, xmlnode):
543         type_name = xmlnode.attrib['name']
544         # We handle this specially above; in 2.16 and below there
545         # was no g_object_get_type, for later versions we need
546         # to skip it
547         if type_name == 'GObject':
548             return
549         parent_type_name = xmlnode.attrib['parent']
550         parent_gitype = self._resolve_gtypename(parent_type_name)
551         is_abstract = not not xmlnode.attrib.get('abstract', False)
552         node = GLibObject(
553             self._transformer.remove_prefix(type_name),
554             parent_gitype,
555             type_name,
556             xmlnode.attrib['get-type'], is_abstract)
557         self._introspect_properties(node, xmlnode)
558         self._introspect_signals(node, xmlnode)
559         self._introspect_implemented_interfaces(node, xmlnode)
560
561         # add struct fields
562         struct = self._get_attribute(node.name)
563         if struct is not None:
564             node.fields = struct.fields
565             for field in node.fields:
566                 if isinstance(field, Field):
567                     # Object instance fields are assumed to be read-only
568                     # (see also _pair_class_struct and transformer.py)
569                     field.writable = False
570
571         self._add_attribute(node, replace=True)
572         self._register_internal_type(type_name, node)
573
574     def _introspect_interface(self, xmlnode):
575         type_name = xmlnode.attrib['name']
576         node = GLibInterface(
577             self._transformer.remove_prefix(type_name),
578             None,
579             type_name, xmlnode.attrib['get-type'])
580         self._introspect_properties(node, xmlnode)
581         self._introspect_signals(node, xmlnode)
582         for child in xmlnode.findall('prerequisite'):
583             name = child.attrib['name']
584             prereq = self._resolve_gtypename(name)
585             node.prerequisites.append(prereq)
586         # GtkFileChooserEmbed is an example of a private interface, we
587         # just filter them out
588         if xmlnode.attrib['get-type'].startswith('_'):
589             print "NOTICE: Marking %s as internal type" % (type_name, )
590             self._private_internal_types[type_name] = node
591         else:
592             self._add_attribute(node, replace=True)
593             self._register_internal_type(type_name, node)
594
595     def _introspect_boxed(self, xmlnode):
596         type_name = xmlnode.attrib['name']
597         # This one doesn't go in the main namespace; we associate it with
598         # the struct or union
599         node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
600         self._boxed_types[node.type_name] = node
601         self._register_internal_type(type_name, node)
602
603     def _introspect_implemented_interfaces(self, node, xmlnode):
604         gt_interfaces = []
605         for interface in xmlnode.findall('implements'):
606             gitype = self._resolve_gtypename(interface.attrib['name'])
607             gt_interfaces.append(gitype)
608         node.interfaces = gt_interfaces
609
610     def _introspect_properties(self, node, xmlnode):
611         for pspec in xmlnode.findall('property'):
612             ctype = pspec.attrib['type']
613             flags = int(pspec.attrib['flags'])
614             readable = (flags & G_PARAM_READABLE) != 0
615             writable = (flags & G_PARAM_WRITABLE) != 0
616             construct = (flags & G_PARAM_CONSTRUCT) != 0
617             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
618             node.properties.append(Property(
619                 pspec.attrib['name'],
620                 type_name_from_ctype(ctype),
621                 readable, writable, construct, construct_only,
622                 ctype,
623                 ))
624
625     def _introspect_signals(self, node, xmlnode):
626         for signal_info in xmlnode.findall('signal'):
627             rctype = signal_info.attrib['return']
628             rtype = Type(self._transformer.parse_ctype(rctype), rctype)
629             return_ = Return(rtype, signal_info.attrib['return'])
630             return_.transfer = 'full'
631             signal = GLibSignal(signal_info.attrib['name'], return_)
632             for i, parameter in enumerate(signal_info.findall('param')):
633                 if i == 0:
634                     name = 'object'
635                 else:
636                     name = 'p%s' % (i-1, )
637                 pctype = parameter.attrib['type']
638                 ptype = Type(self._transformer.parse_ctype(pctype), pctype)
639                 param = Parameter(name, ptype)
640                 param.transfer = 'none'
641                 signal.parameters.append(param)
642             node.signals.append(signal)
643
644     # Resolver
645     def _resolve_type_name(self, type_name, ctype=None):
646         # Workaround glib bug #548689, to be included in 2.18.0
647         if type_name == "GParam":
648             type_name = "GObject.ParamSpec"
649         res = self._transformer.resolve_type_name_full
650         try:
651             return res(type_name, ctype, self._names)
652         except KeyError, e:
653             return self._transformer.resolve_type_name(type_name, ctype)
654
655     def _resolve_param_type(self, ptype, **kwargs):
656         # Workaround glib bug #548689, to be included in 2.18.0
657         if ptype.name == "GParam":
658             ptype.name = "GObject.ParamSpec"
659         return self._transformer.resolve_param_type_full(ptype,
660                                                          self._names,
661                                                          **kwargs)
662
663     def _resolve_node(self, node):
664         if isinstance(node, Function):
665             self._resolve_function_toplevel(node)
666
667         elif isinstance(node, Callback):
668             self._resolve_function(node)
669         elif isinstance(node, GLibObject):
670             self._resolve_glib_object(node)
671         elif isinstance(node, GLibInterface):
672             self._resolve_glib_interface(node)
673         elif isinstance(node, Struct):
674             self._resolve_struct(node)
675         elif isinstance(node, Union):
676             self._resolve_union(node)
677         elif isinstance(node, Alias):
678             self._resolve_alias(node)
679
680     def _resolve_function_toplevel(self, func):
681         for parser in [self._parse_constructor,
682                        self._parse_method,
683                        self._parse_static_method]:
684             newfunc = parser(func)
685             if newfunc:
686                 self._resolve_function(newfunc)
687                 return
688         self._resolve_function(func)
689
690     def _pair_boxed_type(self, boxed):
691         name = self._transformer.remove_prefix(boxed.type_name)
692         pair_node = self._get_attribute(name)
693         if not pair_node:
694             boxed_item = GLibBoxedOther(name, boxed.type_name,
695                                         boxed.get_type)
696         elif isinstance(pair_node, Struct):
697             boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
698                                          boxed.get_type)
699             boxed_item.fields = pair_node.fields
700         elif isinstance(pair_node, Union):
701             boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
702                                          boxed.get_type)
703             boxed_item.fields = pair_node.fields
704         else:
705             return False
706         self._add_attribute(boxed_item, replace=True)
707
708     def _resolve_struct(self, node):
709         for field in node.fields:
710             self._resolve_field(field)
711
712     def _resolve_union(self, node):
713         for field in node.fields:
714             self._resolve_field(field)
715
716     def _force_resolve(self, item, allow_unknown=False):
717         if isinstance(item, Unresolved):
718             if item.target in self._private_internal_types:
719                 return None
720             try:
721                 return self._transformer.gtypename_to_giname(item.target,
722                                                              self._names)
723             except KeyError, e:
724                 if allow_unknown:
725                     print "WARNING: Skipping unknown interface %s" % \
726                         (item.target, )
727                     return None
728                 else:
729                     raise
730         if item in self._private_internal_types:
731             return None
732         return item
733
734     def _resolve_glib_interface(self, node):
735         node.parent = self._force_resolve(node.parent)
736         self._resolve_methods(node.methods)
737         self._resolve_properties(node.properties, node)
738         self._resolve_signals(node.signals)
739         node.prerequisites = filter(None,
740             [self._force_resolve(x, allow_unknown=True)
741              for x in node.prerequisites])
742
743     def _resolve_glib_object(self, node):
744         node.parent = self._force_resolve(node.parent)
745         node.interfaces = filter(None,
746             [self._force_resolve(x, allow_unknown=True)
747                                     for x in node.interfaces])
748         self._resolve_constructors(node.constructors)
749         self._resolve_methods(node.methods)
750         self._resolve_methods(node.static_methods)
751         self._resolve_properties(node.properties, node)
752         self._resolve_signals(node.signals)
753         for field in node.fields:
754             self._resolve_field(field)
755
756     def _resolve_glib_boxed(self, node):
757         self._resolve_constructors(node.constructors)
758         self._resolve_methods(node.methods)
759
760     def _resolve_constructors(self, constructors):
761         for ctor in constructors:
762             self._resolve_function(ctor)
763
764     def _resolve_methods(self, methods):
765         for method in methods:
766             self._resolve_function(method)
767
768     def _resolve_signals(self, signals):
769         for signal in signals:
770             self._resolve_function(signal)
771
772     def _resolve_properties(self, properties, context):
773         failed = []
774         for prop in properties:
775             try:
776                 self._resolve_property(prop)
777             except KeyError, e:
778                 failed.append(prop)
779         for fail in failed:
780             print ("WARNING: Deleting object property %r (of %r) "
781                    "with unknown type") % (fail, context)
782             properties.remove(fail)
783
784     def _resolve_property(self, prop):
785         prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
786
787     def _adjust_transfer(self, param):
788         if not (param.transfer is None or param.transfer_inferred):
789             return
790
791         # Do GLib/GObject-specific type transformations here
792         node = self._lookup_node(param.type.name)
793         if node is None:
794             return
795
796         if isinstance(param, Parameter):
797             if param.direction != PARAM_DIRECTION_IN:
798                 transfer = 'full'
799             else:
800                 transfer = 'none'
801         else:
802             transfer = 'full'
803
804         param.transfer = transfer
805
806     def _adjust_throws(self, func):
807         if func.parameters == []:
808             return
809
810         last_param = func.parameters.pop()
811
812         if (last_param.type.name == 'GLib.Error' or
813             (self._namespace_name == 'GLib' and
814              last_param.type.name == 'Error')):
815             func.throws = True
816         else:
817             func.parameters.append(last_param)
818
819     def _resolve_function(self, func):
820         self._resolve_parameters(func.parameters)
821         func.retval.type = self._resolve_param_type(func.retval.type)
822         self._adjust_throws(func)
823         self._adjust_transfer(func.retval)
824
825     def _resolve_parameters(self, parameters):
826         for parameter in parameters:
827             parameter.type = self._resolve_param_type(parameter.type)
828             self._adjust_transfer(parameter)
829
830     def _resolve_field(self, field):
831         if isinstance(field, Callback):
832             self._resolve_function(field)
833             return
834         field.type = self._resolve_param_type(field.type)
835
836     def _resolve_alias(self, alias):
837         alias.target = self._resolve_type_name(alias.target, alias.target)
838
839     # Validation
840     def _validate(self, nodes):
841         nodes = list(self._names.names.itervalues())
842         i = 0
843         self._validating = True
844         while True:
845             initlen = len(nodes)
846
847             print "Type resolution; pass=%d" % (i, )
848             nodes = list(self._names.names.itervalues())
849             for node in nodes:
850                 try:
851                     self._resolve_node(node)
852                 except UnknownTypeError, e:
853                     print "WARNING: %s: Deleting %r" % (e, node)
854                     self._remove_attribute(node.name)
855             if len(nodes) == initlen:
856                 break
857             i += 1
858             self._print_statistics()
859         self._validating = False