2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Johan Dahlin
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program 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
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 from . import cgobject
26 from .odict import odict
27 from .ast import (Callback, Enum, Function, Parameter, Property, Return,
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30 GLibInterface, GLibObject, GLibSignal)
33 # Copied from h2defs.py
34 _upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
35 _upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
36 _upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
38 def to_underscores(name):
39 """Converts a typename to the equivalent underscores name.
40 This is used to form the type conversion macros and enum/flag
42 name = _upperstr_pat1.sub(r'\1_\2', name)
43 name = _upperstr_pat2.sub(r'\1_\2', name)
44 name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
47 _libtool_pat = re.compile("dlname='([A-z0-9\.]+)'\n")
49 def resolve_libtool(libname):
50 data = open(libname).read()
51 filename = _libtool_pat.search(data).groups()[0]
52 libname = os.path.join(os.path.dirname(libname),
59 class GLibTransformer(object):
60 def __init__(self, namespace_name):
61 self._namespace_name = namespace_name
62 self._output_ns = odict()
69 return self._output_ns.values()
71 def load_library(self, libname):
72 if libname.endswith('.la'):
73 libname = resolve_libtool(libname)
74 self._library = ctypes.cdll.LoadLibrary(libname)
76 def parse(self, nodes):
78 self._parse_node(node)
80 # Second round, associate GtkButtonClass with GtkButton
81 for node in self._output_ns.values():
82 if isinstance(node, Struct):
83 self._pair_class_struct(node)
85 def register_include(self, filename):
86 if filename.endswith('.gir'):
87 from .girparser import GIRParser
88 parser = GIRParser(filename)
89 elif filename.endswith('.gidl'):
90 from .gidlparser import GIDLParser
91 parser = GIDLParser(filename)
93 raise NotImplementedError(filename)
94 nsname = parser.get_namespace_name()
95 for node in parser.get_nodes():
96 self._type_names[node.type_name] = (nsname, node)
100 def _add_attribute(self, node, replace=False):
101 node_name = node.name
102 if node_name in self._output_ns and not replace:
104 self._output_ns[node_name] = node
106 def _remove_attribute(self, name):
107 del self._output_ns[name]
109 def _get_attribute(self, name):
110 return self._output_ns.get(name)
112 def _register_internal_type(self, type_name, node):
113 self._type_names[type_name] = (None, node)
115 def _strip_namespace_func(self, name):
117 prefix = self._namespace_name.lower() + '_'
119 if name.startswith(prefix):
120 name = orig_name[len(prefix):]
123 def _strip_namespace_object(self, name):
125 prefix = self._namespace_name.lower()
127 if name.startswith(prefix):
128 name = orig_name[len(prefix):]
131 def _resolve_type_name(self, type_name):
132 item = self._type_names.get(type_name)
137 return '%s.%s' % (nsname, item.name)
140 def _resolve_param_type(self, ptype):
141 type_name = ptype.name.replace('*', '')
142 ptype.name = self._resolve_type_name(type_name)
145 def _parse_node(self, node):
146 if isinstance(node, Enum):
147 self._parse_enum(node)
148 elif isinstance(node, Function):
149 self._parse_function(node)
150 elif isinstance(node, Struct):
151 self._parse_struct(node)
152 elif isinstance(node, Callback):
153 self._parse_callback(node)
155 print 'GOBJECT BUILDER: Unhandled node:', node
157 def _parse_enum(self, enum):
158 enum.name = self._strip_namespace_object(enum.name)
159 self._add_attribute(enum)
161 def _parse_function(self, func):
162 if self._parse_get_type_function(func):
164 elif self._parse_constructor(func):
166 elif self._parse_method(func):
169 self._parse_parameters(func.parameters)
170 func.retval.type = self._resolve_param_type(func.retval.type)
172 func.name = self._strip_namespace_func(func.name)
173 self._add_attribute(func)
175 def _parse_parameters(self, parameters):
176 for parameter in parameters:
177 parameter.type = self._resolve_param_type(parameter.type)
179 def _parse_get_type_function(self, func):
180 # GType *_get_type(void)
182 if not symbol.endswith('_get_type'):
184 if func.retval.type.name != 'GType':
189 func = getattr(self._library, symbol)
190 func.restype = cgobject.GType
193 self._introspect_type(type_id, symbol)
196 def _parse_method(self, func):
197 if not func.parameters:
200 # FIXME: This is hackish, we should preserve the pointer structures
201 # here, so we can find pointers to objects and not just
202 # pointers to anything
203 first_arg = func.parameters[0].type.name
204 if first_arg.count('*') != 1:
207 object_name = first_arg.replace('*', '')
208 return self._parse_method_common(func, object_name, is_method=True)
210 def _parse_constructor(self, func):
211 # FIXME: This is hackish, we should preserve the pointer structures
212 # here, so we can find pointers to objects and not just
213 # pointers to anything
214 rtype = func.retval.type
215 if rtype.name.count('*') != 1:
218 object_name = rtype.name.replace('*', '')
219 return self._parse_method_common(func, object_name, is_method=False)
221 def _parse_method_common(self, func, object_name, is_method):
222 orig_name = object_name
223 if object_name.lower().startswith(self._namespace_name.lower()):
224 object_name = object_name[len(self._namespace_name):]
225 class_ = self._get_attribute(object_name)
226 if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
229 # GtkButton -> gtk_button_, so we can figure out the method name
230 prefix = to_underscores(orig_name).lower() + '_'
231 if not func.name.startswith(prefix):
234 # Strip namespace and object prefix: gtk_window_new -> new
235 func.name = func.name[len(prefix):]
237 class_.methods.append(func)
239 class_.constructors.append(func)
240 self._parse_parameters(func.parameters)
241 func.retval.type = self._resolve_param_type(func.retval.type)
244 def _parse_struct(self, struct):
245 self._add_attribute(struct)
247 def _parse_callback(self, callback):
248 self._add_attribute(callback)
250 def _pair_class_struct(self, class_node):
251 name = class_node.name
252 if (name.endswith('Class') or
253 name.endswith('Iface')):
255 elif name.endswith('Interface'):
260 node = self._output_ns.get(self._resolve_type_name(name))
261 del self._output_ns[class_node.name]
262 for field in class_node.fields[1:]:
263 node.fields.append(field)
265 def _introspect_type(self, type_id, symbol):
266 fundamental_type_id = cgobject.type_fundamental(type_id)
267 if (fundamental_type_id == cgobject.TYPE_ENUM or
268 fundamental_type_id == cgobject.TYPE_FLAGS):
269 self._introspect_enum(fundamental_type_id, type_id, symbol)
270 elif fundamental_type_id == cgobject.TYPE_OBJECT:
271 self._introspect_object(type_id, symbol)
272 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
273 self._introspect_interface(type_id, symbol)
274 elif fundamental_type_id == cgobject.TYPE_BOXED:
275 self._introspect_boxed(type_id, symbol)
277 print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
279 def _introspect_enum(self, ftype_id, type_id, symbol):
280 type_class = cgobject.type_class_ref(type_id)
281 if type_class is None:
285 for enum_value in type_class.get_values():
286 members.append(GLibEnumMember(enum_value.value_name,
288 enum_value.value_nick))
290 klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
291 type_name = cgobject.type_name(type_id)
292 node = klass(self._strip_namespace_object(type_name),
293 members, type_name, symbol)
294 self._add_attribute(node, replace=True)
295 self._register_internal_type(type_name, node)
297 def _introspect_object(self, type_id, symbol):
298 type_name = cgobject.type_name(type_id)
299 parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
300 node = GLibObject(self._strip_namespace_object(type_name),
301 self._resolve_type_name(parent_type_name),
303 self._introspect_properties(node, type_id)
304 self._introspect_signals(node, type_id)
305 self._add_attribute(node)
306 self._remove_attribute(type_name)
307 self._register_internal_type(type_name, node)
309 def _introspect_interface(self, type_id, symbol):
310 type_name = cgobject.type_name(type_id)
311 node = GLibInterface(self._strip_namespace_object(type_name),
313 self._introspect_properties(node, type_id)
314 self._introspect_signals(node, type_id)
315 self._add_attribute(node)
316 self._register_internal_type(type_name, node)
318 def _introspect_boxed(self, type_id, symbol):
319 type_name = cgobject.type_name(type_id)
320 node = GLibBoxed(self._strip_namespace_object(type_name),
322 self._add_attribute(node)
323 self._register_internal_type(type_name, node)
325 def _introspect_properties(self, node, type_id):
326 fundamental_type_id = cgobject.type_fundamental(type_id)
327 if fundamental_type_id == cgobject.TYPE_OBJECT:
328 pspecs = cgobject.object_class_list_properties(type_id)
329 elif fundamental_type_id == cgobject.TYPE_INTERFACE:
330 pspecs = cgobject.object_interface_list_properties(type_id)
335 if pspec.owner_type != type_id:
337 node.properties.append(Property(
339 cgobject.type_name(pspec.value_type)))
341 def _introspect_signals(self, node, type_id):
342 for signal_info in cgobject.signal_list(type_id):
343 return_ = Return(cgobject.type_name(signal_info.return_type))
344 signal = GLibSignal(signal_info.signal_name, return_)
345 for i, parameter in enumerate(signal_info.get_params()):
349 name = 'p%s' % (i-1,)
350 signal.parameters.append(
351 Parameter(name, cgobject.type_name(parameter)))
352 node.signals.append(signal)