root / logilab.pylintinstaller / logilab / astng / __init__.py

Revision 202:d67e86292521, 9.6 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"""Python Abstract Syntax Tree New Generation
14
15The aim of this module is to provide a common base representation of
16python source code for projects such as pychecker, pyreverse,
17pylint... Well, actually the development of this library is essentialy
18governed by pylint's needs.
19
20It extends class defined in the compiler.ast [1] module with some
21additional methods and attributes. Instance attributes are added by a
22builder object, which can either generate extended ast (let's call
23them astng ;) by visiting an existant ast tree or by inspecting living
24object. Methods are added by monkey patching ast classes.
25
26Main modules are:
27
28* nodes and scoped_nodes for more information about methods and
29  attributes added to different node classes
30
31* the manager contains a high level object to get astng trees from
32  source files and living objects. It maintains a cache of previously
33  constructed tree for quick access
34
35* builder contains the class responsible to build astng trees
36
37
38:author:    Sylvain Thenault
39:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
40:contact:   http://www.logilab.fr/ -- mailto:python-projects@logilab.org
41:copyright: 2003-2007 Sylvain Thenault
42:contact:   mailto:thenault@gmail.com
43"""
44from __future__ import generators
45
46__doctype__ = "restructuredtext en"
47
48from logilab.common.compat import chain, imap
49
50# WARNING: internal imports order matters !
51
52from logilab.astng._exceptions import *
53
54
55class InferenceContext(object):
56    __slots__ = ('startingfrom', 'path', 'lookupname', 'callcontext', 'boundnode')
57   
58    def __init__(self, node=None, path=None):
59        self.startingfrom = node # XXX useful ?
60        if path is None:
61            self.path = []
62        else:
63            self.path = path
64        self.lookupname = None
65        self.callcontext = None
66        self.boundnode = None
67
68    def push(self, node):
69        name = self.lookupname
70        if (node, name) in self.path:
71            raise StopIteration()
72        self.path.append( (node, name) )
73
74    def pop(self):
75        return self.path.pop()
76
77    def clone(self):
78        # XXX copy lookupname/callcontext ?
79        clone = InferenceContext(self.startingfrom, self.path)
80        clone.callcontext = self.callcontext
81        clone.boundnode = self.boundnode
82        return clone
83
84
85def unpack_infer(stmt, context=None):
86    """return an iterator on nodes infered by the given statement
87    if the infered value is a list or a tuple, recurse on it to
88    get values infered by its content
89    """
90    if isinstance(stmt, (List, Tuple)):
91        # XXX loosing context
92        return chain(*imap(unpack_infer, stmt.nodes))
93    infered = stmt.infer(context).next()
94    if infered is stmt:
95        return iter( (stmt,) )
96    return chain(*imap(unpack_infer, stmt.infer(context)))
97
98def copy_context(context):
99    if context is not None:
100        return context.clone()
101    else:
102        return InferenceContext()
103   
104def _infer_stmts(stmts, context, frame=None):
105    """return an iterator on statements infered by each statement in <stmts>
106    """
107    stmt = None
108    infered = False
109    if context is not None:
110        name = context.lookupname
111        context = context.clone()
112    else:
113        name = None
114        context = InferenceContext()
115    for stmt in stmts:
116        if stmt is YES:
117            yield stmt
118            infered = True
119            continue
120        context.lookupname = stmt._infer_name(frame, name)
121        try:
122            for infered in stmt.infer(context):
123                yield infered
124                infered = True
125        except UnresolvableName:
126            continue
127        except InferenceError:
128            yield YES
129            infered = True
130    if not infered:
131        raise InferenceError(str(stmt))
132
133# special inference objects ###################################################
134
135class Yes(object):
136    """a yes object"""
137    def __repr__(self):
138        return 'YES'
139    def __getattribute__(self, name):
140        return self
141    def __call__(self, *args, **kwargs):
142        return self
143YES = Yes()
144
145class Proxy:
146    """a simple proxy object"""
147    def __init__(self, proxied):
148        self._proxied = proxied
149
150    def __getattr__(self, name):
151        return getattr(self._proxied, name)
152
153    def infer(self, context=None):
154        yield self
155
156
157class InstanceMethod(Proxy):
158    """a special node representing a function bound to an instance"""
159    def __repr__(self):
160        instance = self._proxied.parent.frame()
161        return 'Bound method %s of %s.%s' % (self._proxied.name,
162                                             instance.root().name,
163                                             instance.name)
164    __str__ = __repr__
165
166    def is_bound(self):
167        return True
168
169
170class Instance(Proxy):
171    """a special node representing a class instance"""
172    def getattr(self, name, context=None, lookupclass=True):
173        try:
174            return self._proxied.instance_attr(name, context)
175        except NotFoundError:
176            if name == '__class__':
177                return [self._proxied]
178            if name == '__name__':
179                # access to __name__ gives undefined member on class
180                # instances but not on class objects
181                raise NotFoundError(name)
182            if lookupclass:
183                return self._proxied.getattr(name, context)
184        raise NotFoundError(name)
185
186    def igetattr(self, name, context=None):
187        """infered getattr"""
188        try:
189            # XXX frame should be self._proxied, or not ?
190            return _infer_stmts(
191                self._wrap_attr(self.getattr(name, context, lookupclass=False)),
192                                context, frame=self)
193        except NotFoundError:
194            try:
195                # fallback to class'igetattr since it has some logic to handle
196                # descriptors
197                return self._wrap_attr(self._proxied.igetattr(name, context))
198            except NotFoundError:
199                raise InferenceError(name)
200           
201    def _wrap_attr(self, attrs):
202        """wrap bound methods of attrs in a InstanceMethod proxies"""
203        # Guess which attrs are used in inference.
204        def wrap(attr):
205            if isinstance(attr, Function) and attr.type == 'method':
206                return InstanceMethod(attr)
207            else:
208                return attr
209        return imap(wrap, attrs)
210       
211    def infer_call_result(self, caller, context=None):
212        """infer what's a class instance is returning when called"""
213        infered = False
214        for node in self._proxied.igetattr('__call__', context):
215            for res in node.infer_call_result(caller, context):
216                infered = True
217                yield res
218        if not infered:
219            raise InferenceError()
220
221    def __repr__(self):
222        return 'Instance of %s.%s' % (self._proxied.root().name,
223                                      self._proxied.name)
224    __str__ = __repr__
225   
226    def callable(self):
227        try:
228            self._proxied.getattr('__call__')
229            return True
230        except NotFoundError:
231            return False
232
233    def pytype(self):
234        return self._proxied.qname()
235   
236class Generator(Proxy): 
237    """a special node representing a generator"""
238    def callable(self):
239        return True
240   
241    def pytype(self):
242        return '__builtin__.generator'
243
244# imports #####################################################################
245
246from logilab.astng.manager import ASTNGManager, Project, Package
247MANAGER = ASTNGManager()
248
249from logilab.astng.nodes import *
250from logilab.astng import nodes
251from logilab.astng.scoped_nodes import *
252from logilab.astng import inference
253from logilab.astng import lookup
254lookup._decorate(nodes)
255
256List._proxied = MANAGER.astng_from_class(list)
257List.__bases__ += (inference.Instance,)
258List.pytype = lambda x: '__builtin__.list'
259
260Tuple._proxied = MANAGER.astng_from_class(tuple)
261Tuple.__bases__ += (inference.Instance,)
262Tuple.pytype = lambda x: '__builtin__.tuple'
263
264Dict.__bases__ += (inference.Instance,)
265Dict._proxied = MANAGER.astng_from_class(dict)
266Dict.pytype = lambda x: '__builtin__.dict'
267
268builtin_astng = Dict._proxied.root()
269
270Const.__bases__ += (inference.Instance,)
271Const._proxied = None
272def Const___getattr__(self, name):
273    if self.value is None:
274        raise AttributeError(name)
275    if self._proxied is None:
276        self._proxied = MANAGER.astng_from_class(self.value.__class__)
277    return getattr(self._proxied, name)
278Const.__getattr__ = Const___getattr__
279def Const_getattr(self, name, context=None, lookupclass=None):
280    if self.value is None:
281        raise NotFoundError(name)
282    if self._proxied is None:
283        self._proxied = MANAGER.astng_from_class(self.value.__class__)
284    return self._proxied.getattr(name, context)
285Const.getattr = Const_getattr
286Const.has_dynamic_getattr = lambda x: False
287
288def Const_pytype(self):
289    if self.value is None:
290        return '__builtin__.NoneType'
291    if self._proxied is None:
292        self._proxied = MANAGER.astng_from_class(self.value.__class__)
293    return self._proxied.qname()
294Const.pytype = Const_pytype
Note: See TracBrowser for help on using the browser.