Be less verbose about source types, we handle most of them now anyway
[gnome.gobject-introspection] / giscanner / transformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
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.
9 #
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.
14 #
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
18 # 02110-1301, USA.
19 #
20
21 import os
22 import re
23
24 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
25                            Parameter, Return, Array, Struct, Field,
26                            Type, Alias, Interface, Class, Node, Union,
27                            List, Map, Varargs, Constant, type_name_from_ctype,
28                            type_names, default_array_types, default_out_types,
29                            TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
30 from giscanner.config import DATADIR
31 from .glibast import GLibBoxed
32 from giscanner.sourcescanner import (
33     SourceSymbol, ctype_name, CTYPE_POINTER,
34     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
35     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
36     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
37     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
38     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
39     TYPE_QUALIFIER_CONST)
40 from .odict import odict
41 from .utils import strip_common_prefix, to_underscores
42
43 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
44                       + [DATADIR, '/usr/share'] if x]
45
46
47 class SkipError(Exception):
48     pass
49
50
51 class Names(object):
52     names = property(lambda self: self._names)
53     aliases = property(lambda self: self._aliases)
54     type_names = property(lambda self: self._type_names)
55     ctypes = property(lambda self: self._ctypes)
56
57     def __init__(self):
58         super(Names, self).__init__()
59         self._names = odict() # Maps from GIName -> (namespace, node)
60         self._aliases = {} # Maps from GIName -> GIName
61         self._type_names = {} # Maps from GTName -> (namespace, node)
62         self._ctypes = {} # Maps from CType -> (namespace, node)
63
64
65 class Transformer(object):
66
67     def __init__(self, generator, namespace_name, namespace_version):
68         self.generator = generator
69         self._namespace = Namespace(namespace_name, namespace_version)
70         self._names = Names()
71         self._typedefs_ns = {}
72         self._strip_prefix = ''
73         self._includes = set()
74         self._includepaths = []
75
76         self._list_ctypes = []
77         self._map_ctypes = []
78
79     def get_names(self):
80         return self._names
81
82     def get_includes(self):
83         return self._includes
84
85     def set_container_types(self, list_ctypes, map_ctypes):
86         self._list_ctypes = list_ctypes
87         self._map_ctypes = map_ctypes
88
89     def set_strip_prefix(self, strip_prefix):
90         self._strip_prefix = strip_prefix
91
92     def parse(self):
93         nodes = []
94         for symbol in self.generator.get_symbols():
95             node = self._traverse_one(symbol)
96             self._add_node(node)
97         return self._namespace
98
99     def set_include_paths(self, paths):
100         self._includepaths = list(paths)
101
102     def register_include(self, include, path=None):
103         if path is None:
104             girname = '%s-%s.gir' % (include.name, include.version)
105             searchdirs = list(self._includepaths)
106             searchdirs.extend([os.path.join(d, 'gir')
107                                for d in _xdg_data_dirs])
108             for d in searchdirs:
109                 path = os.path.join(d, girname)
110                 if os.path.exists(path):
111                     break
112                 path = None
113             if not path:
114                 raise ValueError("Couldn't find include %r (search path: %r)"\
115                                      % (girname, searchdirs))
116         d = os.path.dirname(path)
117         self._includes.add(include)
118         from .girparser import GIRParser
119         parser = GIRParser(path)
120         for include in parser.get_includes():
121             self.register_include(include)
122         nsname = parser.get_namespace().name
123         for node in parser.get_namespace().nodes:
124             if isinstance(node, Alias):
125                 self._names.aliases[node.name] = (nsname, node)
126             elif isinstance(node, (GLibBoxed, Interface, Class)):
127                 self._names.type_names[node.type_name] = (nsname, node)
128             self._names.names[node.name] = (nsname, node)
129             if hasattr(node, 'ctype'):
130                 self._names.ctypes[node.ctype] = (nsname, node)
131             elif hasattr(node, 'symbol'):
132                 self._names.ctypes[node.symbol] = (nsname, node)
133
134     # Private
135
136     def _add_node(self, node):
137         if node is None:
138             return
139         if node.name.startswith('_'):
140             return
141         self._namespace.nodes.append(node)
142         self._names.names[node.name] = (None, node)
143
144     def _strip_namespace_func(self, name):
145         prefix = self._namespace.name.lower() + '_'
146         if name.lower().startswith(prefix):
147             name = name[len(prefix):]
148         else:
149             prefix = to_underscores(self._namespace.name).lower() + '_'
150             if name.lower().startswith(prefix):
151                 name = name[len(prefix):]
152         return self.remove_prefix(name, isfunction=True)
153
154     def remove_prefix(self, name, isfunction=False):
155         # when --strip-prefix=g:
156         #   GHashTable -> HashTable
157         #   g_hash_table_new -> hash_table_new
158         prefix = self._strip_prefix.lower()
159         if isfunction:
160             prefix += '_'
161         if len(name) > len(prefix) and name.lower().startswith(prefix):
162             name = name[len(prefix):]
163
164         while name.startswith('_'):
165             name = name[1:]
166         return name
167
168     def _traverse_one(self, symbol, stype=None):
169         assert isinstance(symbol, SourceSymbol), symbol
170
171         if stype is None:
172             stype = symbol.type
173         if stype == CSYMBOL_TYPE_FUNCTION:
174             try:
175                 return self._create_function(symbol)
176             except SkipError:
177                 return
178         elif stype == CSYMBOL_TYPE_TYPEDEF:
179             return self._create_typedef(symbol)
180         elif stype == CSYMBOL_TYPE_STRUCT:
181             return self._create_struct(symbol)
182         elif stype == CSYMBOL_TYPE_ENUM:
183             return self._create_enum(symbol)
184         elif stype == CSYMBOL_TYPE_OBJECT:
185             return self._create_object(symbol)
186         elif stype == CSYMBOL_TYPE_MEMBER:
187             return self._create_member(symbol)
188         elif stype == CSYMBOL_TYPE_UNION:
189             return self._create_union(symbol)
190         elif stype == CSYMBOL_TYPE_CONST:
191             return self._create_const(symbol)
192         else:
193             raise NotImplementedError(
194                 'Transformer: unhandled symbol: %r' % (symbol, ))
195
196     def _create_enum(self, symbol):
197         members = []
198         for child in symbol.base_type.child_list:
199             name = strip_common_prefix(symbol.ident, child.ident).lower()
200             members.append(Member(name,
201                                   child.const_int,
202                                   child.ident))
203
204         enum_name = self.remove_prefix(symbol.ident)
205         enum = Enum(enum_name, symbol.ident, members)
206         self._names.type_names[symbol.ident] = (None, enum)
207         return enum
208
209     def _create_object(self, symbol):
210         return Member(symbol.ident, symbol.base_type.name,
211                       symbol.ident)
212
213     def _parse_deprecated(self, node, directives):
214         deprecated = directives.get('deprecated', False)
215         if deprecated:
216             deprecated_value = deprecated[0]
217             if ':' in deprecated_value:
218                 # Split out gtk-doc version
219                 (node.deprecated_version, node.deprecated) = \
220                     [x.strip() for x in deprecated_value.split(':', 1)]
221             else:
222                 # No version, just include str
223                 node.deprecated = deprecated_value.strip()
224
225     def _pair_array(self, params, array):
226         if not array.type.length_param_name:
227             return
228         target_name = array.type.length_param_name
229         for i, param in enumerate(params):
230             if param.name == array.type.length_param_name:
231                 array.type.length_param_index = i
232                 return
233         raise ValueError("Unmatched length parameter name %r"\
234                              % (target_name, ))
235
236     def _pair_annotations(self, params):
237         names = {}
238         for param in params:
239             if param.name in names:
240                 raise ValueError("Duplicate parameter name %r"\
241                                      % (param.name, ))
242             names[param.name] = 1
243             if isinstance(param.type, Array):
244                 self._pair_array(params, param)
245
246     # We take the annotations from the parser as strings; here we
247     # want to split them into components, so:
248     # (transfer full) -> {'transfer' : [ 'full' ]}
249
250     def _parse_options(self, options):
251         ret = {}
252         ws_re = re.compile(r'\s+')
253         for opt in options:
254             items = ws_re.split(opt)
255             ret[items[0]] = items[1:]
256         return ret
257
258     def _create_function(self, symbol):
259         directives = symbol.directives()
260         parameters = list(self._create_parameters(
261             symbol.base_type, directives))
262         self._pair_annotations(parameters)
263         return_ = self._create_return(symbol.base_type.base_type,
264                                       directives.get('return', {}))
265         name = self._strip_namespace_func(symbol.ident)
266         func = Function(name, return_, parameters, symbol.ident)
267         self._parse_deprecated(func, directives)
268         return func
269
270     def _create_source_type(self, source_type):
271         if source_type is None:
272             return 'None'
273         if source_type.type == CTYPE_VOID:
274             value = 'void'
275         elif source_type.type == CTYPE_BASIC_TYPE:
276             value = source_type.name
277         elif source_type.type == CTYPE_TYPEDEF:
278             value = source_type.name
279         elif source_type.type == CTYPE_ARRAY:
280             return self._create_source_type(source_type.base_type)
281         elif source_type.type == CTYPE_POINTER:
282             value = self._create_source_type(source_type.base_type) + '*'
283         else:
284             value = 'any'
285         return value
286
287     def _create_parameters(self, base_type, directives=None):
288         if directives is None:
289             dirs = {}
290         else:
291             dirs = directives
292
293         # warn if we see annotations for unknown parameters
294         param_names = set(child.ident for child in base_type.child_list)
295         dirs_for = set(dirs)
296         dirs_for = dirs_for.difference(param_names)
297         dirs_for.discard('return')
298         if dirs_for:
299             print 'Unexpected annotations for %s, parameters are %s' % (
300                 list(dirs_for), list(param_names), )
301
302         for child in base_type.child_list:
303             yield self._create_parameter(
304                 child, dirs.get(child.ident, {}))
305
306     def _create_member(self, symbol):
307         ctype = symbol.base_type.type
308         if (ctype == CTYPE_POINTER and
309             symbol.base_type.base_type.type == CTYPE_FUNCTION):
310             node = self._create_callback(symbol)
311         else:
312             ftype = self._create_type(symbol.base_type, {}, True)
313             # Fields are assumed to be read-write
314             # (except for Objects, see also glibtransformer.py)
315             node = Field(symbol.ident, ftype, symbol.ident,
316                        readable=True, writable=True, bits=symbol.const_int)
317         return node
318
319     def _create_typedef(self, symbol):
320         ctype = symbol.base_type.type
321         if (ctype == CTYPE_POINTER and
322             symbol.base_type.base_type.type == CTYPE_FUNCTION):
323             node = self._create_callback(symbol)
324         elif ctype == CTYPE_STRUCT:
325             node = self._create_typedef_struct(symbol)
326         elif ctype == CTYPE_UNION:
327             node = self._create_typedef_union(symbol)
328         elif ctype == CTYPE_ENUM:
329             return self._create_enum(symbol)
330         elif ctype in (CTYPE_TYPEDEF,
331                        CTYPE_POINTER,
332                        CTYPE_BASIC_TYPE,
333                        CTYPE_VOID):
334             name = self.remove_prefix(symbol.ident)
335             if symbol.base_type.name:
336                 target = self.remove_prefix(symbol.base_type.name)
337             else:
338                 target = 'none'
339             if name in type_names:
340                 return None
341             return Alias(name, target, ctype=symbol.ident)
342         else:
343             raise NotImplementedError(
344                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
345         return node
346
347     def _parse_ctype(self, ctype):
348         # First look up the ctype including any pointers;
349         # a few type names like 'char*' have their own aliases
350         # and we need pointer information for those.
351         firstpass = type_name_from_ctype(ctype)
352
353         # Remove all pointers - we require standard calling
354         # conventions.  For example, an 'int' is always passed by
355         # value (unless it's out or inout).
356         derefed = firstpass.replace('*', '')
357
358         # Canonicalize our type again, this time without the pointer;
359         # this ensures we turn e.g. plain "guint" => "int"
360         return type_name_from_ctype(derefed)
361
362     def _create_type(self, source_type, options, is_param):
363         ctype = self._create_source_type(source_type)
364         if ctype == 'va_list':
365             raise SkipError()
366         # FIXME: FILE* should not be skipped, it should be handled
367         #        properly instead
368         elif ctype == 'FILE*':
369             raise SkipError
370
371         # Now check for a list/map/array type
372         if ctype in self._list_ctypes:
373             param = options.get('element-type')
374             if param:
375                 contained_type = self._parse_ctype(param[0])
376             else:
377                 contained_type = None
378             derefed_name = self._parse_ctype(ctype)
379             rettype = List(derefed_name,
380                            ctype,
381                            contained_type)
382         elif ctype in self._map_ctypes:
383             param = options.get('element-type')
384             if param:
385                 key_type = self._parse_ctype(param[0])
386                 value_type = self._parse_ctype(param[1])
387             else:
388                 key_type = None
389                 value_type = None
390             derefed_name = self._parse_ctype(ctype)
391             rettype = Map(derefed_name,
392                           ctype,
393                           key_type, value_type)
394         elif (ctype in default_array_types) or ('array' in options):
395             derefed_name = ctype[:-1] # strip the *
396             rettype = Array(ctype,
397                             self._parse_ctype(derefed_name))
398             array_opts = options.get('array')
399             if array_opts:
400                 (_, len_name) = array_opts[0].split('=')
401                 rettype.length_param_name = len_name
402         else:
403             derefed_name = self._parse_ctype(ctype)
404             rettype = Type(derefed_name, ctype)
405
406         # Deduce direction for some types passed by reference that
407         # aren't arrays; modifies the options array.
408         if ('array' not in options and
409             not ('out' in options or
410                  'in' in options or
411                  'inout' in options or
412                  'in-out' in options) and
413             source_type.type == CTYPE_POINTER and
414             derefed_name in default_out_types):
415             options['out'] = []
416
417         if 'transfer' in options:
418             # Transfer is specified, we don't question it.
419             return rettype
420
421         canontype = type_name_from_ctype(ctype)
422
423         # Since no transfer is specified, we drop into a bunch of
424         # heuristics to guess it.  This mutates the options array to
425         # set the 'transfer' option.
426         # Note that we inferred the transfer
427         options['transfer-inferred'] = []
428         stype = source_type
429         if canontype == TYPE_STRING:
430             # It's a string - we just look at 'const'
431             if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
432                 options['transfer'] = ['none']
433             else:
434                 options['transfer'] = ['full']
435         elif 'array' in options or stype.type == CTYPE_ARRAY:
436             # It's rare to mutate arrays in public GObject APIs
437             options['transfer'] = ['none']
438         elif (canontype in BASIC_GIR_TYPES or
439               canontype == TYPE_NONE or
440               stype.type == CTYPE_ENUM):
441             # Basic types default to 'none'
442             options['transfer'] = ['none']
443         elif (stype.type == CTYPE_POINTER and
444               stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
445             # Anything with 'const' gets none
446             options['transfer'] = ['none']
447         elif is_param and stype.type == CTYPE_POINTER:
448             # For generic pointer types, let's look at the argument
449             # direction.  An out/inout argument gets full, everything
450             # else none.
451             if ('out' in options or
452                 'inout' in options or
453                 'in-out' in options):
454                 options['transfer'] = ['full']
455             else:
456                 options['transfer'] = ['none']
457         else:
458             # For anything else we default to none for parameters;
459             # this covers enums and possibly some other corner cases.
460             # Return values of structures and the like will end up
461             # full.
462             if is_param:
463                 options['transfer'] = ['none']
464             else:
465                 options['transfer'] = ['full']
466
467         return rettype
468
469     def _handle_generic_param_options(self, param, options):
470         for option, data in options.iteritems():
471             if option == 'transfer':
472                 if data:
473                     depth = data[0]
474                     if depth not in ('none', 'container', 'full'):
475                         raise ValueError("Invalid transfer %r" % (depth, ))
476                 else:
477                     depth = 'full'
478                 param.transfer = depth
479             elif option == 'transfer-inferred':
480                 # This is a purely internal flag; we don't expect
481                 # people to write it
482                 param.transfer_inferred = True
483
484     def _create_parameter(self, symbol, options):
485         options = self._parse_options(options)
486         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
487             ptype = Varargs()
488             if 'transfer' not in options:
489                 options['transfer'] = ['none']
490         else:
491             ptype = self._create_type(symbol.base_type, options, True)
492         param = Parameter(symbol.ident, ptype)
493         for option, data in options.iteritems():
494             if option in ['in-out', 'inout']:
495                 param.direction = 'inout'
496             elif option == 'in':
497                 param.direction = 'in'
498             elif option == 'out':
499                 param.direction = 'out'
500             elif option == 'allow-none':
501                 param.allow_none = True
502             elif option.startswith(('element-type', 'array')):
503                 pass
504             elif option in ('transfer', 'transfer-inferred'):
505                 pass
506             else:
507                 print 'Unhandled parameter annotation option: %r' % (
508                     option, )
509         self._handle_generic_param_options(param, options)
510
511         assert param.transfer is not None, param
512         return param
513
514     def _create_return(self, source_type, options=None):
515         if options is None:
516             options_map = {}
517         else:
518             options_map = self._parse_options(options)
519         rtype = self._create_type(source_type, options_map, False)
520         rtype = self.resolve_param_type(rtype)
521         return_ = Return(rtype)
522         self._handle_generic_param_options(return_, options_map)
523         for option, data in options_map.iteritems():
524             if option in ('transfer', 'transfer-inferred',
525                           'element-type', 'out'):
526                 pass
527             else:
528                 print 'Unhandled return type annotation option: %r' % (
529                     option, )
530
531         assert return_.transfer is not None, return_
532         return return_
533
534     def _create_const(self, symbol):
535         name = self.remove_prefix(symbol.ident)
536         if symbol.const_string is None:
537             type_name = 'int'
538             value = symbol.const_int
539         else:
540             type_name = 'utf8'
541             value = symbol.const_string
542         const = Constant(name, type_name, value)
543         return const
544
545     def _create_typedef_struct(self, symbol):
546         name = self.remove_prefix(symbol.ident)
547         struct = Struct(name, symbol.ident)
548         self._typedefs_ns[symbol.ident] = struct
549         self._create_struct(symbol)
550         return struct
551
552     def _create_typedef_union(self, symbol):
553         name = self.remove_prefix(symbol.ident)
554         union = Union(name, symbol.ident)
555         self._typedefs_ns[symbol.ident] = union
556         self._create_union(symbol)
557         return union
558
559     def _create_struct(self, symbol):
560         struct = self._typedefs_ns.get(symbol.ident, None)
561         if struct is None:
562             # This is a bit of a hack; really we should try
563             # to resolve through the typedefs to find the real
564             # name
565             if symbol.ident.startswith('_'):
566                 name = symbol.ident[1:]
567             else:
568                 name = symbol.ident
569             name = self.remove_prefix(name)
570             struct = Struct(name, symbol.ident)
571
572         for child in symbol.base_type.child_list:
573             field = self._traverse_one(child)
574             if field:
575                 struct.fields.append(field)
576
577         return struct
578
579     def _create_union(self, symbol):
580         union = self._typedefs_ns.get(symbol.ident, None)
581         if union is None:
582             # This is a bit of a hack; really we should try
583             # to resolve through the typedefs to find the real
584             # name
585             if symbol.ident.startswith('_'):
586                 name = symbol.ident[1:]
587             else:
588                 name = symbol.ident
589             name = self.remove_prefix(name)
590             union = Union(name, symbol.ident)
591
592         for child in symbol.base_type.child_list:
593             field = self._traverse_one(child)
594             if field:
595                 union.fields.append(field)
596
597         return union
598
599     def _create_callback(self, symbol):
600         directives = symbol.directives()
601         parameters = self._create_parameters(symbol.base_type.base_type,
602             directives)
603         retval = self._create_return(symbol.base_type.base_type.base_type,
604             directives.get('return', {}))
605         if symbol.ident.find('_') > 0:
606             name = self.remove_prefix(symbol.ident, True)
607         else:
608             name = self.remove_prefix(symbol.ident)
609         return Callback(name, retval, list(parameters), symbol.ident)
610
611     def _typepair_to_str(self, item):
612         nsname, item = item
613         if nsname is None:
614             return item.name
615         return '%s.%s' % (nsname, item.name)
616
617     def _resolve_type_name_1(self, type_name, ctype, names):
618         # First look using the built-in names
619         if ctype:
620             try:
621                 return type_names[ctype]
622             except KeyError, e:
623                 pass
624         try:
625             return type_names[type_name]
626         except KeyError, e:
627             pass
628         type_name = self.remove_prefix(type_name)
629         resolved = names.aliases.get(type_name)
630         if resolved:
631             return self._typepair_to_str(resolved)
632         resolved = names.names.get(type_name)
633         if resolved:
634             return self._typepair_to_str(resolved)
635         if ctype:
636             ctype = ctype.replace('*', '')
637             resolved = names.ctypes.get(ctype)
638             if resolved:
639                 return self._typepair_to_str(resolved)
640         resolved = names.type_names.get(type_name)
641         if resolved:
642             return self._typepair_to_str(resolved)
643         raise KeyError("failed to find %r" % (type_name, ))
644
645     def resolve_type_name_full(self, type_name, ctype,
646                                names, allow_invalid=True):
647         try:
648             return self._resolve_type_name_1(type_name, ctype, names)
649         except KeyError, e:
650             try:
651                 return self._resolve_type_name_1(type_name, ctype, self._names)
652             except KeyError, e:
653                 if not allow_invalid:
654                     raise
655                 return type_name
656
657     def resolve_type_name(self, type_name, ctype=None):
658         try:
659             return self.resolve_type_name_full(type_name, ctype, self._names)
660         except KeyError, e:
661             return type_name
662
663     def gtypename_to_giname(self, gtname, names):
664         resolved = names.type_names.get(gtname)
665         if resolved:
666             return self._typepair_to_str(resolved)
667         resolved = self._names.type_names.get(gtname)
668         if resolved:
669             return self._typepair_to_str(resolved)
670         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
671
672     def ctype_of(self, obj):
673         if hasattr(obj, 'ctype'):
674             return obj.ctype
675         elif hasattr(obj, 'symbol'):
676             return obj.symbol
677         else:
678             return None
679
680     def resolve_param_type_full(self, ptype, names, **kwargs):
681         if isinstance(ptype, Node):
682             ptype.name = self.resolve_type_name_full(ptype.name,
683                                                      self.ctype_of(ptype),
684                                                      names, **kwargs)
685             if isinstance(ptype, (Array, List)):
686                 if ptype.element_type is not None:
687                     ptype.element_type = \
688                         self.resolve_param_type_full(ptype.element_type,
689                                                      names, **kwargs)
690             if isinstance(ptype, Map):
691                 if ptype.key_type is not None:
692                     ptype.key_type = \
693                         self.resolve_param_type_full(ptype.key_type,
694                                                      names, **kwargs)
695                     ptype.value_type = \
696                         self.resolve_param_type_full(ptype.value_type,
697                                                      names, **kwargs)
698         elif isinstance(ptype, basestring):
699             return self.resolve_type_name_full(ptype, None, names, **kwargs)
700         else:
701             raise AssertionError("Unhandled param: %r" % (ptype, ))
702         return ptype
703
704     def resolve_param_type(self, ptype):
705         try:
706             return self.resolve_param_type_full(ptype, self._names)
707         except KeyError, e:
708             return ptype
709
710     def follow_aliases(self, type_name, names):
711         while True:
712             resolved = names.aliases.get(type_name)
713             if resolved:
714                 (ns, alias) = resolved
715                 type_name = alias.target
716             else:
717                 break
718         return type_name