2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Johan Dahlin
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.
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.
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.
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
38 default_array_types['guchar*'] = TYPE_UINT8
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
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',
59 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
60 [r'\w+_marshal_[A-Z]+__', ]]
63 class IntrospectionBinary(object):
65 def __init__(self, args, tmpdir=None):
68 self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
73 class Unresolved(object):
75 def __init__(self, target):
79 class UnknownTypeError(Exception):
83 class GLibTransformer(object):
85 def __init__(self, transformer, noclosure=False):
86 self._transformer = transformer
87 self._transformer.set_container_types(['GList*', 'GSList*'],
89 self._namespace_name = None
91 self._uscore_type_names = {}
93 self._get_type_functions = []
95 self._failed_types = {}
96 self._boxed_types = {}
97 self._private_internal_types = {}
98 self._noclosure = noclosure
99 self._validating = False
103 def set_introspection_binary(self, binary):
104 self._binary = binary
106 def _print_statistics(self):
107 nodes = list(self._names.names.itervalues())
109 def count_type(otype):
110 return len([x for x in nodes
111 if isinstance(x[1], otype)])
112 objectcount = count_type(GLibObject)
113 ifacecount = count_type(GLibInterface)
114 enumcount = count_type(GLibEnum)
115 print " %d nodes; %d objects, %d interfaces, %d enums" \
116 % (len(nodes), objectcount, ifacecount, enumcount)
119 namespace = self._transformer.parse()
120 self._namespace_name = namespace.name
122 # First pass: parsing
123 for node in namespace.nodes:
124 self._parse_node(node)
126 # We don't want an alias for this - it's handled specially in
127 # the typelib compiler.
128 if namespace.name == 'GObject':
129 del self._names.aliases['Type']
131 # Get all the GObject data by passing our list of get_type
132 # functions to the compiled binary
134 self._execute_binary()
136 # Introspection is done from within parsing
138 # Second pass: pair boxed structures
139 for boxed in self._boxed_types.itervalues():
140 self._pair_boxed_type(boxed)
141 # Third pass: delete class structures, resolve
142 # all types we now know about
143 nodes = list(self._names.names.itervalues())
144 for (ns, node) in nodes:
146 self._resolve_node(node)
148 print "WARNING: DELETING node %s: %s" % (node.name, e)
149 self._remove_attribute(node.name)
150 # associate GtkButtonClass with GtkButton
151 if isinstance(node, Struct):
152 self._pair_class_struct(node)
153 for (ns, alias) in self._names.aliases.itervalues():
154 self._resolve_alias(alias)
156 self._print_statistics()
157 # Fourth pass: ensure all types are known
158 if not self._noclosure:
159 self._validate(nodes)
161 # Create a new namespace with what we found
162 namespace = Namespace(namespace.name, namespace.version)
163 namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
164 for (ns, x) in self._names.names.itervalues():
165 namespace.nodes.append(x)
170 def _add_attribute(self, node, replace=False):
171 node_name = node.name
172 if (not replace) and node_name in self._names.names:
174 self._names.names[node_name] = (None, node)
176 def _remove_attribute(self, name):
177 del self._names.names[name]
179 def _get_attribute(self, name):
180 node = self._names.names.get(name)
185 def _lookup_node(self, name):
186 if name in type_names:
188 node = self._get_attribute(name)
190 node = self._transformer.get_names().names.get(name)
195 def _register_internal_type(self, type_name, node):
196 self._names.type_names[type_name] = (None, node)
197 uscored = to_underscores(type_name).lower()
198 self._uscore_type_names[uscored] = node
199 # Besides the straight underscore conversion, we also try
200 # removing the underscores from the namespace as a possible C
201 # mapping; e.g. it's webkit_web_view, not web_kit_web_view
202 suffix = self._transformer.remove_prefix(type_name)
203 prefix = type_name[:-len(suffix)]
204 no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
205 self._uscore_type_names[no_uscore_prefixed] = node
209 def _resolve_gtypename(self, gtype_name):
211 return self._transformer.gtypename_to_giname(gtype_name,
214 return Unresolved(gtype_name)
216 def _execute_binary(self):
217 in_path = os.path.join(self._binary.tmpdir, 'types.txt')
218 f = open(in_path, 'w')
219 for func in self._get_type_functions:
223 out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
225 introspect_arg = '--introspect-dump=%s,%s' % (in_path, out_path)
226 args = self._binary.args
227 args.append(introspect_arg)
228 # Invoke the binary, having written our get_type functions to types.txt
230 subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
231 self._read_introspect_dump(out_path)
233 # Clean up temporaries
234 shutil.rmtree(self._binary.tmpdir)
236 def _read_introspect_dump(self, xmlpath):
237 from xml.etree.cElementTree import parse
238 tree = parse(xmlpath)
239 root = tree.getroot()
241 self._gtype_data[child.attrib['name']] = child
243 self._introspect_type(child)
245 def _create_gobject(self, node):
246 type_name = 'G' + node.name
247 if type_name == 'GObject':
250 elif type_name == 'GInitiallyUnowned':
251 parent_type_name = 'GObject'
252 parent_gitype = self._resolve_gtypename(parent_type_name)
253 symbol = 'g_initially_unowned_get_type'
254 node = GLibObject(node.name, parent_gitype, type_name, symbol, True)
255 self._add_attribute(node)
256 self._register_internal_type(type_name, node)
260 def _parse_node(self, node):
261 if isinstance(node, Enum):
262 self._parse_enum(node)
263 elif isinstance(node, Function):
264 self._parse_function(node)
265 elif isinstance(node, Struct):
266 self._parse_struct(node)
267 elif isinstance(node, Callback):
268 self._parse_callback(node)
269 elif isinstance(node, Alias):
270 self._parse_alias(node)
271 elif isinstance(node, Member):
272 # FIXME: atk_misc_instance singletons
274 elif isinstance(node, Union):
275 self._parse_union(node)
276 elif isinstance(node, Constant):
277 self._parse_constant(node)
279 print 'GLIB Transformer: Unhandled node:', node
281 def _parse_alias(self, alias):
282 self._names.aliases[alias.name] = (None, alias)
284 def _parse_enum(self, enum):
285 self._add_attribute(enum)
287 def _parse_constant(self, constant):
288 self._add_attribute(constant)
290 def _parse_function(self, func):
291 if func.symbol in SYMBOL_BLACKLIST:
293 if func.symbol.startswith('_'):
295 for regexp in SYMBOL_BLACKLIST_RE:
296 if regexp.match(func.symbol):
298 if self._parse_get_type_function(func):
301 self._add_attribute(func)
303 def _parse_get_type_function(self, func):
305 if not symbol.endswith('_get_type'):
307 if self._namespace_name == 'GLib':
308 # No GObjects in GLib
312 # GType *_get_type(void)
313 if func.retval.type.name not in ['Type',
317 print ("Warning: *_get_type function returns '%r'"
318 ", not GObject.Type") % (func.retval.type.name, )
321 self._get_type_functions.append(symbol)
324 def _name_is_internal_gtype(self, giname):
326 node = self._get_attribute(giname)
327 return isinstance(node, (GLibObject, GLibInterface,
328 GLibBoxed, GLibEnum, GLibFlags))
332 def _parse_method(self, func):
333 if not func.parameters:
335 return self._parse_method_common(func, True)
337 def _parse_constructor(self, func):
338 return self._parse_method_common(func, False)
340 def _parse_method_common(self, func, is_method):
341 # Skip _get_type functions, we processed them
343 if func.symbol.endswith('_get_type'):
345 if self._namespace_name == 'GLib':
346 # No GObjects in GLib
350 target_arg = func.retval
352 target_arg = func.parameters[0]
355 # Methods require their first arg to be a known class
356 # Look at the original C type (before namespace stripping), without
357 # pointers: GtkButton -> gtk_button_, so we can figure out the
359 argtype = target_arg.type.ctype.replace('*', '')
360 name = self._transformer.remove_prefix(argtype)
361 name_uscore = to_underscores_noprefix(name).lower()
362 name_offset = func.symbol.find(name_uscore)
365 prefix = func.symbol[:name_offset+len(name_uscore)]
367 # Constructors must have _new
368 # Take everything before that as class name
369 new_idx = func.symbol.find('_new')
372 # Constructors don't return basic types
373 derefed = self._transformer.follow_aliases(target_arg.type.name,
375 if derefed in type_names:
376 #print "NOTE: Rejecting constructor returning basic: %r" \
379 prefix = func.symbol[:new_idx]
383 def valid_matching_klass(tclass):
386 elif isinstance(klass, (GLibEnum, GLibFlags)):
388 elif not isinstance(tclass, (GLibObject, GLibBoxed,
394 klass = self._uscore_type_names.get(prefix)
396 #print "NOTE: No valid matching class for likely "+\
397 # "method or constructor: %r" % (func.symbol, )
399 # Enums can't have ctors or methods
400 if isinstance(klass, (GLibEnum, GLibFlags)):
403 # The _uscore_type_names member holds the plain GLibBoxed
404 # object; we want to actually use the struct/record associated
405 if isinstance(klass, GLibBoxed):
406 name = self._transformer.remove_prefix(klass.type_name)
407 klass = self._get_attribute(name)
410 # Interfaces can't have constructors, punt to global scope
411 if isinstance(klass, GLibInterface):
412 #print "NOTE: Rejecting constructor for"+\
413 # " interface type: %r" % (func.symbol, )
415 # TODO - check that the return type is a subclass of the
416 # class from the prefix
417 # But for now, ensure that constructor returns are always
418 # the most concrete class
419 name = self._transformer.remove_prefix(klass.type_name)
420 func.retval.type = Type(name, func.retval.type.ctype)
422 self._remove_attribute(func.name)
423 # Strip namespace and object prefix: gtk_window_new -> new
424 func.name = func.symbol[len(prefix)+1:]
426 # We don't need the "this" parameter
427 del func.parameters[0]
428 klass.methods.append(func)
430 klass.constructors.append(func)
433 def _parse_struct(self, struct):
434 # This is a hack, but GObject is a rather fundamental piece so.
435 internal_names = ["Object", 'InitiallyUnowned']
436 g_internal_names = ["G" + x for x in internal_names]
437 if (self._namespace_name == 'GObject' and
438 struct.name in internal_names):
439 self._create_gobject(struct)
441 elif struct.name in g_internal_names:
444 node = self._names.names.get(struct.name)
446 self._add_attribute(struct, replace=True)
449 node.fields = struct.fields[:]
451 def _parse_union(self, union):
452 node = self._names.names.get(union.name)
454 self._add_attribute(union, replace=True)
457 node.fields = union.fields[:]
459 def _parse_callback(self, callback):
460 self._add_attribute(callback)
462 def _strip_class_suffix(self, name):
463 if (name.endswith('Class') or
464 name.endswith('Iface')):
466 elif name.endswith('Interface'):
471 def _arg_is_failed(self, param):
472 ctype = self._transformer.ctype_of(param).replace('*', '')
473 uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
474 if uscored in self._failed_types:
475 print "Warning: failed type: %r" % (param, )
479 def _pair_class_struct(self, maybe_class):
480 name = self._strip_class_suffix(maybe_class.name)
481 if name == maybe_class.name:
484 if self._arg_is_failed(maybe_class):
485 print "WARNING: deleting no-type %r" % (maybe_class.name, )
486 del self._names.names[maybe_class.name]
489 # Object class fields are assumed to be read-only
490 # (see also _introspect_object and transformer.py)
491 for field in maybe_class.fields:
492 if isinstance(field, Field):
493 field.writable = False
495 name = self._resolve_type_name(name)
496 resolved = self._transformer.remove_prefix(name)
497 pair_class = self._get_attribute(resolved)
498 if pair_class and isinstance(pair_class,
499 (GLibObject, GLibInterface)):
500 for field in maybe_class.fields[1:]:
501 pair_class.fields.append(field)
503 name = self._transformer.remove_prefix(maybe_class.name)
504 pair_class = self._get_attribute(name)
505 if pair_class and isinstance(pair_class,
506 (GLibObject, GLibInterface)):
508 del self._names.names[maybe_class.name]
512 def _introspect_type(self, xmlnode):
513 if xmlnode.tag in ('enum', 'flags'):
514 self._introspect_enum(xmlnode)
515 elif xmlnode.tag == 'class':
516 self._introspect_object(xmlnode)
517 elif xmlnode.tag == 'interface':
518 self._introspect_interface(xmlnode)
519 elif xmlnode.tag == 'boxed':
520 self._introspect_boxed(xmlnode)
522 raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
524 def _introspect_enum(self, node):
526 for member in node.findall('member'):
527 members.append(GLibEnumMember(member.attrib['nick'],
528 member.attrib['value'],
529 member.attrib['name'],
530 member.attrib['nick']))
532 klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
533 type_name = node.attrib['name']
534 enum_name = self._transformer.remove_prefix(type_name)
535 node = klass(enum_name, type_name, members, node.attrib['get-type'])
536 self._add_attribute(node, replace=True)
537 self._register_internal_type(type_name, node)
539 def _introspect_object(self, xmlnode):
540 type_name = xmlnode.attrib['name']
541 # We handle this specially above; in 2.16 and below there
542 # was no g_object_get_type, for later versions we need
544 if type_name == 'GObject':
546 parent_type_name = xmlnode.attrib['parent']
547 parent_gitype = self._resolve_gtypename(parent_type_name)
548 is_abstract = not not xmlnode.attrib.get('abstract', False)
550 self._transformer.remove_prefix(type_name),
553 xmlnode.attrib['get-type'], is_abstract)
554 self._introspect_properties(node, xmlnode)
555 self._introspect_signals(node, xmlnode)
556 self._introspect_implemented_interfaces(node, xmlnode)
559 struct = self._get_attribute(node.name)
560 if struct is not None:
561 node.fields = struct.fields
562 for field in node.fields:
563 if isinstance(field, Field):
564 # Object instance fields are assumed to be read-only
565 # (see also _pair_class_struct and transformer.py)
566 field.writable = False
568 self._add_attribute(node, replace=True)
569 self._register_internal_type(type_name, node)
571 def _introspect_interface(self, xmlnode):
572 type_name = xmlnode.attrib['name']
573 node = GLibInterface(
574 self._transformer.remove_prefix(type_name),
576 type_name, xmlnode.attrib['get-type'])
577 self._introspect_properties(node, xmlnode)
578 self._introspect_signals(node, xmlnode)
579 # GtkFileChooserEmbed is an example of a private interface, we
580 # just filter them out
581 if xmlnode.attrib['get-type'].startswith('_'):
582 print "NOTICE: Marking %s as internal type" % (type_name, )
583 self._private_internal_types[type_name] = node
585 self._add_attribute(node, replace=True)
586 self._register_internal_type(type_name, node)
588 def _introspect_boxed(self, xmlnode):
589 type_name = xmlnode.attrib['name']
590 # This one doesn't go in the main namespace; we associate it with
591 # the struct or union
592 node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
593 self._boxed_types[node.type_name] = node
594 self._register_internal_type(type_name, node)
596 def _introspect_implemented_interfaces(self, node, xmlnode):
598 for interface in xmlnode.findall('implements'):
599 gitype = self._resolve_gtypename(interface.attrib['name'])
600 gt_interfaces.append(gitype)
601 node.interfaces = gt_interfaces
603 def _introspect_properties(self, node, xmlnode):
604 for pspec in xmlnode.findall('property'):
605 ctype = pspec.attrib['type']
606 flags = int(pspec.attrib['flags'])
607 readable = (flags & G_PARAM_READABLE) != 0
608 writable = (flags & G_PARAM_WRITABLE) != 0
609 construct = (flags & G_PARAM_CONSTRUCT) != 0
610 construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
611 node.properties.append(Property(
612 pspec.attrib['name'],
613 type_name_from_ctype(ctype),
614 readable, writable, construct, construct_only,
618 def _introspect_signals(self, node, xmlnode):
619 for signal_info in xmlnode.findall('signal'):
620 rctype = signal_info.attrib['return']
621 rtype = Type(self._transformer.parse_ctype(rctype), rctype)
622 return_ = Return(rtype, signal_info.attrib['return'])
623 return_.transfer = 'full'
624 signal = GLibSignal(signal_info.attrib['name'], return_)
625 for i, parameter in enumerate(signal_info.findall('param')):
629 name = 'p%s' % (i-1, )
630 pctype = parameter.attrib['type']
631 ptype = Type(self._transformer.parse_ctype(pctype), pctype)
632 param = Parameter(name, ptype)
633 param.transfer = 'none'
634 signal.parameters.append(param)
635 node.signals.append(signal)
639 def _resolve_type_name(self, type_name, ctype=None):
640 # Workaround glib bug #548689, to be included in 2.18.0
641 if type_name == "GParam":
642 type_name = "GObject.ParamSpec"
643 res = self._transformer.resolve_type_name_full
645 return res(type_name, ctype, self._names)
647 return self._transformer.resolve_type_name(type_name, ctype)
649 def _resolve_param_type(self, ptype, **kwargs):
650 # Workaround glib bug #548689, to be included in 2.18.0
651 if ptype.name == "GParam":
652 ptype.name = "GObject.ParamSpec"
653 return self._transformer.resolve_param_type_full(ptype,
657 def _resolve_node(self, node):
658 if isinstance(node, Function):
659 self._resolve_function_toplevel(node)
661 elif isinstance(node, Callback):
662 self._resolve_function(node)
663 elif isinstance(node, GLibObject):
664 self._resolve_glib_object(node)
665 elif isinstance(node, GLibInterface):
666 self._resolve_glib_interface(node)
667 elif isinstance(node, Struct):
668 self._resolve_struct(node)
669 elif isinstance(node, Union):
670 self._resolve_union(node)
671 elif isinstance(node, Alias):
672 self._resolve_alias(node)
674 def _resolve_function_toplevel(self, func):
675 newfunc = self._parse_constructor(func)
677 newfunc = self._parse_method(func)
679 self._resolve_function(func)
681 self._resolve_function(newfunc)
683 def _pair_boxed_type(self, boxed):
684 name = self._transformer.remove_prefix(boxed.type_name)
685 pair_node = self._get_attribute(name)
687 boxed_item = GLibBoxedOther(name, boxed.type_name,
689 elif isinstance(pair_node, Struct):
690 boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
692 boxed_item.fields = pair_node.fields
693 elif isinstance(pair_node, Union):
694 boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
696 boxed_item.fields = pair_node.fields
699 self._add_attribute(boxed_item, replace=True)
701 def _resolve_struct(self, node):
702 for field in node.fields:
703 self._resolve_field(field)
705 def _resolve_union(self, node):
706 for field in node.fields:
707 self._resolve_field(field)
709 def _force_resolve(self, item, allow_unknown=False):
710 if isinstance(item, Unresolved):
711 if item.target in self._private_internal_types:
714 return self._transformer.gtypename_to_giname(item.target,
718 print "WARNING: Skipping unknown interface %s" % \
723 if item in self._private_internal_types:
727 def _resolve_glib_interface(self, node):
728 node.parent = self._force_resolve(node.parent)
729 self._resolve_methods(node.methods)
730 self._resolve_properties(node.properties, node)
731 self._resolve_signals(node.signals)
733 def _resolve_glib_object(self, node):
734 node.parent = self._force_resolve(node.parent)
735 node.interfaces = filter(None,
736 [self._force_resolve(x, allow_unknown=True)
737 for x in node.interfaces])
738 self._resolve_constructors(node.constructors)
739 self._resolve_methods(node.methods)
740 self._resolve_properties(node.properties, node)
741 self._resolve_signals(node.signals)
742 for field in node.fields:
743 self._resolve_field(field)
745 def _resolve_glib_boxed(self, node):
746 self._resolve_constructors(node.constructors)
747 self._resolve_methods(node.methods)
749 def _resolve_constructors(self, constructors):
750 for ctor in constructors:
751 self._resolve_function(ctor)
753 def _resolve_methods(self, methods):
754 for method in methods:
755 self._resolve_function(method)
757 def _resolve_signals(self, signals):
758 for signal in signals:
759 self._resolve_function(signal)
761 def _resolve_properties(self, properties, context):
763 for prop in properties:
765 self._resolve_property(prop)
769 print ("WARNING: Deleting object property %r (of %r) "
770 "with unknown type") % (fail, context)
771 properties.remove(fail)
773 def _resolve_property(self, prop):
774 prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
776 def _adjust_transfer(self, param):
777 if not (param.transfer is None or param.transfer_inferred):
780 # Do GLib/GObject-specific type transformations here
781 node = self._lookup_node(param.type.name)
785 if isinstance(param, Parameter):
786 if param.direction != PARAM_DIRECTION_IN:
793 param.transfer = transfer
795 def _adjust_throws(self, func):
796 if func.parameters == []:
799 last_param = func.parameters.pop()
801 if (last_param.type.name == 'GLib.Error' or
802 (self._namespace_name == 'GLib' and
803 last_param.type.name == 'Error')):
806 func.parameters.append(last_param)
808 def _resolve_function(self, func):
809 self._resolve_parameters(func.parameters)
810 func.retval.type = self._resolve_param_type(func.retval.type)
811 self._adjust_throws(func)
812 self._adjust_transfer(func.retval)
814 def _resolve_parameters(self, parameters):
815 for parameter in parameters:
816 parameter.type = self._resolve_param_type(parameter.type)
817 self._adjust_transfer(parameter)
819 def _resolve_field(self, field):
820 if isinstance(field, Callback):
821 self._resolve_function(field)
823 field.type = self._resolve_param_type(field.type)
825 def _resolve_alias(self, alias):
826 alias.target = self._resolve_type_name(alias.target, alias.target)
830 def _validate(self, nodes):
831 nodes = list(self._names.names.itervalues())
833 self._validating = True
837 print "Type resolution; pass=%d" % (i, )
838 nodes = list(self._names.names.itervalues())
841 self._resolve_node(node)
842 except UnknownTypeError, e:
843 print "WARNING: %s: Deleting %r" % (e, node)
844 self._remove_attribute(node.name)
845 if len(nodes) == initlen:
848 self._print_statistics()
849 self._validating = False