root / logilab.pylintinstaller / logilab / astng / scoped_nodes.py

Revision 202:d67e86292521, 25.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"""This module extends ast "scoped" node, i.e. which are opening a new
14local scope in the language definition : Module, Class, Function (and
15Lambda in some extends).
16
17Each new methods and attributes added on each class are documented
18below.
19
20
21:author:    Sylvain Thenault
22:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
23:contact:   http://www.logilab.fr/ -- mailto:python-projects@logilab.org
24:copyright: 2003-2007 Sylvain Thenault
25:contact:   mailto:thenault@gmail.com
26"""
27from __future__ import generators
28
29__doctype__ = "restructuredtext en"
30
31import sys
32
33from logilab.common.compat import chain, set
34
35from logilab.astng.utils import extend_class
36from logilab.astng import YES, MANAGER, Instance, InferenceContext, copy_context, \
37     unpack_infer, _infer_stmts, \
38     Class, Const, Dict, Function, GenExpr, Lambda, \
39     Module, Name, Pass, Raise, Tuple, Yield
40from logilab.astng import NotFoundError, NoDefault, \
41     ASTNGBuildingException, InferenceError
42
43# module class dict/iterator interface ########################################
44   
45class LocalsDictMixIn(object):
46    """ this class provides locals handling common to Module, Function
47    and Class nodes, including a dict like interface for direct access
48    to locals information
49   
50    /!\ this class should not be used directly /!\ it's
51    only used as a methods and attribute container, and update the
52    original class from the compiler.ast module using its dictionnary
53    (see below the class definition)
54    """
55   
56    # attributes below are set by the builder module or by raw factories
57   
58    # dictionary of locals with name as key and node defining the local as
59    # value   
60    locals = None
61
62    def qname(self):
63        """return the 'qualified' name of the node, eg module.name,
64        module.class.name ...
65        """
66        if self.parent is None:
67            return self.name
68        return '%s.%s' % (self.parent.frame().qname(), self.name)
69       
70    def frame(self):
71        """return the first parent frame node (i.e. Module, Function or Class)
72        """
73        return self
74   
75    def scope(self):
76        """return the first node defining a new scope (i.e. Module,
77        Function, Class, Lambda but also GenExpr)
78        """
79        return self
80   
81    def set_local(self, name, stmt):
82        """define <name> in locals (<stmt> is the node defining the name)
83        if the node is a Module node (i.e. has globals), add the name to
84        globals
85
86        if the name is already defined, ignore it
87        """
88        self.locals.setdefault(name, []).append(stmt)
89       
90    __setitem__ = set_local
91   
92    def add_local_node(self, child_node, name=None):
93        """append a child which should alter locals to the given node"""
94        if name != '__class__':
95            # add __class__ node as a child will cause infinite recursion later!
96            self._append_node(child_node)
97        self.set_local(name or child_node.name, child_node)
98
99    def _append_node(self, child_node):
100        """append a child, linking it in the tree"""
101        self.code.nodes.append(child_node)
102        child_node.parent = self
103   
104    def __getitem__(self, item):
105        """method from the `dict` interface returning the first node
106        associated with the given name in the locals dictionnary
107
108        :type item: str
109        :param item: the name of the locally defined object
110        :raises KeyError: if the name is not defined
111        """
112        return self.locals[item][0]
113   
114    def __iter__(self):
115        """method from the `dict` interface returning an iterator on
116        `self.keys()`
117        """
118        return iter(self.keys())
119   
120    def keys(self):
121        """method from the `dict` interface returning a tuple containing
122        locally defined names
123        """
124        return self.locals.keys()
125##         associated to nodes which are instance of `Function` or
126##         `Class`
127##         """
128##         # FIXME: sort keys according to line number ?
129##         try:
130##             return self.__keys
131##         except AttributeError:
132##             keys = [member.name for member in self.locals.values()
133##                     if (isinstance(member, Function)
134##                         or isinstance(member, Class))
135##                         and member.parent.frame() is self]
136##             self.__keys = tuple(keys)
137##             return keys
138
139    def values(self):
140        """method from the `dict` interface returning a tuple containing
141        locally defined nodes which are instance of `Function` or `Class`
142        """
143        return [self[key] for key in self.keys()]
144   
145    def items(self):
146        """method from the `dict` interface returning a list of tuple
147        containing each locally defined name with its associated node,
148        which is an instance of `Function` or `Class`
149        """
150        return zip(self.keys(), self.values())
151
152    def has_key(self, name):
153        """method from the `dict` interface returning True if the given
154        name is defined in the locals dictionary
155        """
156        return self.locals.has_key(name)
157   
158    __contains__ = has_key
159   
160extend_class(Module, LocalsDictMixIn)
161extend_class(Class, LocalsDictMixIn)
162extend_class(Function, LocalsDictMixIn)
163extend_class(Lambda, LocalsDictMixIn)
164# GenExpr has it's own locals but isn't a frame
165extend_class(GenExpr, LocalsDictMixIn)
166def frame(self):
167    return self.parent.frame()
168GenExpr.frame = frame
169
170
171class GetattrMixIn(object):
172    def getattr(self, name, context=None):
173        try:
174            return self.locals[name]
175        except KeyError:
176            raise NotFoundError(name)
177       
178    def igetattr(self, name, context=None):
179        """infered getattr"""
180        # set lookup name since this is necessary to infer on import nodes for
181        # instance
182        context = copy_context(context)
183        context.lookupname = name
184        try:
185            return _infer_stmts(self.getattr(name, context), context, frame=self)
186        except NotFoundError:
187            raise InferenceError(name)
188extend_class(Module, GetattrMixIn)
189extend_class(Class, GetattrMixIn)
190
191# Module  #####################################################################
192
193class ModuleNG(object):
194    """/!\ this class should not be used directly /!\ it's
195    only used as a methods and attribute container, and update the
196    original class from the compiler.ast module using its dictionnary
197    (see below the class definition)
198    """
199       
200    # attributes below are set by the builder module or by raw factories
201
202    # the file from which as been extracted the astng representation. It may
203    # be None if the representation has been built from a built-in module
204    file = None
205    # the module name
206    name = None
207    # boolean for astng built from source (i.e. ast)
208    pure_python = None
209    # boolean for package module
210    package = None
211    # dictionary of globals with name as key and node defining the global
212    # as value
213    globals = None
214
215    def pytype(self):
216        return '__builtin__.module'
217   
218    def getattr(self, name, context=None):
219        try:
220            return self.locals[name]
221        except KeyError:
222            if self.package:
223                try:
224                    return [self.import_module(name, relative_only=True)]
225                except KeyboardInterrupt:
226                    raise
227                except:
228                    pass
229            raise NotFoundError(name)
230       
231    def _append_node(self, child_node):
232        """append a child version specific to Module node"""
233        self.node.nodes.append(child_node)
234        child_node.parent = self
235       
236    def source_line(self):
237        """return the source line number, 0 on a module"""
238        return 0
239
240    def fully_defined(self):
241        """return True if this module has been built from a .py file
242        and so contains a complete representation including the code
243        """
244        return self.file is not None and self.file.endswith('.py')
245   
246    def statement(self):
247        """return the first parent node marked as statement node
248        consider a module as a statement...
249        """
250        return self
251
252    def import_module(self, modname, relative_only=False):
253        """import the given module considering self as context"""
254        try:
255            return MANAGER.astng_from_module_name(self.relative_name(modname))
256        except ASTNGBuildingException:
257            if relative_only:
258                raise
259        module = MANAGER.astng_from_module_name(modname)
260        return module
261
262    def relative_name(self, modname):
263        if self.package:
264            return '%s.%s' % (self.name, modname)
265        package_name = '.'.join(self.name.split('.')[:-1])
266        if package_name:
267            return '%s.%s' % (package_name, modname)
268        return modname
269       
270    def wildcard_import_names(self):
271        """return the list of imported names when this module is 'wildard
272        imported'
273
274        It doesn't include the '__builtins__' name which is added by the
275        current CPython implementation of wildcard imports.
276        """
277        # take advantage of a living module if it exists
278        try:
279            living = sys.modules[self.name]
280        except KeyError:
281            pass
282        else:
283            try:
284                return living.__all__
285            except AttributeError:
286                return [name for name in living.__dict__.keys()
287                        if not name.startswith('_')]
288        # else lookup the astng
289        try:
290            explicit = self['__all__'].assigned_stmts().next()
291            # should be a tuple of constant string
292            return [const.value for const in explicit.nodes]
293        except (KeyError, AttributeError, InferenceError):
294            # XXX should admit we have lost if there is something like
295            # __all__ that we've not been able to analyse (such as
296            # dynamically constructed __all__)
297            return [name for name in self.keys()
298                    if not name.startswith('_')]
299
300extend_class(Module, ModuleNG)
301
302# Function  ###################################################################
303
304class FunctionNG(object):
305    """/!\ this class should not be used directly /!\ it's
306    only used as a methods and attribute container, and update the
307    original class from the compiler.ast module using its dictionnary
308    (see below the class definition)
309    """
310
311    # attributes below are set by the builder module or by raw factories
312
313    # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod'
314    type = 'function'
315    # list of argument names. MAY BE NONE on some builtin functions where
316    # arguments are unknown
317    argnames = None
318
319    def pytype(self):
320        if 'method' in self.type:
321            return '__builtin__.instancemethod'
322        return '__builtin__.function'
323
324    def is_method(self):
325        """return true if the function node should be considered as a method"""
326        return self.type != 'function'
327   
328    def is_bound(self):
329        """return true if the function is bound to an Instance or a class"""
330        return self.type == 'classmethod'
331
332    def is_abstract(self, pass_is_abstract=True):
333        """return true if the method is abstract
334        It's considered as abstract if the only statement is a raise of
335        NotImplementError, or, if pass_is_abstract, a pass statement
336        """
337        for child_node in self.code.getChildNodes():
338            if isinstance(child_node, Raise) and child_node.expr1:
339                try:
340                    name = child_node.expr1.nodes_of_class(Name).next()
341                    if name.name == 'NotImplementedError':
342                        return True
343                except StopIteration:
344                    pass
345            if pass_is_abstract and isinstance(child_node, Pass):
346                return True
347            return False
348        # empty function is the same as function with a single "pass" statement
349        if pass_is_abstract:
350            return True
351
352    def is_generator(self):
353        """return true if this is a generator function"""
354        try:
355            return self.nodes_of_class(Yield, skip_klass=Function).next()
356        except StopIteration:
357            return False
358       
359    def format_args(self):
360        """return arguments formatted as string"""
361        if self.argnames is None: # information is missing
362            return ''
363        result = []
364        args, kwargs, last, default_idx = self._pos_information()
365        for i in range(len(self.argnames)):
366            name = self.argnames[i]
367            if type(name) is type(()):
368                name = '(%s)' % ','.join(name)
369            if i == last and kwargs:
370                name = '**%s' % name
371            elif args and i == last or (kwargs and i == last - 1):
372                name = '*%s' % name
373            elif i >= default_idx:
374                default_str = self.defaults[i - default_idx].as_string()
375                name = '%s=%s' % (name, default_str)
376            result.append(name)
377        return ', '.join(result)
378
379    def default_value(self, argname):
380        """return the default value for an argument
381
382        :raise `NoDefault`: if there is no default value defined
383        """
384        if self.argnames is None: # information is missing
385            raise NoDefault()
386        args, kwargs, last, defaultidx = self._pos_information()
387        try:
388            i = self.argnames.index(argname)
389        except ValueError:
390            raise NoDefault() # XXX
391        if i >= defaultidx and (i - defaultidx) < len(self.defaults):
392            return self.defaults[i - defaultidx]
393        raise NoDefault()
394
395    def mularg_class(self, argname):
396        """if the given argument is a * or ** argument, return respectivly
397        a Tuple or Dict instance, else return None
398        """
399        args, kwargs, last, defaultidx = self._pos_information()
400        try:
401            i = self.argnames.index(argname)
402        except ValueError:
403            return None # XXX
404        if i == last and kwargs:
405            valnode = Dict([])
406            valnode.parent = self
407            return valnode
408        if args and (i == last or (kwargs and i == last - 1)):
409            valnode = Tuple([])
410            valnode.parent = self
411            return valnode
412        return None
413
414    def _pos_information(self):
415        """return a 4-uple with positional information about arguments:
416        (true if * is used,
417         true if ** is used,
418         index of the last argument,
419         index of the first argument having a default value)
420        """
421        args = self.flags & 4
422        kwargs = self.flags & 8
423        last = len(self.argnames) - 1
424        defaultidx = len(self.argnames) - (len(self.defaults) +
425                                           (args and 1 or 0) +
426                                           (kwargs and 1 or 0))
427        return args, kwargs, last, defaultidx
428
429extend_class(Function, FunctionNG)
430
431# lambda nodes may also need some of the function members
432Lambda._pos_information = FunctionNG._pos_information.im_func
433Lambda.format_args = FunctionNG.format_args.im_func
434Lambda.default_value = FunctionNG.default_value.im_func
435Lambda.mularg_class = FunctionNG.mularg_class.im_func
436Lambda.type = 'function'
437Lambda.pytype = FunctionNG.pytype.im_func
438
439# Class ######################################################################
440
441def _class_type(klass):
442    """return a Class node type to differ metaclass, interface and exception
443    from 'regular' classes
444    """
445    if klass._type is not None:
446        return klass._type
447    if klass.name == 'type':
448        klass._type = 'metaclass'
449    elif klass.name.endswith('Interface'):
450        klass._type = 'interface'
451    elif klass.name.endswith('Exception'):
452        klass._type = 'exception'
453    else:
454        for base in klass.ancestors(recurs=False):
455            if base.type != 'class':
456                klass._type = base.type
457                break
458    if klass._type is None:
459        klass._type = 'class'
460    return klass._type
461
462def _iface_hdlr(iface_node):
463    """a handler function used by interfaces to handle suspicious
464    interface nodes
465    """
466    return True
467
468class ClassNG(object):
469    """/!\ this class should not be used directly /!\ it's
470    only used as a methods and attribute container, and update the
471    original class from the compiler.ast module using its dictionnary
472    (see below the class definition)
473    """
474   
475    _type = None
476    type = property(_class_type,
477                    doc="class'type, possible values are 'class