Conform last patch with pep8
[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 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.
9 #
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.
14 #
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.
19 #
20
21 import os
22 import sys
23
24 from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
25                   Parameter, Return, Struct, Field,
26                   Type, Array, Alias, Interface, Class, Node, Union,
27                   Varargs, Constant, type_name_from_ctype,
28                   type_names, TYPE_STRING, BASIC_GIR_TYPES)
29 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
30 from .glibast import GLibBoxed
31 from .girparser import GIRParser
32 from .odict import odict
33 from .sourcescanner import (
34     SourceSymbol, ctype_name, CTYPE_POINTER,
35     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
36     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
37     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
38     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
39     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
40     TYPE_QUALIFIER_CONST)
41 from .utils import 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, cachestore, namespace_name, namespace_version):
68         self._cachestore = cachestore
69         self.generator = None
70         self._namespace = Namespace(namespace_name, namespace_version)
71         self._names = Names()
72         self._pkg_config_packages = set()
73         self._typedefs_ns = {}
74         self._strip_prefix = ''
75         self._includes = set()
76         self._includepaths = []
77
78     def get_names(self):
79         return self._names
80
81     def get_includes(self):
82         return self._includes
83
84     def set_strip_prefix(self, strip_prefix):
85         self._strip_prefix = strip_prefix
86
87     def get_strip_prefix(self):
88         return self._strip_prefix
89
90     def get_pkgconfig_packages(self):
91         return self._pkg_config_packages
92
93     def set_source_ast(self, src_ast):
94         self.generator = src_ast
95
96     def parse(self):
97         nodes = []
98         for symbol in self.generator.get_symbols():
99             try:
100                 node = self._traverse_one(symbol)
101             except SkipError:
102                 continue
103             self._add_node(node)
104         return self._namespace
105
106     def set_include_paths(self, paths):
107         self._includepaths = list(paths)
108
109     def register_include(self, include):
110         if include in self._includes:
111             return
112         filename = self._find_include(include)
113         self._parse_include(filename)
114         self._includes.add(include)
115
116     # Private
117
118     def _find_include(self, include):
119         searchdirs = self._includepaths[:]
120         for path in _xdg_data_dirs:
121             searchdirs.append(os.path.join(path, GIR_SUFFIX))
122         searchdirs.append(GIR_DIR)
123
124         girname = '%s-%s.gir' % (include.name, include.version)
125         for d in searchdirs:
126             path = os.path.join(d, girname)
127             if os.path.exists(path):
128                 return path
129         sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
130                          % (girname, searchdirs))
131         sys.exit(1)
132
133     def _parse_include(self, filename):
134         parser = self._cachestore.load(filename)
135         if parser is None:
136             parser = GIRParser()
137             parser.set_include_parsing(True)
138             parser.parse(filename)
139             self._cachestore.store(filename, parser)
140
141         for include in parser.get_includes():
142             self.register_include(include)
143
144         for pkg in parser.get_pkgconfig_packages():
145             self._pkg_config_packages.add(pkg)
146         namespace = parser.get_namespace()
147         nsname = namespace.name
148         for node in namespace.nodes:
149             if isinstance(node, Alias):
150                 self._names.aliases[node.name] = (nsname, node)
151             elif isinstance(node, (GLibBoxed, Interface, Class)):
152                 self._names.type_names[node.type_name] = (nsname, node)
153             giname = '%s.%s' % (nsname, node.name)
154             self._names.names[giname] = (nsname, node)
155             if hasattr(node, 'ctype'):
156                 self._names.ctypes[node.ctype] = (nsname, node)
157             elif hasattr(node, 'symbol'):
158                 self._names.ctypes[node.symbol] = (nsname, node)
159
160     def _add_node(self, node):
161         if node is None:
162             return
163         if node.name.startswith('_'):
164             return
165         self._namespace.nodes.append(node)
166         self._names.names[node.name] = (None, node)
167
168     def _strip_namespace_func(self, name):
169         prefix = self._namespace.name.lower() + '_'
170         if name.lower().startswith(prefix):
171             name = name[len(prefix):]
172         else:
173             prefix = to_underscores(self._namespace.name).lower() + '_'
174             if name.lower().startswith(prefix):
175                 name = name[len(prefix):]
176         return self.remove_prefix(name, isfunction=True)
177
178     def remove_prefix(self, name, isfunction=False):
179         # when --strip-prefix=g:
180         #   GHashTable -> HashTable
181         #   g_hash_table_new -> hash_table_new
182         prefix = self._strip_prefix.lower()
183         if isfunction:
184             prefix += '_'
185         if len(name) > len(prefix) and name.lower().startswith(prefix):
186             name = name[len(prefix):]
187
188         while name.startswith('_'):
189             name = name[1:]
190         return name
191
192     def _traverse_one(self, symbol, stype=None):
193         assert isinstance(symbol, SourceSymbol), symbol
194
195         if stype is None:
196             stype = symbol.type
197         if stype == CSYMBOL_TYPE_FUNCTION:
198             return self._create_function(symbol)
199         elif stype == CSYMBOL_TYPE_TYPEDEF:
200             return self._create_typedef(symbol)
201         elif stype == CSYMBOL_TYPE_STRUCT:
202             return self._create_struct(symbol)
203         elif stype == CSYMBOL_TYPE_ENUM:
204             return self._create_enum(symbol)
205         elif stype == CSYMBOL_TYPE_OBJECT:
206             return self._create_object(symbol)
207         elif stype == CSYMBOL_TYPE_MEMBER:
208             return self._create_member(symbol)
209         elif stype == CSYMBOL_TYPE_UNION:
210             return self._create_union(symbol)
211         elif stype == CSYMBOL_TYPE_CONST:
212             return self._create_const(symbol)
213         else:
214             raise NotImplementedError(
215                 'Transformer: unhandled symbol: %r' % (symbol, ))
216
217     def _enum_common_prefix(self, symbol):
218         def common_prefix(a, b):
219             commonparts = []
220             for aword, bword in zip(a.split('_'), b.split('_')):
221                 if aword != bword:
222                     return '_'.join(commonparts) + '_'
223                 commonparts.append(aword)
224             return min(a, b)
225
226         # Nothing less than 2 has a common prefix
227         if len(list(symbol.base_type.child_list)) < 2:
228             return None
229         prefix = None
230         for child in symbol.base_type.child_list:
231             if prefix is None:
232                 prefix = child.ident
233             else:
234                 prefix = common_prefix(prefix, child.ident)
235                 if prefix == '':
236                     return None
237         return prefix
238
239     def _create_enum(self, symbol):
240         prefix = self._enum_common_prefix(symbol)
241         if prefix:
242             prefixlen = len(prefix)
243         else:
244             prefixlen = 0
245         members = []
246         for child in symbol.base_type.child_list:
247             if prefixlen > 0:
248                 name = child.ident[prefixlen:]
249             else:
250                 # Ok, the enum members don't have a consistent prefix
251                 # among them, so let's just remove the global namespace
252                 # prefix.
253                 name = self.remove_prefix(child.ident)
254             members.append(Member(name.lower(),
255                                   child.const_int,
256                                   child.ident))
257
258         enum_name = self.remove_prefix(symbol.ident)
259         if symbol.base_type.is_bitfield:
260             klass = Bitfield
261         else:
262             klass = Enum
263         node = klass(enum_name, symbol.ident, members)
264         self._names.type_names[symbol.ident] = (None, node)
265         return node
266
267     def _create_object(self, symbol):
268         return Member(symbol.ident, symbol.base_type.name,
269                       symbol.ident)
270
271     def _type_is_callback(self, type):
272         if isinstance(type, Callback):
273             return True
274         node = self._names.names.get(type.name)
275         if node and isinstance(node[1], Callback):
276             return True
277         return False
278
279     def _handle_closure(self, param, closure_idx, closure_param):
280         if (closure_param.type.name == 'any' and
281             closure_param.name.endswith('data')):
282             param.closure_name = closure_param.name
283             param.closure_index = closure_idx
284             return True
285         return False
286
287     def _handle_destroy(self, param, destroy_idx, destroy_param):
288         if (destroy_param.type.name == 'GLib.DestroyNotify' or
289             destroy_param.type.ctype == 'GDestroyNotify'):
290             param.destroy_name = destroy_param.name
291             param.destroy_index = destroy_idx
292             return True
293         return False
294
295     def _augment_callback_params(self, params):
296         for i, param in enumerate(params):
297             if not self._type_is_callback(param.type):
298                 continue
299
300             # set a default scope
301             if param.scope is None:
302                 param.scope = 'call'
303
304             # j is the index where we look for closure/destroy to
305             # group with the callback param
306             j = i + 1
307             if j == len(params):
308                 continue # no more args -> nothing to group
309             # look at the param directly following for either a
310             # closure or a destroy; only one of these will fire
311             had_closure = self._handle_closure(param, j, params[j])
312             had_destroy = self._handle_destroy(param, j, params[j])
313             j += 1
314             # are we out of params, or did we find neither?
315             if j == len(params) or (not had_closure and not had_destroy):
316                 continue
317             # we found either a closure or a destroy; check the
318             # parameter following for the other
319             if not had_closure:
320                 self._handle_closure(param, j, params[j])
321             if not had_destroy:
322                 self._handle_destroy(param, j, params[j])
323
324     def _create_function(self, symbol):
325         parameters = list(self._create_parameters(symbol.base_type))
326         return_ = self._create_return(symbol.base_type.base_type)
327         self._augment_callback_params(parameters)
328         name = self._strip_namespace_func(symbol.ident)
329         func = Function(name, return_, parameters, symbol.ident)
330         return func
331
332     def _create_source_type(self, source_type):
333         if source_type is None:
334             return 'None'
335         if source_type.type == CTYPE_VOID:
336             value = 'void'
337         elif source_type.type == CTYPE_BASIC_TYPE:
338             value = source_type.name
339         elif source_type.type == CTYPE_TYPEDEF:
340             value = source_type.name
341         elif source_type.type == CTYPE_ARRAY:
342             return self._create_source_type(source_type.base_type)
343         elif source_type.type == CTYPE_POINTER:
344             value = self._create_source_type(source_type.base_type) + '*'
345         else:
346             value = 'any'
347         return value
348
349     def _create_parameters(self, base_type):
350
351         # warn if we see annotations for unknown parameters
352         param_names = set(child.ident for child in base_type.child_list)
353         for child in base_type.child_list:
354             yield self._create_parameter(child)
355
356     def _create_member(self, symbol):
357         source_type = symbol.base_type
358         if (source_type.type == CTYPE_POINTER and
359             symbol.base_type.base_type.type == CTYPE_FUNCTION):
360             node = self._create_callback(symbol)
361         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
362             node = self._create_struct(symbol, anonymous=True)
363         elif source_type.type == CTYPE_UNION and source_type.name is None:
364             node = self._create_union(symbol, anonymous=True)
365         else:
366             # Special handling for fields; we don't have annotations on them
367             # to apply later, yet.
368             if source_type.type == CTYPE_ARRAY:
369                 ctype = self._create_source_type(source_type)
370                 canonical_ctype = self._canonicalize_ctype(ctype)
371                 if canonical_ctype[-1] == '*':
372                     derefed_name = canonical_ctype[:-1]
373                 else:
374                     derefed_name = canonical_ctype
375                 derefed_name = self.resolve_param_type(derefed_name)
376                 ftype = Array(ctype, self.parse_ctype(derefed_name))
377                 child_list = list(symbol.base_type.child_list)
378                 ftype.zeroterminated = False
379                 if child_list:
380                     ftype.size = '%d' % (child_list[0].const_int, )
381             else:
382                 ftype = self._create_type(symbol.base_type,
383                                           is_param=False, is_retval=False)
384             ftype = self.resolve_param_type(ftype)
385             # Fields are assumed to be read-write
386             # (except for Objects, see also glibtransformer.py)
387             node = Field(symbol.ident, ftype, ftype.name,
388                          readable=True, writable=True, bits=symbol.const_int)
389         return node
390
391     def _create_typedef(self, symbol):
392         ctype = symbol.base_type.type
393         if (ctype == CTYPE_POINTER and
394             symbol.base_type.base_type.type == CTYPE_FUNCTION):
395             node = self._create_typedef_callback(symbol)
396         elif (ctype == CTYPE_POINTER and
397             symbol.base_type.base_type.type == CTYPE_STRUCT):
398             node = self._create_typedef_struct(symbol, disguised=True)
399         elif ctype == CTYPE_STRUCT:
400             node = self._create_typedef_struct(symbol)
401         elif ctype == CTYPE_UNION:
402             node = self._create_typedef_union(symbol)
403         elif ctype == CTYPE_ENUM:
404             return self._create_enum(symbol)
405         elif ctype in (CTYPE_TYPEDEF,
406                        CTYPE_POINTER,
407                        CTYPE_BASIC_TYPE,
408                        CTYPE_VOID):
409             name = self.remove_prefix(symbol.ident)
410             if symbol.base_type.name:
411                 target = self.remove_prefix(symbol.base_type.name)
412             else:
413                 target = 'none'
414             if name in type_names:
415                 return None
416             return Alias(name, target, ctype=symbol.ident)
417         else:
418             raise NotImplementedError(
419                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
420         return node
421
422     def _canonicalize_ctype(self, ctype):
423         # First look up the ctype including any pointers;
424         # a few type names like 'char*' have their own aliases
425         # and we need pointer information for those.
426         firstpass = type_name_from_ctype(ctype)
427
428         # If we have a particular alias for this, skip deep
429         # canonicalization to prevent changing
430         # e.g. char* -> int8*
431         if firstpass != ctype:
432             return firstpass
433
434         # We're also done if the type is already a fundamental
435         # known type, or there are no pointers.
436         if ctype in type_names or not firstpass.endswith('*'):
437             return firstpass
438
439         # We have a pointer type.
440         # Strip the end pointer, canonicalize our base type
441         base = firstpass[:-1]
442         canonical_base = self._canonicalize_ctype(base)
443
444         # Append the pointer again
445         canonical = canonical_base + '*'
446
447         return canonical
448
449     def parse_ctype(self, ctype, is_member=False):
450         canonical = self._canonicalize_ctype(ctype)
451
452         # Remove all pointers - we require standard calling
453         # conventions.  For example, an 'int' is always passed by
454         # value (unless it's out or inout).
455         derefed_typename = canonical.replace('*', '')
456
457         # Preserve "pointerness" of struct/union members
458         if (is_member and canonical.endswith('*') and
459             derefed_typename in BASIC_GIR_TYPES):
460             return 'any'
461         else:
462             return derefed_typename
463
464     def _create_type(self, source_type, is_param, is_retval):
465         ctype = self._create_source_type(source_type)
466         if ctype.startswith('va_list'):
467             raise SkipError()
468         # FIXME: FILE* should not be skipped, it should be handled
469         #        properly instead
470         elif ctype == 'FILE*':
471             raise SkipError
472
473         is_member = not (is_param or is_retval)
474         # Here we handle basic type parsing; most of the heavy lifting
475         # and inference comes in annotationparser.py when we merge
476         # in annotation data.
477         derefed_name = self.parse_ctype(ctype, is_member)
478         rettype = Type(derefed_name, ctype)
479         rettype.canonical = self._canonicalize_ctype(ctype)
480         derefed_ctype = ctype.replace('*', '')
481         rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
482
483         canontype = type_name_from_ctype(ctype)
484         # Is it a const char * or a const gpointer?
485         if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
486             (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
487             rettype.is_const = True
488         return rettype
489
490     def _create_parameter(self, symbol):
491         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
492             ptype = Varargs()
493         else:
494             ptype = self._create_type(symbol.base_type,
495                                       is_param=True, is_retval=False)
496             ptype = self.resolve_param_type(ptype)
497         return Parameter(symbol.ident, ptype)
498
499     def _create_return(self, source_type):
500         rtype = self._create_type(source_type,
501                                   is_param=False, is_retval=True)
502         rtype = self.resolve_param_type(rtype)
503         return_ = Return(rtype)
504         return return_
505
506     def _create_const(self, symbol):
507         # Don't create constants for non-public things
508         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
509         if (symbol.source_filename is None or
510             not symbol.source_filename.endswith('.h')):
511             return None
512         name = self.remove_prefix(symbol.ident)
513         if symbol.const_string is not None:
514             type_name = 'utf8'
515             value = symbol.const_string
516         elif symbol.const_int is not None:
517             type_name = 'int'
518             value = symbol.const_int
519         elif symbol.const_double is not None:
520             type_name = 'double'
521             value = symbol.const_double
522         else:
523             raise AssertionError()
524
525         const = Constant(name, type_name, value)
526         return const
527
528     def _create_typedef_struct(self, symbol, disguised=False):
529         name = self.remove_prefix(symbol.ident)
530         struct = Struct(name, symbol.ident, disguised)
531         self._typedefs_ns[symbol.ident] = struct
532         self._create_struct(symbol)
533         return struct
534
535     def _create_typedef_union(self, symbol):
536         name = self.remove_prefix(symbol.ident)
537         union = Union(name, symbol.ident)
538         self._typedefs_ns[symbol.ident] = union
539         self._create_union(symbol)
540         return union
541
542     def _create_typedef_callback(self, symbol):
543         callback = self._create_callback(symbol)
544         self._typedefs_ns[callback.name] = callback
545         return callback
546
547     def _create_compound(self, klass, symbol, anonymous):
548         if symbol.ident is None:
549             # the compound is an anonymous member of another union or a struct
550             assert anonymous
551             compound = klass(None, None)
552         else:
553             compound = self._typedefs_ns.get(symbol.ident, None)
554
555         if compound is None:
556             # This is a bit of a hack; really we should try
557             # to resolve through the typedefs to find the real
558             # name
559             if symbol.ident.startswith('_'):
560                 name = symbol.ident[1:]
561                 compound = self._typedefs_ns.get(name, None)
562             else:
563                 name = symbol.ident
564             if compound is None:
565                 name = self.remove_prefix(name)
566                 compound = klass(name, symbol.ident)
567
568         for child in symbol.base_type.child_list:
569             field = self._traverse_one(child)
570             if field:
571                 compound.fields.append(field)
572
573         return compound
574
575     def _create_struct(self, symbol, anonymous=False):
576         return self._create_compound(Struct, symbol, anonymous)
577
578     def _create_union(self, symbol, anonymous=False):
579         return self._create_compound(Union, symbol, anonymous)
580
581     def _create_callback(self, symbol):
582         parameters = list(self._create_parameters(symbol.base_type.base_type))
583         retval = self._create_return(symbol.base_type.base_type.base_type)
584
585         # Mark the 'user_data' arguments
586         for i, param in enumerate(parameters):
587             if (param.type.name == 'any' and
588                 param.name == 'user_data'):
589                 param.closure_index = i
590
591         if symbol.ident.find('_') > 0:
592             name = self.remove_prefix(symbol.ident, True)
593         else:
594             name = self.remove_prefix(symbol.ident)
595         callback = Callback(name, retval, parameters, symbol.ident)
596
597         return callback
598
599     def _typepair_to_str(self, item):
600         nsname, item = item
601         if nsname is None:
602             return item.name
603         return '%s.%s' % (nsname, item.name)
604
605     def _resolve_type_name_1(self, type_name, ctype, names):
606         # First look using the built-in names
607         if ctype:
608             try:
609                 return type_names[ctype]
610             except KeyError, e:
611                 pass
612         try:
613             return type_names[type_name]
614         except KeyError, e:
615             pass
616
617         if ctype:
618             ctype = ctype.replace('*', '')
619             resolved = names.ctypes.get(ctype)
620             if resolved:
621                 return self._typepair_to_str(resolved)
622         type_name = self.remove_prefix(type_name)
623         resolved = names.aliases.get(type_name)
624         if resolved:
625             return self._typepair_to_str(resolved)
626         resolved = names.names.get(type_name)
627         if resolved:
628             return self._typepair_to_str(resolved)
629         resolved = names.type_names.get(type_name)
630         if resolved:
631             return self._typepair_to_str(resolved)
632         raise KeyError("failed to find %r" % (type_name, ))
633
634     def resolve_type_name_full(self, type_name, ctype,
635                                names, allow_invalid=True):
636         try:
637             return self._resolve_type_name_1(type_name, ctype, names)
638         except KeyError, e:
639             try:
640                 return self._resolve_type_name_1(type_name, ctype, self._names)
641             except KeyError, e:
642                 if not allow_invalid:
643                     raise
644                 return type_name
645
646     def resolve_type_name(self, type_name, ctype=None):
647         try:
648             return self.resolve_type_name_full(type_name, ctype, self._names)
649         except KeyError, e:
650             return type_name
651
652     def gtypename_to_giname(self, gtname, names):
653         resolved = names.type_names.get(gtname)
654         if resolved:
655             return self._typepair_to_str(resolved)
656         resolved = self._names.type_names.get(gtname)
657         if resolved:
658             return self._typepair_to_str(resolved)
659         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
660
661     def ctype_of(self, obj):
662         if hasattr(obj, 'ctype'):
663             return obj.ctype
664         elif hasattr(obj, 'symbol'):
665             return obj.symbol
666         else:
667             return None
668
669     def resolve_param_type_full(self, ptype, names, **kwargs):
670         if isinstance(ptype, Node):
671             ptype.name = self.resolve_type_name_full(ptype.name,
672                                                      self.ctype_of(ptype),
673                                                      names, **kwargs)
674         elif isinstance(ptype, basestring):
675             return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
676         else:
677             raise AssertionError("Unhandled param: %r" % (ptype, ))
678         return ptype
679
680     def resolve_param_type(self, ptype):
681         try:
682             return self.resolve_param_type_full(ptype, self._names)
683         except KeyError, e:
684             return ptype
685
686     def follow_aliases(self, type_name, names):
687         while True:
688             resolved = names.aliases.get(type_name)
689             if resolved:
690                 (ns, alias) = resolved
691                 type_name = alias.target
692             else:
693                 break
694         return type_name