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 xml.etree.cElementTree import parse
26 from .girwriter import COMPATIBLE_GIR_VERSION
28 CORE_NS = "http://www.gtk.org/introspection/core/1.0"
29 C_NS = "http://www.gtk.org/introspection/c/1.0"
30 GLIB_NS = "http://www.gtk.org/introspection/glib/1.0"
34 return '{%s}%s' % (CORE_NS, tag)
38 return '{%s}%s' % (GLIB_NS, tag)
42 return '{%s}%s' % (C_NS, tag)
45 class GIRParser(object):
47 def __init__(self, types_only=False):
48 self._types_only = types_only
49 self._namespace = None
50 self._filename_stack = []
54 def parse(self, filename):
55 filename = os.path.abspath(filename)
56 self._filename_stack.append(filename)
57 tree = parse(filename)
59 self._filename_stack.pop()
61 def parse_tree(self, tree):
62 self._namespace = None
63 self._pkgconfig_packages = set()
64 self._includes = set()
65 self._c_includes = set()
67 self._parse_api(tree.getroot())
69 def get_namespace(self):
70 return self._namespace
74 def _find_first_child(self, node, name_or_names):
75 if isinstance(name_or_names, str):
76 for child in node.getchildren():
77 if child.tag == name_or_names:
80 for child in node.getchildren():
81 if child.tag in name_or_names:
85 def _find_children(self, node, name):
86 return [child for child in node.getchildren() if child.tag == name]
88 def _get_current_file(self):
89 if not self._filename_stack:
91 cwd = os.getcwd() + os.sep
92 curfile = self._filename_stack[-1]
93 if curfile.startswith(cwd):
94 return curfile[len(cwd):]
97 def _parse_api(self, root):
98 assert root.tag == _corens('repository')
99 version = root.attrib['version']
100 if version != COMPATIBLE_GIR_VERSION:
101 raise SystemExit("%s: Incompatible version %s (supported: %s)" %
102 (self._get_current_file(), version, COMPATIBLE_GIR_VERSION))
104 for node in root.getchildren():
105 if node.tag == _corens('include'):
106 self._parse_include(node)
107 elif node.tag == _corens('package'):
108 self._parse_pkgconfig_package(node)
109 elif node.tag == _cns('include'):
110 self._parse_c_include(node)
112 ns = root.find(_corens('namespace'))
113 assert ns is not None
114 identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
115 if identifier_prefixes:
116 identifier_prefixes = identifier_prefixes.split(',')
117 symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
119 symbol_prefixes = symbol_prefixes.split(',')
120 self._namespace = ast.Namespace(ns.attrib['name'],
121 ns.attrib['version'],
122 identifier_prefixes=identifier_prefixes,
123 symbol_prefixes=symbol_prefixes)
124 if 'shared-library' in ns.attrib:
125 self._namespace.shared_libraries = ns.attrib['shared-library'].split(',')
126 self._namespace.includes = self._includes
127 self._namespace.c_includes = self._c_includes
128 self._namespace.exported_packages = self._pkgconfig_packages
131 _corens('alias'): self._parse_alias,
132 _corens('bitfield'): self._parse_enumeration_bitfield,
133 _corens('callback'): self._parse_callback,
134 _corens('class'): self._parse_object_interface,
135 _corens('enumeration'): self._parse_enumeration_bitfield,
136 _corens('interface'): self._parse_object_interface,
137 _corens('record'): self._parse_record,
138 _corens('union'): self._parse_union,
139 _glibns('boxed'): self._parse_boxed}
141 if not self._types_only:
142 parser_methods[_corens('constant')] = self._parse_constant
143 parser_methods[_corens('function')] = self._parse_function
145 for node in ns.getchildren():
146 method = parser_methods.get(node.tag)
147 if method is not None:
150 def _parse_include(self, node):
151 include = ast.Include(node.attrib['name'], node.attrib['version'])
152 self._includes.add(include)
154 def _parse_pkgconfig_package(self, node):
155 self._pkgconfig_packages.add(node.attrib['name'])
157 def _parse_c_include(self, node):
158 self._c_includes.add(node.attrib['name'])
160 def _parse_alias(self, node):
161 typeval = self._parse_type(node)
162 alias = ast.Alias(node.attrib['name'], typeval, node.attrib.get(_cns('type')))
163 self._parse_generic_attribs(node, alias)
164 self._namespace.append(alias)
166 def _parse_generic_attribs(self, node, obj):
167 assert isinstance(obj, ast.Annotated)
168 introspectable = node.attrib.get('introspectable')
170 obj.introspectable = int(introspectable) > 0
173 doc = node.find(_corens('doc'))
177 version = node.attrib.get('version')
179 obj.version = version
180 version_doc = node.find(_corens('doc-version'))
181 if version_doc is not None:
183 obj.version_doc = version_doc.text
184 deprecated = node.attrib.get('deprecated-version')
186 obj.deprecated = deprecated
187 deprecated_doc = node.find(_corens('doc-deprecated'))
188 if deprecated_doc is not None:
189 if deprecated_doc.text:
190 obj.deprecated_doc = deprecated_doc.text
191 stability = node.attrib.get('stability')
193 obj.stability = stability
194 stability_doc = node.find(_corens('doc-stability'))
195 if stability_doc is not None:
196 if stability_doc.text:
197 obj.stability_doc = stability_doc.text
199 def _parse_object_interface(self, node):
200 parent = node.attrib.get('parent')
202 parent_type = self._namespace.type_from_name(parent)
206 ctor_kwargs = {'name': node.attrib['name'],
207 'parent_type': parent_type,
208 'gtype_name': node.attrib[_glibns('type-name')],
209 'get_type': node.attrib[_glibns('get-type')],
210 'c_symbol_prefix': node.attrib.get(_cns('symbol-prefix')),
211 'ctype': node.attrib.get(_cns('type'))}
212 if node.tag == _corens('interface'):
213 klass = ast.Interface
214 elif node.tag == _corens('class'):
216 is_abstract = node.attrib.get('abstract')
217 is_abstract = is_abstract and is_abstract != '0'
218 ctor_kwargs['is_abstract'] = is_abstract
220 raise AssertionError(node)
222 obj = klass(**ctor_kwargs)
223 self._parse_generic_attribs(node, obj)
224 type_struct = node.attrib.get(_glibns('type-struct'))
226 obj.glib_type_struct = self._namespace.type_from_name(type_struct)
227 if klass == ast.Class:
228 is_fundamental = node.attrib.get(_glibns('fundamental'))
229 if is_fundamental and is_fundamental != '0':
230 obj.fundamental = True
231 for func_id in ['ref-func', 'unref-func',
232 'set-value-func', 'get-value-func']:
233 func_name = node.attrib.get(_glibns(func_id))
234 obj.__dict__[func_id.replace('-', '_')] = func_name
237 self._namespace.append(obj)
240 for iface in self._find_children(node, _corens('implements')):
241 obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
242 for iface in self._find_children(node, _corens('prerequisite')):
243 obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
244 for func_node in self._find_children(node, _corens('function')):
245 func = self._parse_function_common(func_node, ast.Function, obj)
246 obj.static_methods.append(func)
247 for method in self._find_children(node, _corens('method')):
248 func = self._parse_function_common(method, ast.Function, obj)
249 func.is_method = True
250 obj.methods.append(func)
251 for method in self._find_children(node, _corens('virtual-method')):
252 func = self._parse_function_common(method, ast.VFunction, obj)
253 self._parse_generic_attribs(method, func)
254 func.is_method = True
255 func.invoker = method.get('invoker')
256 obj.virtual_methods.append(func)
257 for ctor in self._find_children(node, _corens('constructor')):
258 func = self._parse_function_common(ctor, ast.Function, obj)
259 func.is_constructor = True
260 obj.constructors.append(func)
261 obj.fields.extend(self._parse_fields(node, obj))
262 for prop in self._find_children(node, _corens('property')):
263 obj.properties.append(self._parse_property(prop, obj))
264 for signal in self._find_children(node, _glibns('signal')):
265 obj.signals.append(self._parse_function_common(signal, ast.Signal, obj))
267 self._namespace.append(obj)
269 def _parse_callback(self, node):
270 callback = self._parse_function_common(node, ast.Callback)
271 self._namespace.append(callback)
273 def _parse_function(self, node):
274 function = self._parse_function_common(node, ast.Function)
275 self._namespace.append(function)
277 def _parse_parameter(self, node):
278 typeval = self._parse_type(node)
279 param = ast.Parameter(node.attrib.get('name'),
281 node.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
282 node.attrib.get('transfer-ownership'),
283 node.attrib.get('allow-none') == '1',
284 node.attrib.get('scope'),
285 node.attrib.get('caller-allocates') == '1')
286 self._parse_generic_attribs(node, param)
289 def _parse_function_common(self, node, klass, parent=None):
290 name = node.attrib['name']
291 returnnode = node.find(_corens('return-value'))
293 raise ValueError('node %r has no return-value' % (name, ))
294 transfer = returnnode.attrib.get('transfer-ownership')
295 retval = ast.Return(self._parse_type(returnnode), transfer)
296 self._parse_generic_attribs(returnnode, retval)
299 throws = (node.attrib.get('throws') == '1')
301 if klass is ast.Callback:
302 func = klass(name, retval, parameters, throws,
303 node.attrib.get(_cns('type')))
304 elif klass is ast.Function:
305 identifier = node.attrib.get(_cns('identifier'))
306 func = klass(name, retval, parameters, throws, identifier)
307 elif klass is ast.VFunction:
308 func = klass(name, retval, parameters, throws)
309 elif klass is ast.Signal:
310 func = klass(name, retval, parameters,
311 when=node.attrib.get('when'),
312 no_recurse=node.attrib.get('no-recurse', '0') == '1',
313 detailed=node.attrib.get('detailed', '0') == '1',
314 action=node.attrib.get('action', '0') == '1',
315 no_hooks=node.attrib.get('no-hooks', '0') == '1')
319 func.shadows = node.attrib.get('shadows', None)
320 func.shadowed_by = node.attrib.get('shadowed-by', None)
321 func.moved_to = node.attrib.get('moved-to', None)
324 parameters_node = node.find(_corens('parameters'))
325 if (parameters_node is not None):
326 paramnode = self._find_first_child(parameters_node, _corens('instance-parameter'))
328 func.instance_parameter = self._parse_parameter(paramnode)
329 for paramnode in self._find_children(parameters_node, _corens('parameter')):
330 parameters.append(self._parse_parameter(paramnode))
331 for i, paramnode in enumerate(self._find_children(parameters_node,
332 _corens('parameter'))):
333 param = parameters[i]
334 self._parse_type_array_length(parameters, paramnode, param.type)
335 closure = paramnode.attrib.get('closure')
338 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
339 param.closure_name = parameters[idx].argname
340 destroy = paramnode.attrib.get('destroy')
343 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
344 param.destroy_name = parameters[idx].argname
346 self._parse_type_array_length(parameters, returnnode, retval.type)
348 self._parse_generic_attribs(node, func)
350 self._namespace.track(func)
353 def _parse_fields(self, node, obj):
355 names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
356 for child in node.getchildren():
357 if child.tag in names:
358 fieldobj = self._parse_field(child, obj)
362 def _parse_compound(self, cls, node):
363 compound = cls(node.attrib.get('name'),
364 ctype=node.attrib.get(_cns('type')),
365 disguised=node.attrib.get('disguised') == '1',
366 gtype_name=node.attrib.get(_glibns('type-name')),
367 get_type=node.attrib.get(_glibns('get-type')),
368 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
369 if node.attrib.get('foreign') == '1':
370 compound.foreign = True
371 self._parse_generic_attribs(node, compound)
372 if not self._types_only:
373 compound.fields.extend(self._parse_fields(node, compound))
374 for method in self._find_children(node, _corens('method')):
375 func = self._parse_function_common(method, ast.Function, compound)
376 func.is_method = True
377 compound.methods.append(func)
378 for i, fieldnode in enumerate(self._find_children(node, _corens('field'))):
379 field = compound.fields[i]
380 self._parse_type_array_length(compound.fields, fieldnode, field.type)
381 for func in self._find_children(node, _corens('function')):
382 compound.static_methods.append(
383 self._parse_function_common(func, ast.Function, compound))
384 for ctor in self._find_children(node, _corens('constructor')):
385 func = self._parse_function_common(ctor, ast.Function, compound)
386 func.is_constructor = True
387 compound.constructors.append(func)
390 def _parse_record(self, node, anonymous=False):
391 struct = self._parse_compound(ast.Record, node)
392 is_gtype_struct_for = node.attrib.get(_glibns('is-gtype-struct-for'))
393 if is_gtype_struct_for is not None:
394 struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
396 self._namespace.append(struct)
399 def _parse_union(self, node, anonymous=False):
400 union = self._parse_compound(ast.Union, node)
402 self._namespace.append(union)
405 def _parse_type_simple(self, typenode):
406 # ast.Fields can contain inline callbacks
407 if typenode.tag == _corens('callback'):
408 typeval = self._namespace.type_from_name(typenode.attrib['name'])
409 typeval.ctype = typenode.attrib.get(_cns('type'))
411 # ast.Arrays have their own toplevel XML
412 elif typenode.tag == _corens('array'):
413 array_type = typenode.attrib.get('name')
414 element_type = self._parse_type(typenode)
415 array_ctype = typenode.attrib.get(_cns('type'))
416 ret = ast.Array(array_type, element_type, ctype=array_ctype)
417 # zero-terminated defaults to true...
418 zero = typenode.attrib.get('zero-terminated')
419 if zero and zero == '0':
420 ret.zeroterminated = False
421 fixed_size = typenode.attrib.get('fixed-size')
423 ret.size = int(fixed_size)
426 elif typenode.tag == _corens('varargs'):
428 elif typenode.tag == _corens('type'):
429 name = typenode.attrib.get('name')
430 ctype = typenode.attrib.get(_cns('type'))
433 return ast.TypeUnknown()
434 return ast.Type(ctype=ctype)
435 elif name in ['GLib.List', 'GLib.SList']:
436 subchild = self._find_first_child(typenode,
437 map(_corens, ('callback', 'array',
439 if subchild is not None:
440 element_type = self._parse_type(typenode)
442 element_type = ast.TYPE_ANY
443 return ast.List(name, element_type, ctype=ctype)
444 elif name == 'GLib.HashTable':
445 subchildren = self._find_children(typenode, _corens('type'))
446 subchildren_types = map(self._parse_type_simple, subchildren)
447 while len(subchildren_types) < 2:
448 subchildren_types.append(ast.TYPE_ANY)
449 return ast.Map(subchildren_types[0], subchildren_types[1], ctype=ctype)
451 return self._namespace.type_from_name(name, ctype)
453 assert False, "Failed to parse inner type"
455 def _parse_type(self, node):
456 for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
457 typenode = node.find(name)
458 if typenode is not None:
459 return self._parse_type_simple(typenode)
460 assert False, "Failed to parse toplevel type"
462 def _parse_type_array_length(self, siblings, node, typeval):
463 """A hack necessary to handle the integer parameter/field indexes on
465 typenode = node.find(_corens('array'))
468 lenidx = typenode.attrib.get('length')
469 if lenidx is not None:
471 assert idx < len(siblings), "%r %d >= %d" % (parent, idx, len(siblings))
472 if isinstance(siblings[idx], ast.Field):
473 typeval.length_param_name = siblings[idx].name
475 typeval.length_param_name = siblings[idx].argname
477 def _parse_boxed(self, node):
478 obj = ast.Boxed(node.attrib[_glibns('name')],
479 gtype_name=node.attrib[_glibns('type-name')],
480 get_type=node.attrib[_glibns('get-type')],
481 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
482 self._parse_generic_attribs(node, obj)
485 self._namespace.append(obj)
488 for method in self._find_children(node, _corens('method')):
489 func = self._parse_function_common(method, ast.Function, obj)
490 func.is_method = True
491 obj.methods.append(func)
492 for ctor in self._find_children(node, _corens('constructor')):
493 obj.constructors.append(
494 self._parse_function_common(ctor, ast.Function, obj))
495 for callback in self._find_children(node, _corens('callback')):
497 self._parse_function_common(callback, ast.Callback, obj))
498 self._namespace.append(obj)
500 def _parse_field(self, node, parent):
502 anonymous_node = None
503 if node.tag in map(_corens, ('record', 'union')):
506 anonymous_elt = self._find_first_child(node, _corens('callback'))
507 if anonymous_elt is not None:
508 if anonymous_elt.tag == _corens('callback'):
509 anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
510 elif anonymous_elt.tag == _corens('record'):
511 anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
512 elif anonymous_elt.tag == _corens('union'):
513 anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
515 assert False, anonymous_elt.tag
517 assert node.tag == _corens('field'), node.tag
518 type_node = self._parse_type(node)
519 field = ast.Field(node.attrib.get('name'),
521 node.attrib.get('readable') != '0',
522 node.attrib.get('writable') == '1',
523 node.attrib.get('bits'),
524 anonymous_node=anonymous_node)
525 field.private = node.attrib.get('private') == '1'
526 field.parent = parent
527 self._parse_generic_attribs(node, field)
530 def _parse_property(self, node, parent):
531 prop = ast.Property(node.attrib['name'],
532 self._parse_type(node),
533 node.attrib.get('readable') != '0',
534 node.attrib.get('writable') == '1',
535 node.attrib.get('construct') == '1',
536 node.attrib.get('construct-only') == '1',
537 node.attrib.get('transfer-ownership'))
538 self._parse_generic_attribs(node, prop)
542 def _parse_member(self, node):
543 member = ast.Member(node.attrib['name'],
544 node.attrib['value'],
545 node.attrib.get(_cns('identifier')),
546 node.attrib.get(_glibns('nick')))
547 self._parse_generic_attribs(node, member)
550 def _parse_constant(self, node):
551 type_node = self._parse_type(node)
552 constant = ast.Constant(node.attrib['name'],
554 node.attrib['value'],
555 node.attrib.get(_cns('type')))
556 self._parse_generic_attribs(node, constant)
557 self._namespace.append(constant)
559 def _parse_enumeration_bitfield(self, node):
560 name = node.attrib.get('name')
561 ctype = node.attrib.get(_cns('type'))
562 get_type = node.attrib.get(_glibns('get-type'))
563 type_name = node.attrib.get(_glibns('type-name'))
564 glib_error_domain = node.attrib.get(_glibns('error-domain'))
565 if node.tag == _corens('bitfield'):
570 obj = klass(name, ctype,
572 gtype_name=type_name,
574 obj.error_domain = glib_error_domain
576 self._parse_generic_attribs(node, obj)
579 self._namespace.append(obj)
582 for member_node in self._find_children(node, _corens('member')):
583 member = self._parse_member(member_node)
585 members.append(member)
586 for func_node in self._find_children(node, _corens('function')):
587 func = self._parse_function_common(func_node, ast.Function)
588 obj.static_methods.append(func)
589 self._namespace.append(obj)