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.
23 from collections import OrderedDict
24 from xml.etree.ElementTree import parse
27 from .girwriter import COMPATIBLE_GIR_VERSION
28 from .message import Position
30 CORE_NS = "http://www.gtk.org/introspection/core/1.0"
31 C_NS = "http://www.gtk.org/introspection/c/1.0"
32 GLIB_NS = "http://www.gtk.org/introspection/glib/1.0"
36 return '{%s}%s' % (CORE_NS, tag)
40 return '{%s}%s' % (GLIB_NS, tag)
44 return '{%s}%s' % (C_NS, tag)
47 class GIRParser(object):
49 def __init__(self, types_only=False):
50 self._types_only = types_only
51 self._namespace = None
52 self._filename_stack = []
56 def parse(self, filename):
57 filename = os.path.abspath(filename)
58 self._filename_stack.append(filename)
59 tree = parse(filename)
61 self._filename_stack.pop()
63 def parse_tree(self, tree):
64 self._namespace = None
65 self._pkgconfig_packages = set()
66 self._includes = set()
67 self._c_includes = set()
69 self._parse_api(tree.getroot())
71 def get_namespace(self):
72 return self._namespace
76 def _find_first_child(self, node, name_or_names):
77 if isinstance(name_or_names, str):
79 if child.tag == name_or_names:
83 if child.tag in name_or_names:
87 def _find_children(self, node, name):
88 return [child for child in node if child.tag == name]
90 def _get_current_file(self):
91 if not self._filename_stack:
93 cwd = os.getcwd() + os.sep
94 curfile = self._filename_stack[-1]
95 if curfile.startswith(cwd):
96 return curfile[len(cwd):]
99 def _parse_api(self, root):
100 assert root.tag == _corens('repository')
101 version = root.attrib['version']
102 if version != COMPATIBLE_GIR_VERSION:
103 raise SystemExit("%s: Incompatible version %s (supported: %s)" %
104 (self._get_current_file(), version, COMPATIBLE_GIR_VERSION))
107 if node.tag == _corens('include'):
108 self._parse_include(node)
109 elif node.tag == _corens('package'):
110 self._parse_pkgconfig_package(node)
111 elif node.tag == _cns('include'):
112 self._parse_c_include(node)
114 ns = root.find(_corens('namespace'))
115 assert ns is not None
116 identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
117 if identifier_prefixes:
118 identifier_prefixes = identifier_prefixes.split(',')
119 symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
121 symbol_prefixes = symbol_prefixes.split(',')
122 self._namespace = ast.Namespace(ns.attrib['name'],
123 ns.attrib['version'],
124 identifier_prefixes=identifier_prefixes,
125 symbol_prefixes=symbol_prefixes)
126 if 'shared-library' in ns.attrib:
127 self._namespace.shared_libraries = ns.attrib['shared-library'].split(',')
128 self._namespace.includes = self._includes
129 self._namespace.c_includes = self._c_includes
130 self._namespace.exported_packages = self._pkgconfig_packages
133 _corens('alias'): self._parse_alias,
134 _corens('bitfield'): self._parse_enumeration_bitfield,
135 _corens('callback'): self._parse_callback,
136 _corens('class'): self._parse_object_interface,
137 _corens('enumeration'): self._parse_enumeration_bitfield,
138 _corens('interface'): self._parse_object_interface,
139 _corens('record'): self._parse_record,
140 _corens('union'): self._parse_union,
141 _corens('docsection'): self._parse_doc_section,
142 _glibns('boxed'): self._parse_boxed}
144 if not self._types_only:
145 parser_methods[_corens('constant')] = self._parse_constant
146 parser_methods[_corens('function-macro')] = self._parse_function_macro
147 parser_methods[_corens('function')] = self._parse_function
150 method = parser_methods.get(node.tag)
151 if method is not None:
154 def _parse_doc_section(self, node):
155 docsection = ast.DocSection(node.attrib["name"])
156 self._parse_generic_attribs(node, docsection)
157 self._namespace.append(docsection)
159 def _parse_include(self, node):
160 include = ast.Include(node.attrib['name'], node.attrib['version'])
161 self._includes.add(include)
163 def _parse_pkgconfig_package(self, node):
164 self._pkgconfig_packages.add(node.attrib['name'])
166 def _parse_c_include(self, node):
167 self._c_includes.add(node.attrib['name'])
169 def _parse_alias(self, node):
170 typeval = self._parse_type(node)
171 alias = ast.Alias(node.attrib['name'], typeval, node.attrib.get(_cns('type')))
172 self._parse_generic_attribs(node, alias)
173 self._namespace.append(alias)
175 def _parse_generic_attribs(self, node, obj):
176 assert isinstance(obj, ast.Annotated)
177 skip = node.attrib.get('skip')
180 obj.skip = int(skip) > 0
183 introspectable = node.attrib.get('introspectable')
186 obj.introspectable = int(introspectable) > 0
188 obj.introspectable = False
191 doc = node.find(_corens('doc'))
195 obj.doc_position = Position(doc.attrib.get('filename', '<unknown>'),
196 doc.attrib.get('line', None),
197 doc.attrib.get('column', None))
198 version = node.attrib.get('version')
200 obj.version = version
201 version_doc = node.find(_corens('doc-version'))
202 if version_doc is not None:
204 obj.version_doc = version_doc.text
205 deprecated = node.attrib.get('deprecated-version')
207 obj.deprecated = deprecated
208 deprecated_doc = node.find(_corens('doc-deprecated'))
209 if deprecated_doc is not None:
210 if deprecated_doc.text:
211 obj.deprecated_doc = deprecated_doc.text
212 stability = node.attrib.get('stability')
214 obj.stability = stability
215 stability_doc = node.find(_corens('doc-stability'))
216 if stability_doc is not None:
217 if stability_doc.text:
218 obj.stability_doc = stability_doc.text
219 attributes = node.findall(_corens('attribute'))
221 attributes_ = OrderedDict()
222 for attribute in attributes:
223 name = attribute.attrib.get('name')
224 value = attribute.attrib.get('value')
225 attributes_[name] = value
226 obj.attributes = attributes_
228 if hasattr(obj, 'add_file_position'):
229 positions = sorted(node.findall(_corens('source-position')),
230 key=lambda x: (x.attrib['filename'],
231 int(x.attrib['line'])))
232 for position in positions:
233 if 'column' in position.attrib:
234 column = int(position.attrib['column'])
238 obj.add_file_position(Position(position.attrib['filename'],
239 int(position.attrib['line']),
242 def _parse_object_interface(self, node):
243 parent = node.attrib.get('parent')
245 parent_type = self._namespace.type_from_name(parent)
249 ctor_kwargs = {'name': node.attrib['name'],
250 'parent_type': parent_type,
251 'gtype_name': node.attrib[_glibns('type-name')],
252 'get_type': node.attrib[_glibns('get-type')],
253 'c_symbol_prefix': node.attrib.get(_cns('symbol-prefix')),
254 'ctype': node.attrib.get(_cns('type'))}
255 if node.tag == _corens('interface'):
256 klass = ast.Interface
257 elif node.tag == _corens('class'):
259 is_abstract = node.attrib.get('abstract')
260 is_abstract = is_abstract and is_abstract != '0'
261 ctor_kwargs['is_abstract'] = is_abstract
262 is_final = node.attrib.get('final')
263 is_final = is_final and is_final != '0'
264 ctor_kwargs['is_final'] = is_final
266 raise AssertionError(node)
268 obj = klass(**ctor_kwargs)
269 self._parse_generic_attribs(node, obj)
270 type_struct = node.attrib.get(_glibns('type-struct'))
272 obj.glib_type_struct = self._namespace.type_from_name(type_struct)
273 if klass == ast.Class:
274 is_fundamental = node.attrib.get(_glibns('fundamental'))
275 if is_fundamental and is_fundamental != '0':
276 obj.fundamental = True
277 for func_id in ['ref-func', 'unref-func',
278 'set-value-func', 'get-value-func']:
279 func_name = node.attrib.get(_glibns(func_id))
280 obj.__dict__[func_id.replace('-', '_')] = func_name
283 self._namespace.append(obj)
286 for iface in self._find_children(node, _corens('implements')):
287 obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
288 for iface in self._find_children(node, _corens('prerequisite')):
289 obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
290 for func_node in self._find_children(node, _corens('function')):
291 func = self._parse_function_common(func_node, ast.Function, obj)
292 obj.static_methods.append(func)
293 for method in self._find_children(node, _corens('method')):
294 func = self._parse_function_common(method, ast.Function, obj)
295 func.is_method = True
296 obj.methods.append(func)
297 for method in self._find_children(node, _corens('virtual-method')):
298 func = self._parse_function_common(method, ast.VFunction, obj)
299 self._parse_generic_attribs(method, func)
300 func.is_method = True
301 func.invoker = method.get('invoker')
302 obj.virtual_methods.append(func)
303 for ctor in self._find_children(node, _corens('constructor')):
304 func = self._parse_function_common(ctor, ast.Function, obj)
305 func.is_constructor = True
306 obj.constructors.append(func)
307 obj.fields.extend(self._parse_fields(node, obj))
308 for prop in self._find_children(node, _corens('property')):
309 obj.properties.append(self._parse_property(prop, obj))
310 for signal in self._find_children(node, _glibns('signal')):
311 obj.signals.append(self._parse_function_common(signal, ast.Signal, obj))
313 self._namespace.append(obj)
315 def _parse_callback(self, node):
316 callback = self._parse_function_common(node, ast.Callback)
317 self._namespace.append(callback)
319 def _parse_function(self, node):
320 function = self._parse_function_common(node, ast.Function)
321 self._namespace.append(function)
323 def _parse_function_macro(self, node):
324 name = node.attrib['name']
325 symbol = node.attrib.get(_cns('identifier'))
327 parameters_node = node.find(_corens('parameters'))
328 if (parameters_node is not None):
329 for paramnode in self._find_children(parameters_node, _corens('parameter')):
330 param = ast.Parameter(paramnode.attrib.get('name'), None)
331 parameters.append(param)
332 self._parse_generic_attribs(paramnode, param)
334 func = ast.FunctionMacro(name, parameters, symbol)
335 self._parse_generic_attribs(node, func)
337 self._namespace.track(func)
338 self._namespace.append(func)
340 def _parse_parameter(self, node):
341 typeval = self._parse_type(node)
342 param = ast.Parameter(node.attrib.get('name'),
344 node.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
345 node.attrib.get('transfer-ownership'),
346 node.attrib.get('nullable') == '1',
347 node.attrib.get('optional') == '1',
348 node.attrib.get('allow-none') == '1',
349 node.attrib.get('scope'),
350 node.attrib.get('caller-allocates') == '1')
351 self._parse_generic_attribs(node, param)
354 def _parse_function_common(self, node, klass, parent=None):
355 name = node.attrib['name']
356 returnnode = node.find(_corens('return-value'))
358 raise ValueError('node %r has no return-value' % (name, ))
359 transfer = returnnode.attrib.get('transfer-ownership')
360 nullable = returnnode.attrib.get('nullable') == '1'
361 retval = ast.Return(self._parse_type(returnnode), nullable, False, transfer)
362 self._parse_generic_attribs(returnnode, retval)
365 throws = (node.attrib.get('throws') == '1')
367 if klass is ast.Callback:
368 func = klass(name, retval, parameters, throws,
369 node.attrib.get(_cns('type')))
370 elif klass is ast.Function:
371 identifier = node.attrib.get(_cns('identifier'))
372 func = klass(name, retval, parameters, throws, identifier)
373 elif klass is ast.VFunction:
374 func = klass(name, retval, parameters, throws)
375 elif klass is ast.Signal:
376 func = klass(name, retval, parameters,
377 when=node.attrib.get('when'),
378 no_recurse=node.attrib.get('no-recurse', '0') == '1',
379 detailed=node.attrib.get('detailed', '0') == '1',
380 action=node.attrib.get('action', '0') == '1',
381 no_hooks=node.attrib.get('no-hooks', '0') == '1')
385 func.set_property = node.attrib.get(_glibns('set-property'), None)
386 func.get_property = node.attrib.get(_glibns('get-property'), None)
387 func.shadows = node.attrib.get('shadows', None)
388 func.shadowed_by = node.attrib.get('shadowed-by', None)
389 func.moved_to = node.attrib.get('moved-to', None)
392 parameters_node = node.find(_corens('parameters'))
393 if (parameters_node is not None):
394 paramnode = self._find_first_child(parameters_node, _corens('instance-parameter'))
396 func.instance_parameter = self._parse_parameter(paramnode)
397 for paramnode in self._find_children(parameters_node, _corens('parameter')):
398 parameters.append(self._parse_parameter(paramnode))
399 for i, paramnode in enumerate(self._find_children(parameters_node,
400 _corens('parameter'))):
401 param = parameters[i]
402 self._parse_type_array_length(parameters, paramnode, param.type)
403 closure = paramnode.attrib.get('closure')
406 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
407 param.closure_name = parameters[idx].argname
408 destroy = paramnode.attrib.get('destroy')
411 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
412 param.destroy_name = parameters[idx].argname
414 self._parse_type_array_length(parameters, returnnode, retval.type)
416 # Re-set the function's parameters to notify it of changes to the list.
417 func.parameters = parameters
419 self._parse_generic_attribs(node, func)
421 self._namespace.track(func)
424 def _parse_fields(self, node, obj):
426 names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
428 if child.tag in names:
429 fieldobj = self._parse_field(child, obj)
433 def _parse_compound(self, cls, node):
434 compound = cls(node.attrib.get('name'),
435 ctype=node.attrib.get(_cns('type')),
436 disguised=node.attrib.get('disguised') == '1',
437 gtype_name=node.attrib.get(_glibns('type-name')),
438 get_type=node.attrib.get(_glibns('get-type')),
439 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
440 if node.attrib.get('foreign') == '1':
441 compound.foreign = True
442 self._parse_generic_attribs(node, compound)
443 if not self._types_only:
444 compound.fields.extend(self._parse_fields(node, compound))
445 for method in self._find_children(node, _corens('method')):
446 func = self._parse_function_common(method, ast.Function, compound)
447 func.is_method = True
448 compound.methods.append(func)
449 for i, fieldnode in enumerate(self._find_children(node, _corens('field'))):
450 field = compound.fields[i]
451 self._parse_type_array_length(compound.fields, fieldnode, field.type)
452 for func in self._find_children(node, _corens('function')):
453 compound.static_methods.append(
454 self._parse_function_common(func, ast.Function, compound))
455 for ctor in self._find_children(node, _corens('constructor')):
456 func = self._parse_function_common(ctor, ast.Function, compound)
457 func.is_constructor = True
458 compound.constructors.append(func)
461 def _parse_record(self, node, anonymous=False):
462 struct = self._parse_compound(ast.Record, node)
463 is_gtype_struct_for = node.attrib.get(_glibns('is-gtype-struct-for'))
464 if is_gtype_struct_for is not None:
465 struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
467 self._namespace.append(struct)
470 def _parse_union(self, node, anonymous=False):
471 union = self._parse_compound(ast.Union, node)
473 self._namespace.append(union)
476 def _parse_type_simple(self, typenode):
477 # ast.Fields can contain inline callbacks
478 if typenode.tag == _corens('callback'):
479 typeval = self._namespace.type_from_name(typenode.attrib['name'])
480 typeval.ctype = typenode.attrib.get(_cns('type'))
482 # ast.Arrays have their own toplevel XML
483 elif typenode.tag == _corens('array'):
484 array_type = typenode.attrib.get('name')
485 element_type = self._parse_type(typenode)
486 array_ctype = typenode.attrib.get(_cns('type'))
487 ret = ast.Array(array_type, element_type, ctype=array_ctype)
488 # zero-terminated defaults to true...
489 zero = typenode.attrib.get('zero-terminated')
490 if zero and zero == '0':
491 ret.zeroterminated = False
492 fixed_size = typenode.attrib.get('fixed-size')
494 ret.size = int(fixed_size)
497 elif typenode.tag == _corens('varargs'):
499 elif typenode.tag == _corens('type'):
500 name = typenode.attrib.get('name')
501 ctype = typenode.attrib.get(_cns('type'))
504 return ast.TypeUnknown()
505 return ast.Type(ctype=ctype)
506 elif name in ['GLib.List', 'GLib.SList']:
507 subchild = self._find_first_child(typenode,
508 list(map(_corens, ('callback', 'array',
509 ' varargs', 'type'))))
510 if subchild is not None:
511 element_type = self._parse_type(typenode)
513 element_type = ast.TYPE_ANY
514 return ast.List(name, element_type, ctype=ctype)
515 elif name == 'GLib.HashTable':
516 subchildren = self._find_children(typenode, _corens('type'))
517 subchildren_types = list(map(self._parse_type_simple, subchildren))
518 while len(subchildren_types) < 2:
519 subchildren_types.append(ast.TYPE_ANY)
520 return ast.Map(subchildren_types[0], subchildren_types[1], ctype=ctype)
522 return self._namespace.type_from_name(name, ctype)
524 assert False, "Failed to parse inner type"
526 def _parse_type(self, node):
527 for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
528 typenode = node.find(name)
529 if typenode is not None:
530 return self._parse_type_simple(typenode)
531 assert False, "Failed to parse toplevel type"
533 def _parse_type_array_length(self, siblings, node, typeval):
534 """A hack necessary to handle the integer parameter/field indexes on
536 typenode = node.find(_corens('array'))
539 lenidx = typenode.attrib.get('length')
540 if lenidx is not None:
542 assert idx < len(siblings), "%r %d >= %d" % (siblings, idx, len(siblings))
543 if isinstance(siblings[idx], ast.Field):
544 typeval.length_param_name = siblings[idx].name
546 typeval.length_param_name = siblings[idx].argname
548 def _parse_boxed(self, node):
549 obj = ast.Boxed(node.attrib[_glibns('name')],
550 gtype_name=node.attrib[_glibns('type-name')],
551 get_type=node.attrib[_glibns('get-type')],
552 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
553 self._parse_generic_attribs(node, obj)
556 self._namespace.append(obj)
559 for method in self._find_children(node, _corens('method')):
560 func = self._parse_function_common(method, ast.Function, obj)
561 func.is_method = True
562 obj.methods.append(func)
563 for ctor in self._find_children(node, _corens('constructor')):
564 obj.constructors.append(
565 self._parse_function_common(ctor, ast.Function, obj))
566 for callback in self._find_children(node, _corens('callback')):
568 self._parse_function_common(callback, ast.Callback, obj))
569 self._namespace.append(obj)
571 def _parse_field(self, node, parent):
573 anonymous_node = None
574 if node.tag in map(_corens, ('callback', 'record', 'union')):
577 anonymous_elt = self._find_first_child(node, _corens('callback'))
578 if anonymous_elt is not None:
579 if anonymous_elt.tag == _corens('callback'):
580 anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
581 elif anonymous_elt.tag == _corens('record'):
582 anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
583 elif anonymous_elt.tag == _corens('union'):
584 anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
586 assert False, anonymous_elt.tag
588 assert node.tag == _corens('field'), node.tag
589 type_node = self._parse_type(node)
590 field = ast.Field(node.attrib.get('name'),
592 node.attrib.get('readable') != '0',
593 node.attrib.get('writable') == '1',
594 node.attrib.get('bits'),
595 anonymous_node=anonymous_node)
596 field.private = node.attrib.get('private') == '1'
597 field.parent = parent
598 self._parse_generic_attribs(node, field)
601 def _parse_property(self, node, parent):
602 prop = ast.Property(node.attrib['name'],
603 self._parse_type(node),
604 node.attrib.get('readable') != '0',
605 node.attrib.get('writable') == '1',
606 node.attrib.get('construct') == '1',
607 node.attrib.get('construct-only') == '1',
608 node.attrib.get('transfer-ownership'))
609 prop.setter = node.attrib.get('setter')
610 prop.getter = node.attrib.get('getter')
612 self._parse_generic_attribs(node, prop)
615 def _parse_member(self, node):
616 member = ast.Member(node.attrib['name'],
617 node.attrib['value'],
618 node.attrib.get(_cns('identifier')),
619 node.attrib.get(_glibns('nick')),
620 node.attrib.get(_glibns('name')))
621 self._parse_generic_attribs(node, member)
624 def _parse_constant(self, node):
625 type_node = self._parse_type(node)
626 constant = ast.Constant(node.attrib['name'],
628 node.attrib['value'],
629 node.attrib.get(_cns('type')))
630 self._parse_generic_attribs(node, constant)
631 self._namespace.append(constant)
633 def _parse_enumeration_bitfield(self, node):
634 name = node.attrib.get('name')
635 ctype = node.attrib.get(_cns('type'))
636 get_type = node.attrib.get(_glibns('get-type'))
637 type_name = node.attrib.get(_glibns('type-name'))
638 glib_error_domain = node.attrib.get(_glibns('error-domain'))
639 if node.tag == _corens('bitfield'):
644 obj = klass(name, ctype,
646 gtype_name=type_name,
648 obj.error_domain = glib_error_domain
650 self._parse_generic_attribs(node, obj)
653 self._namespace.append(obj)
656 for member_node in self._find_children(node, _corens('member')):
657 member = self._parse_member(member_node)
659 members.append(member)
660 for func_node in self._find_children(node, _corens('function')):
661 func = self._parse_function_common(func_node, ast.Function)
663 obj.static_methods.append(func)
664 self._namespace.append(obj)