root / logilab.pylintinstaller / logilab / astng / inspector.py

Revision 202:d67e86292521, 8.8 kB (checked in by tziade@…, 9 months ago)

added logilab.pylintinstaller

Line 
1# This program is free software; you can redistribute it and/or modify it under
2# the terms of the GNU General Public License as published by the Free Software
3# Foundation; either version 2 of the License, or (at your option) any later
4# version.
5#
6# This program is distributed in the hope that it will be useful, but WITHOUT
7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc.,
12# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
13"""visitor doing some postprocessing on the astng tree.
14Try to resolve definitions (namespace) dictionnary, relationship...
15
16This module has been imported from pyreverse
17
18
19:version:   $Revision: 1.6 $ 
20:author:    Sylvain Thenault
21:copyright: 2003-2005 LOGILAB S.A. (Paris, FRANCE)
22:contact:   http://www.logilab.fr/ -- mailto:python-projects@logilab.org
23:copyright: 2003-2005 Sylvain Thenault
24:contact:   mailto:thenault@gmail.com
25"""
26
27__docformat__ = "restructuredtext en"
28
29from os.path import dirname
30
31from logilab.common.modutils import get_module_part, is_relative, \
32     is_standard_module
33
34from logilab import astng
35from logilab.astng.utils import LocalsVisitor
36
37class IdGeneratorMixIn:
38    """
39    Mixin adding the ability to generate integer uid
40    """
41    def __init__(self, start_value=0):
42        self.id_count = start_value
43   
44    def init_counter(self, start_value=0):
45        """init the id counter
46        """
47        self.id_count = start_value
48       
49    def generate_id(self):
50        """generate a new identifer
51        """
52        self.id_count += 1
53        return self.id_count
54
55
56class Linker(IdGeneratorMixIn, LocalsVisitor):
57    """
58    walk on the project tree and resolve relationships.
59   
60    According to options the following attributes may be added to visited nodes:
61   
62    * uid,
63      a unique identifier for the node (on astng.Project, astng.Module,
64      astng.Class and astng.locals_type). Only if the linker has been instantiad
65      with tag=True parameter (False by default).
66           
67    * Function
68      a mapping from locals'names to their bounded value, which may be a
69      constant like a string or an integer, or an astng node (on astng.Module,
70      astng.Class and astng.Function).
71
72    * instance_attrs_type
73      as locals_type but for klass member attributes (only on astng.Class)
74     
75    * implements,
76      list of implemented interfaces _objects_ (only on astng.Class nodes)
77    """
78   
79    def __init__(self, project, inherited_interfaces=0, tag=False):
80        IdGeneratorMixIn.__init__(self)
81        LocalsVisitor.__init__(self)
82        # take inherited interface in consideration or not
83        self.inherited_interfaces = inherited_interfaces
84        # tag nodes or not
85        self.tag = tag
86        # visited project
87        self.project = project
88
89       
90    def visit_project(self, node):
91        """visit an astng.Project node
92       
93         * optionaly tag the node wth a unique id
94        """
95        if self.tag:
96            node.uid = self.generate_id()
97        for module in node.modules:
98            self.visit(module)
99           
100    def visit_package(self, node):
101        """visit an astng.Package node
102       
103         * optionaly tag the node wth a unique id
104        """
105        if self.tag:
106            node.uid = self.generate_id()
107        for subelmt in node.values():
108            self.visit(subelmt)
109           
110    def visit_module(self, node):
111        """visit an astng.Module node
112       
113         * set the locals_type mapping
114         * set the depends mapping
115         * optionaly tag the node wth a unique id
116        """
117        if hasattr(node, 'locals_type'):
118            return
119        node.locals_type = {}
120        node.depends = []
121        if self.tag:
122            node.uid = self.generate_id()
123   
124    def visit_class(self, node):
125        """visit an astng.Class node
126       
127         * set the locals_type and instance_attrs_type mappings
128         * set the implements list and build it
129         * optionaly tag the node wth a unique id
130        """
131        if hasattr(node, 'locals_type'):
132            return
133        node.locals_type = {}
134        if self.tag:
135            node.uid = self.generate_id()
136        # resolve ancestors
137        for baseobj in node.ancestors(recurs=False):
138            specializations = getattr(baseobj, 'specializations', [])
139            specializations.append(node)
140            baseobj.specializations = specializations
141        # resolve instance attributes
142        node.instance_attrs_type = {}
143        for assattrs in node.instance_attrs.values():
144            for assattr in assattrs:
145                self.visit_assattr(assattr, node)
146        # resolve implemented interface
147        try:
148            node.implements = list(node.interfaces(self.inherited_interfaces))
149        except TypeError:
150            node.implements = ()
151           
152    def visit_function(self, node):
153        """visit an astng.Function node
154       
155         * set the locals_type mapping
156         * optionaly tag the node wth a unique id
157        """
158        if hasattr(node, 'locals_type'):
159            return
160        node.locals_type = {}
161        if self.tag:
162            node.uid = self.generate_id()
163           
164    link_project = visit_project
165    link_module = visit_module
166    link_class = visit_class
167    link_function = visit_function
168       
169    def visit_assname(self, node):
170        """visit an astng.AssName node
171
172        handle locals_type
173        """
174        frame = node.frame()
175        try:
176            values = list(node.infer())
177            try:
178                already_infered = frame.locals_type[node.name]
179                for valnode in values:
180                    if not valnode in already_infered:
181                        already_infered.append(valnode)
182            except KeyError:
183                frame.locals_type[node.name] = values
184        except astng.InferenceError:
185            pass
186       
187    def visit_assattr(self, node, parent):
188        """visit an astng.AssAttr node
189
190        handle instance_attrs_type
191        """
192        try:
193            values = list(node.infer())
194            try:
195                already_infered = parent.instance_attrs_type[node.attrname]
196                for valnode in values:
197                    if not valnode in already_infered:
198                        already_infered.append(valnode)
199            except KeyError:
200                parent.instance_attrs_type[node.attrname] = values
201        except astng.InferenceError:
202            pass
203           
204    def visit_import(self, node):
205        """visit an astng.Import node
206       
207        resolve module dependencies
208        """
209        context_file = node.root().file
210        for name in node.names:
211            relative = is_relative(name[0], context_file)
212            self._imported_module(node, name[0], relative)
213       
214
215    def visit_from(self, node):
216        """visit an astng.From node
217       
218        resolve module dependencies
219        """
220        basename = node.modname
221        context_file = node.root().file
222        if context_file is not None:
223            relative = is_relative(basename, context_file)
224        else:
225            relative = False
226        for name in node.names:
227            if name[0] == '*':
228                continue
229            # analyze dependancies
230            fullname = '%s.%s' % (basename, name[0])
231            if fullname.find('.') > -1:
232                try:
233                    # XXX: don't use get_module_part, missing package precedence
234                    fullname = get_module_part(fullname)
235                except ImportError:
236                    continue
237            if fullname != basename:
238                self._imported_module(node, fullname, relative)
239
240       
241    def compute_module(self, context_name, mod_path):
242        """return true if the module should be added to dependencies"""
243        package_dir = dirname(self.project.path)
244        if context_name == mod_path:
245            return 0
246        elif is_standard_module(mod_path, (package_dir,)):
247            return 1
248        return 0
249   
250    # protected methods ########################################################
251
252    def _imported_module(self, node, mod_path, relative):
253        """notify an imported module, used to analyze dependancies
254        """
255        module = node.root()
256        context_name = module.name
257        if relative:
258            mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]),
259                                  mod_path)
260        if self.compute_module(context_name, mod_path):
261            # handle dependancies
262            if not hasattr(module, 'depends'):
263                module.depends = []
264            mod_paths = module.depends
265            if not mod_path in mod_paths:
266                mod_paths.append(mod_path)
Note: See TracBrowser for help on using the browser.