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._shared_libraries = []
50 self._includes = set()
51 self._pkgconfig_packages = set()
52 self._namespace = None
53 self._filename_stack = []
57 def parse(self, filename):
58 filename = os.path.abspath(filename)
59 self._filename_stack.append(filename)
60 tree = parse(filename)
62 self._filename_stack.pop()
64 def parse_tree(self, tree):
65 self._includes.clear()
66 self._namespace = None
67 self._shared_libraries = []
68 self._pkgconfig_packages = set()
69 self._c_includes = set()
71 self._parse_api(tree.getroot())
73 def get_namespace(self):
74 return self._namespace
76 def get_shared_libraries(self):
77 return self._shared_libraries
79 def get_includes(self):
82 def get_c_includes(self):
83 return self._c_includes
85 def get_c_prefix(self):
88 def get_pkgconfig_packages(self):
89 if not hasattr(self, '_pkgconfig_packages'):
90 self._pkgconfig_packages = []
91 return self._pkgconfig_packages
94 return parse(self._filename)
98 def _find_first_child(self, node, name_or_names):
99 if isinstance(name_or_names, str):
100 for child in node.getchildren():
101 if child.tag == name_or_names:
104 for child in node.getchildren():
105 if child.tag in name_or_names:
109 def _find_children(self, node, name):
110 return [child for child in node.getchildren() if child.tag == name]
112 def _get_current_file(self):
113 if not self._filename_stack:
115 cwd = os.getcwd() + os.sep
116 curfile = self._filename_stack[-1]
117 if curfile.startswith(cwd):
118 return curfile[len(cwd):]
121 def _parse_api(self, root):
122 assert root.tag == _corens('repository')
123 version = root.attrib['version']
124 if version != COMPATIBLE_GIR_VERSION:
125 raise SystemExit("%s: Incompatible version %s (supported: %s)" \
126 % (self._get_current_file(),
127 version, COMPATIBLE_GIR_VERSION))
129 for node in root.getchildren():
130 if node.tag == _corens('include'):
131 self._parse_include(node)
132 elif node.tag == _corens('package'):
133 self._parse_pkgconfig_package(node)
134 elif node.tag == _cns('include'):
135 self._parse_c_include(node)
137 ns = root.find(_corens('namespace'))
138 assert ns is not None
139 identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
140 if identifier_prefixes:
141 identifier_prefixes = identifier_prefixes.split(',')
142 symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
144 symbol_prefixes = symbol_prefixes.split(',')
145 self._namespace = ast.Namespace(ns.attrib['name'],
146 ns.attrib['version'],
147 identifier_prefixes=identifier_prefixes,
148 symbol_prefixes=symbol_prefixes)
149 if 'shared-library' in ns.attrib:
150 self._shared_libraries.extend(
151 ns.attrib['shared-library'].split(','))
154 _corens('alias'): self._parse_alias,
155 _corens('bitfield'): self._parse_enumeration_bitfield,
156 _corens('callback'): self._parse_callback,
157 _corens('class'): self._parse_object_interface,
158 _corens('enumeration'): self._parse_enumeration_bitfield,
159 _corens('interface'): self._parse_object_interface,
160 _corens('record'): self._parse_record,
161 _corens('union'): self._parse_union,
162 _glibns('boxed'): self._parse_boxed,
165 if not self._types_only:
166 parser_methods[_corens('constant')] = self._parse_constant
167 parser_methods[_corens('function')] = self._parse_function
169 for node in ns.getchildren():
170 method = parser_methods.get(node.tag)
171 if method is not None:
174 def _parse_include(self, node):
175 include = ast.Include(node.attrib['name'],
176 node.attrib['version'])
177 self._includes.add(include)
179 def _parse_pkgconfig_package(self, node):
180 self._pkgconfig_packages.add(node.attrib['name'])
182 def _parse_c_include(self, node):
183 self._c_includes.add(node.attrib['name'])
185 def _parse_alias(self, node):
186 typeval = self._parse_type(node)
187 alias = ast.Alias(node.attrib['name'],
189 node.attrib.get(_cns('type')))
190 self._parse_generic_attribs(node, alias)
191 self._namespace.append(alias)
193 def _parse_generic_attribs(self, node, obj):
194 assert isinstance(obj, ast.Annotated)
195 introspectable = node.attrib.get('introspectable')
197 obj.introspectable = int(introspectable) > 0
200 doc = node.find(_corens('doc'))
204 version = node.attrib.get('version')
206 obj.version = version
207 deprecated = node.attrib.get('deprecated')
209 obj.deprecated = deprecated
210 deprecated_version = node.attrib.get('deprecated-version')
211 if deprecated_version:
212 obj.deprecated_version = deprecated_version
214 def _parse_object_interface(self, node):
215 parent = node.attrib.get('parent')
217 parent_type = self._namespace.type_from_name(parent)
221 ctor_args = [node.attrib['name'],
223 ctor_kwargs = {'gtype_name': node.attrib[_glibns('type-name')],
224 'get_type': node.attrib[_glibns('get-type')],
225 'c_symbol_prefix': node.attrib.get(_cns('symbol-prefix')),
226 'ctype': node.attrib.get(_cns('type'))}
227 if node.tag == _corens('interface'):
228 klass = ast.Interface
229 elif node.tag == _corens('class'):
231 is_abstract = node.attrib.get('abstract')
232 is_abstract = is_abstract and is_abstract != '0'
233 ctor_kwargs['is_abstract'] = is_abstract
235 raise AssertionError(node)
237 obj = klass(*ctor_args, **ctor_kwargs)
238 self._parse_generic_attribs(node, obj)
239 type_struct = node.attrib.get(_glibns('type-struct'))
241 obj.glib_type_struct = self._namespace.type_from_name(type_struct)
242 if klass == ast.Class:
243 is_fundamental = node.attrib.get(_glibns('fundamental'))
244 if is_fundamental and is_fundamental != '0':
245 obj.fundamental = True
246 for func_id in ['ref-func', 'unref-func',
247 'set-value-func', 'get-value-func']:
248 func_name = node.attrib.get(_glibns(func_id))
249 obj.__dict__[func_id.replace('-', '_')] = func_name
252 self._namespace.append(obj)
255 for iface in self._find_children(node, _corens('implements')):
256 obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
257 for iface in self._find_children(node, _corens('prerequisite')):
258 obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
259 for func_node in self._find_children(node, _corens('function')):
260 func = self._parse_function_common(func_node, ast.Function, obj)
261 obj.static_methods.append(func)
262 for method in self._find_children(node, _corens('method')):
263 func = self._parse_function_common(method, ast.Function, obj)
264 func.is_method = True
265 obj.methods.append(func)
266 for method in self._find_children(node, _corens('virtual-method')):
267 func = self._parse_function_common(method, ast.VFunction, obj)
268 self._parse_generic_attribs(method, func)
269 func.is_method = True
270 func.invoker = method.get('invoker')
271 obj.virtual_methods.append(func)
272 for ctor in self._find_children(node, _corens('constructor')):
273 func = self._parse_function_common(ctor, ast.Function, obj)
274 func.is_constructor = True
275 obj.constructors.append(func)
276 obj.fields.extend(self._parse_fields(node, obj))
277 for prop in self._find_children(node, _corens('property')):
278 obj.properties.append(self._parse_property(prop, obj))
279 for signal in self._find_children(node, _glibns('signal')):
280 obj.signals.append(self._parse_function_common(signal, ast.Signal, obj))
282 self._namespace.append(obj)
284 def _parse_callback(self, node):
285 callback = self._parse_function_common(node, ast.Callback)
286 self._namespace.append(callback)
288 def _parse_function(self, node):
289 function = self._parse_function_common(node, ast.Function)
290 self._namespace.append(function)
292 def _parse_function_common(self, node, klass, parent=None):
293 name = node.attrib['name']
294 returnnode = node.find(_corens('return-value'))
296 raise ValueError('node %r has no return-value' % (name, ))
297 transfer = returnnode.attrib.get('transfer-ownership')
298 retval = ast.Return(self._parse_type(returnnode), transfer)
299 self._parse_generic_attribs(returnnode, retval)
302 throws = (node.attrib.get('throws') == '1')
304 if klass is ast.Callback:
305 func = klass(name, retval, parameters, throws,
306 node.attrib.get(_cns('type')))
307 elif klass is ast.Function:
308 identifier = node.attrib.get(_cns('identifier'))
309 func = klass(name, retval, parameters, throws, identifier)
310 elif klass is ast.VFunction:
311 func = klass(name, retval, parameters, throws)
312 elif klass is ast.Signal:
313 func = klass(name, retval, parameters,
314 when=node.attrib.get('when'),
315 no_recurse=node.attrib.get('no-recurse', '0') == '1',
316 detailed=node.attrib.get('detailed', '0') == '1',
317 action=node.attrib.get('action', '0') == '1',
318 no_hooks=node.attrib.get('no-hooks', '0') == '1')
322 func.shadows = node.attrib.get('shadows', None)
323 func.shadowed_by = node.attrib.get('shadowed-by', None)
324 func.moved_to = node.attrib.get('moved-to', None)
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 typeval = self._parse_type(paramnode)
331 param = ast.Parameter(paramnode.attrib.get('name'),
333 paramnode.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
334 paramnode.attrib.get('transfer-ownership'),
335 paramnode.attrib.get('allow-none') == '1',
336 paramnode.attrib.get('scope'),
337 paramnode.attrib.get('caller-allocates') == '1')
338 self._parse_generic_attribs(paramnode, param)
339 parameters.append(param)
340 for i, paramnode in enumerate(self._find_children(parameters_node,
341 _corens('parameter'))):
342 param = parameters[i]
343 self._parse_type_second_pass(func, paramnode, param.type)
344 closure = paramnode.attrib.get('closure')
347 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
348 param.closure_name = parameters[idx].argname
349 destroy = paramnode.attrib.get('destroy')
352 assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
353 param.destroy_name = parameters[idx].argname
355 self._parse_type_second_pass(func, returnnode, retval.type)
357 self._parse_generic_attribs(node, func)
359 self._namespace.track(func)
362 def _parse_fields(self, node, obj):
364 names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
365 for child in node.getchildren():
366 if child.tag in names:
367 fieldobj = self._parse_field(child, obj)
371 def _parse_compound(self, cls, node):
372 compound = cls(node.attrib.get('name'),
373 ctype=node.attrib.get(_cns('type')),
374 disguised=node.attrib.get('disguised') == '1',
375 gtype_name=node.attrib.get(_glibns('type-name')),
376 get_type=node.attrib.get(_glibns('get-type')),
377 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
378 if node.attrib.get('foreign') == '1':
379 compound.foreign = True
380 self._parse_generic_attribs(node, compound)
381 if not self._types_only:
382 compound.fields.extend(self._parse_fields(node, compound))
383 for method in self._find_children(node, _corens('method')):
384 func = self._parse_function_common(method, ast.Function, compound)
385 func.is_method = True
386 compound.methods.append(func)
387 for func in self._find_children(node, _corens('function')):
388 compound.static_methods.append(
389 self._parse_function_common(func, ast.Function, compound))
390 for ctor in self._find_children(node, _corens('constructor')):
391 func = self._parse_function_common(ctor, ast.Function, compound)
392 func.is_constructor = True
393 compound.constructors.append(func)
396 def _parse_record(self, node, anonymous=False):
397 struct = self._parse_compound(ast.Record, node)
398 is_gtype_struct_for = node.attrib.get(_glibns('is-gtype-struct-for'))
399 if is_gtype_struct_for is not None:
400 struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
402 self._namespace.append(struct)
405 def _parse_union(self, node, anonymous=False):
406 union = self._parse_compound(ast.Union, node)
408 self._namespace.append(union)
411 def _parse_type_simple(self, typenode):
412 # ast.Fields can contain inline callbacks
413 if typenode.tag == _corens('callback'):
414 typeval = self._namespace.type_from_name(typenode.attrib['name'])
415 typeval.ctype = typenode.attrib.get(_cns('type'))
417 # ast.Arrays have their own toplevel XML
418 elif typenode.tag == _corens('array'):
419 array_type = typenode.attrib.get('name')
420 element_type = self._parse_type(typenode)
421 array_ctype = typenode.attrib.get(_cns('type'))
422 ret = ast.Array(array_type, element_type, ctype=array_ctype)
423 # zero-terminated defaults to true...
424 zero = typenode.attrib.get('zero-terminated')
425 if zero and zero == '0':
426 ret.zeroterminated = False
427 fixed_size = typenode.attrib.get('fixed-size')
429 ret.size = int(fixed_size)
432 elif typenode.tag == _corens('varargs'):
434 elif typenode.tag == _corens('type'):
435 name = typenode.attrib.get('name')
436 ctype = typenode.attrib.get(_cns('type'))
439 return ast.TypeUnknown()
440 return ast.Type(ctype=ctype)
441 elif name in ['GLib.List', 'GLib.SList']:
442 subchild = self._find_first_child(typenode,
443 map(_corens, ('callback', 'array', 'varargs', 'type')))
444 if subchild is not None:
445 element_type = self._parse_type(typenode)
447 element_type = ast.TYPE_ANY
448 return ast.List(name, element_type, ctype=ctype)
449 elif name == 'GLib.HashTable':
450 subchildren = self._find_children(typenode, _corens('type'))
451 subchildren_types = map(self._parse_type_simple, subchildren)
452 while len(subchildren_types) < 2:
453 subchildren_types.append(ast.TYPE_ANY)
454 return ast.Map(subchildren_types[0],
455 subchildren_types[1],
458 return self._namespace.type_from_name(name, ctype)
460 assert False, "Failed to parse inner type"
462 def _parse_type(self, node):
463 for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
464 typenode = node.find(name)
465 if typenode is not None:
466 return self._parse_type_simple(typenode)
467 assert False, "Failed to parse toplevel type"
469 def _parse_type_second_pass(self, parent, node, typeval):
470 """A hack necessary to handle the integer parameter indexes on
472 typenode = node.find(_corens('array'))
475 lenidx = typenode.attrib.get('length')
476 if lenidx is not None:
478 assert idx < len(parent.parameters), "%r %d >= %d" \
479 % (parent, idx, len(parent.parameters))
480 typeval.length_param_name = parent.parameters[idx].argname
482 def _parse_boxed(self, node):
483 obj = ast.Boxed(node.attrib[_glibns('name')],
484 gtype_name=node.attrib[_glibns('type-name')],
485 get_type=node.attrib[_glibns('get-type')],
486 c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
487 self._parse_generic_attribs(node, obj)
490 self._namespace.append(obj)
493 for method in self._find_children(node, _corens('method')):
494 func = self._parse_function_common(method, ast.Function, obj)
495 func.is_method = True
496 obj.methods.append(func)
497 for ctor in self._find_children(node, _corens('constructor')):
498 obj.constructors.append(
499 self._parse_function_common(ctor, ast.Function, obj))
500 for callback in self._find_children(node, _corens('callback')):
502 self._parse_function_common(callback, ast.Callback, obj))
503 self._namespace.append(obj)
505 def _parse_field(self, node, parent):
507 anonymous_node = None
508 if node.tag in map(_corens, ('record', 'union')):
511 anonymous_elt = self._find_first_child(node, _corens('callback'))
512 if anonymous_elt is not None:
513 if anonymous_elt.tag == _corens('callback'):
514 anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
515 elif anonymous_elt.tag == _corens('record'):
516 anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
517 elif anonymous_elt.tag == _corens('union'):
518 anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
520 assert False, anonymous_elt.tag
522 assert node.tag == _corens('field'), node.tag
523 type_node = self._parse_type(node)
524 field = ast.Field(node.attrib.get('name'),
526 node.attrib.get('readable') != '0',
527 node.attrib.get('writable') == '1',
528 node.attrib.get('bits'),
529 anonymous_node=anonymous_node)
530 field.private = node.attrib.get('private') == '1'
531 field.parent = parent
532 self._parse_generic_attribs(node, field)
535 def _parse_property(self, node, parent):
536 prop = ast.Property(node.attrib['name'],
537 self._parse_type(node),
538 node.attrib.get('readable') != '0',
539 node.attrib.get('writable') == '1',
540 node.attrib.get('construct') == '1',
541 node.attrib.get('construct-only') == '1',
542 node.attrib.get('transfer-ownership'))
543 self._parse_generic_attribs(node, prop)
547 def _parse_member(self, node):
548 member = ast.Member(node.attrib['name'],
549 node.attrib['value'],
550 node.attrib.get(_cns('identifier')),
551 node.attrib.get(_glibns('nick')))
552 self._parse_generic_attribs(node, member)
555 def _parse_constant(self, node):
556 type_node = self._parse_type(node)
557 constant = ast.Constant(node.attrib['name'],
559 node.attrib['value'],
560 node.attrib.get(_cns('type')))
561 self._parse_generic_attribs(node, constant)
562 self._namespace.append(constant)
564 def _parse_enumeration_bitfield(self, node):
565 name = node.attrib.get('name')
566 ctype = node.attrib.get(_cns('type'))
567 get_type = node.attrib.get(_glibns('get-type'))
568 type_name = node.attrib.get(_glibns('type-name'))
569 glib_error_domain = node.attrib.get(_glibns('error-domain'))
570 if node.tag == _corens('bitfield'):
575 obj = klass(name, ctype,
577 gtype_name=type_name,
579 obj.error_domain = glib_error_domain
581 self._parse_generic_attribs(node, obj)
584 self._namespace.append(obj)
587 for member in self._find_children(node, _corens('member')):
588 members.append(self._parse_member(member))
589 for func_node in self._find_children(node, _corens('function')):
590 func = self._parse_function_common(func_node, ast.Function)
591 obj.static_methods.append(func)
592 self._namespace.append(obj)