Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / astroid / raw_building.py @ 745

History | View | Annotate | Download (14.5 KB)

1
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3
#
4
# This file is part of astroid.
5
#
6
# astroid is free software: you can redistribute it and/or modify it
7
# under the terms of the GNU Lesser General Public License as published by the
8
# Free Software Foundation, either version 2.1 of the License, or (at your
9
# option) any later version.
10
#
11
# astroid is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
14
# for more details.
15
#
16
# You should have received a copy of the GNU Lesser General Public License along
17
# with astroid. If not, see <http://www.gnu.org/licenses/>.
18
"""this module contains a set of functions to create astroid trees from scratch
19
(build_* functions) or from living object (object_build_* functions)
20
"""
21

    
22
import sys
23
import os
24
from os.path import abspath
25
from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
26
                     ismethoddescriptor, isclass, isbuiltin, ismodule)
27
import six
28

    
29
from astroid.node_classes import CONST_CLS
30
from astroid.nodes import (Module, Class, Const, const_factory, From,
31
                           Function, EmptyNode, Name, Arguments)
32
from astroid.bases import BUILTINS, Generator
33
from astroid.manager import AstroidManager
34
MANAGER = AstroidManager()
35

    
36
_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
37
_JYTHON = os.name == 'java'
38
_BUILTINS = vars(six.moves.builtins)
39

    
40
def _io_discrepancy(member):
41
    # _io module names itself `io`: http://bugs.python.org/issue18602
42
    member_self = getattr(member, '__self__', None)
43
    return (member_self and
44
            ismodule(member_self) and
45
            member_self.__name__ == '_io' and
46
            member.__module__ == 'io')
47

    
48
def _attach_local_node(parent, node, name):
49
    node.name = name # needed by add_local_node
50
    parent.add_local_node(node)
51

    
52

    
53
def _add_dunder_class(func, member):
54
    """Add a __class__ member to the given func node, if we can determine it."""
55
    python_cls = member.__class__
56
    cls_name = getattr(python_cls, '__name__', None)
57
    if not cls_name:
58
        return
59
    bases = [ancestor.__name__ for ancestor in python_cls.__bases__]
60
    ast_klass = build_class(cls_name, bases, python_cls.__doc__)
61
    func._instance_attrs['__class__'] = [ast_klass]
62

    
63

    
64
_marker = object()
65

    
66
def attach_dummy_node(node, name, object=_marker):
67
    """create a dummy node and register it in the locals of the given
68
    node with the specified name
69
    """
70
    enode = EmptyNode()
71
    enode.object = object
72
    _attach_local_node(node, enode, name)
73

    
74
def _has_underlying_object(self):
75
    return hasattr(self, 'object') and self.object is not _marker
76

    
77
EmptyNode.has_underlying_object = _has_underlying_object
78

    
79
def attach_const_node(node, name, value):
80
    """create a Const node and register it in the locals of the given
81
    node with the specified name
82
    """
83
    if not name in node.special_attributes:
84
        _attach_local_node(node, const_factory(value), name)
85

    
86
def attach_import_node(node, modname, membername):
87
    """create a From node and register it in the locals of the given
88
    node with the specified name
89
    """
90
    from_node = From(modname, [(membername, None)])
91
    _attach_local_node(node, from_node, membername)
92

    
93

    
94
def build_module(name, doc=None):
95
    """create and initialize a astroid Module node"""
96
    node = Module(name, doc, pure_python=False)
97
    node.package = False
98
    node.parent = None
99
    return node
100

    
101
def build_class(name, basenames=(), doc=None):
102
    """create and initialize a astroid Class node"""
103
    node = Class(name, doc)
104
    for base in basenames:
105
        basenode = Name()
106
        basenode.name = base
107
        node.bases.append(basenode)
108
        basenode.parent = node
109
    return node
110

    
111
def build_function(name, args=None, defaults=None, flag=0, doc=None):
112
    """create and initialize a astroid Function node"""
113
    args, defaults = args or [], defaults or []
114
    # first argument is now a list of decorators
115
    func = Function(name, doc)
116
    func.args = argsnode = Arguments()
117
    argsnode.args = []
118
    for arg in args:
119
        argsnode.args.append(Name())
120
        argsnode.args[-1].name = arg
121
        argsnode.args[-1].parent = argsnode
122
    argsnode.defaults = []
123
    for default in defaults:
124
        argsnode.defaults.append(const_factory(default))
125
        argsnode.defaults[-1].parent = argsnode
126
    argsnode.kwarg = None
127
    argsnode.vararg = None
128
    argsnode.parent = func
129
    if args:
130
        register_arguments(func)
131
    return func
132

    
133

    
134
def build_from_import(fromname, names):
135
    """create and initialize an astroid From import statement"""
136
    return From(fromname, [(name, None) for name in names])
137

    
138
def register_arguments(func, args=None):
139
    """add given arguments to local
140

141
    args is a list that may contains nested lists
142
    (i.e. def func(a, (b, c, d)): ...)
143
    """
144
    if args is None:
145
        args = func.args.args
146
        if func.args.vararg:
147
            func.set_local(func.args.vararg, func.args)
148
        if func.args.kwarg:
149
            func.set_local(func.args.kwarg, func.args)
150
    for arg in args:
151
        if isinstance(arg, Name):
152
            func.set_local(arg.name, arg)
153
        else:
154
            register_arguments(func, arg.elts)
155

    
156
def object_build_class(node, member, localname):
157
    """create astroid for a living class object"""
158
    basenames = [base.__name__ for base in member.__bases__]
159
    return _base_class_object_build(node, member, basenames,
160
                                    localname=localname)
161

    
162
def object_build_function(node, member, localname):
163
    """create astroid for a living function object"""
164
    args, varargs, varkw, defaults = getargspec(member)
165
    if varargs is not None:
166
        args.append(varargs)
167
    if varkw is not None:
168
        args.append(varkw)
169
    func = build_function(getattr(member, '__name__', None) or localname, args,
170
                          defaults, six.get_function_code(member).co_flags, member.__doc__)
171
    node.add_local_node(func, localname)
172

    
173
def object_build_datadescriptor(node, member, name):
174
    """create astroid for a living data descriptor object"""
175
    return _base_class_object_build(node, member, [], name)
176

    
177
def object_build_methoddescriptor(node, member, localname):
178
    """create astroid for a living method descriptor object"""
179
    # FIXME get arguments ?
180
    func = build_function(getattr(member, '__name__', None) or localname,
181
                          doc=member.__doc__)
182
    # set node's arguments to None to notice that we have no information, not
183
    # and empty argument list
184
    func.args.args = None
185
    node.add_local_node(func, localname)
186
    _add_dunder_class(func, member)
187

    
188
def _base_class_object_build(node, member, basenames, name=None, localname=None):
189
    """create astroid for a living class object, with a given set of base names
190
    (e.g. ancestors)
191
    """
192
    klass = build_class(name or getattr(member, '__name__', None) or localname,
193
                        basenames, member.__doc__)
194
    klass._newstyle = isinstance(member, type)
195
    node.add_local_node(klass, localname)
196
    try:
197
        # limit the instantiation trick since it's too dangerous
198
        # (such as infinite test execution...)
199
        # this at least resolves common case such as Exception.args,
200
        # OSError.errno
201
        if issubclass(member, Exception):
202
            instdict = member().__dict__
203
        else:
204
            raise TypeError
205
    except:
206
        pass
207
    else:
208
        for name, obj in instdict.items():
209
            valnode = EmptyNode()
210
            valnode.object = obj
211
            valnode.parent = klass
212
            valnode.lineno = 1
213
            klass._instance_attrs[name] = [valnode]
214
    return klass
215

    
216

    
217

    
218

    
219
class InspectBuilder(object):
220
    """class for building nodes from living object
221

222
    this is actually a really minimal representation, including only Module,
223
    Function and Class nodes and some others as guessed.
224
    """
225

    
226
    # astroid from living objects ###############################################
227

    
228
    def __init__(self):
229
        self._done = {}
230
        self._module = None
231

    
232
    def inspect_build(self, module, modname=None, path=None):
233
        """build astroid from a living module (i.e. using inspect)
234
        this is used when there is no python source code available (either
235
        because it's a built-in module or because the .py is not available)
236
        """
237
        self._module = module
238
        if modname is None:
239
            modname = module.__name__
240
        try:
241
            node = build_module(modname, module.__doc__)
242
        except AttributeError:
243
            # in jython, java modules have no __doc__ (see #109562)
244
            node = build_module(modname)
245
        node.source_file = path and abspath(path) or path
246
        node.name = modname
247
        MANAGER.cache_module(node)
248
        node.package = hasattr(module, '__path__')
249
        self._done = {}
250
        self.object_build(node, module)
251
        return node
252

    
253
    def object_build(self, node, obj):
254
        """recursive method which create a partial ast from real objects
255
         (only function, class, and method are handled)
256
        """
257
        if obj in self._done:
258
            return self._done[obj]
259
        self._done[obj] = node
260
        for name in dir(obj):
261
            try:
262
                member = getattr(obj, name)
263
            except AttributeError:
264
                # damned ExtensionClass.Base, I know you're there !
265
                attach_dummy_node(node, name)
266
                continue
267
            if ismethod(member):
268
                member = six.get_method_function(member)
269
            if isfunction(member):
270
                # verify this is not an imported function
271
                filename = getattr(six.get_function_code(member),
272
                                   'co_filename', None)
273
                if filename is None:
274
                    assert isinstance(member, object)
275
                    object_build_methoddescriptor(node, member, name)
276
                elif filename != getattr(self._module, '__file__', None):
277
                    attach_dummy_node(node, name, member)
278
                else:
279
                    object_build_function(node, member, name)
280
            elif isbuiltin(member):
281
                if (not _io_discrepancy(member) and
282
                        self.imported_member(node, member, name)):
283
                    continue
284
                object_build_methoddescriptor(node, member, name)
285
            elif isclass(member):
286
                if self.imported_member(node, member, name):
287
                    continue
288
                if member in self._done:
289
                    class_node = self._done[member]
290
                    if not class_node in node._locals.get(name, ()):
291
                        node.add_local_node(class_node, name)
292
                else:
293
                    class_node = object_build_class(node, member, name)
294
                    # recursion
295
                    self.object_build(class_node, member)
296
                if name == '__class__' and class_node.parent is None:
297
                    class_node.parent = self._done[self._module]
298
            elif ismethoddescriptor(member):
299
                assert isinstance(member, object)
300
                object_build_methoddescriptor(node, member, name)
301
            elif isdatadescriptor(member):
302
                assert isinstance(member, object)
303
                object_build_datadescriptor(node, member, name)
304
            elif type(member) in _CONSTANTS:
305
                attach_const_node(node, name, member)
306
            else:
307
                # create an empty node so that the name is actually defined
308
                attach_dummy_node(node, name, member)
309

    
310
    def imported_member(self, node, member, name):
311
        """verify this is not an imported class or handle it"""
312
        # /!\ some classes like ExtensionClass doesn't have a __module__
313
        # attribute ! Also, this may trigger an exception on badly built module
314
        # (see http://www.logilab.org/ticket/57299 for instance)
315
        try:
316
            modname = getattr(member, '__module__', None)
317
        except:
318
            # XXX use logging
319
            print('unexpected error while building astroid from living object')
320
            import traceback
321
            traceback.print_exc()
322
            modname = None
323
        if modname is None:
324
            if (name in ('__new__', '__subclasshook__')
325
                    or (name in _BUILTINS and _JYTHON)):
326
                # Python 2.5.1 (r251:54863, Sep  1 2010, 22:03:14)
327
                # >>> print object.__new__.__module__
328
                # None
329
                modname = BUILTINS
330
            else:
331
                attach_dummy_node(node, name, member)
332
                return True
333

    
334
        real_name = {
335
            'gtk': 'gtk_gtk',
336
            '_io': 'io',
337
        }.get(modname, modname)
338

    
339
        if real_name != self._module.__name__:
340
            # check if it sounds valid and then add an import node, else use a
341
            # dummy node
342
            try:
343
                getattr(sys.modules[modname], name)
344
            except (KeyError, AttributeError):
345
                attach_dummy_node(node, name, member)
346
            else:
347
                attach_import_node(node, modname, name)
348
            return True
349
        return False
350

    
351

    
352
### astroid bootstrapping ######################################################
353
Astroid_BUILDER = InspectBuilder()
354

    
355
_CONST_PROXY = {}
356
def _astroid_bootstrapping(astroid_builtin=None):
357
    """astroid boot strapping the builtins module"""
358
    # this boot strapping is necessary since we need the Const nodes to
359
    # inspect_build builtins, and then we can proxy Const
360
    if astroid_builtin is None:
361
        from six.moves import builtins
362
        astroid_builtin = Astroid_BUILDER.inspect_build(builtins)
363

    
364
    for cls, node_cls in CONST_CLS.items():
365
        if cls is type(None):
366
            proxy = build_class('NoneType')
367
            proxy.parent = astroid_builtin
368
        elif cls is type(NotImplemented):
369
            proxy = build_class('NotImplementedType')
370
            proxy.parent = astroid_builtin
371
        else:
372
            proxy = astroid_builtin.getattr(cls.__name__)[0]
373
        if cls in (dict, list, set, tuple):
374
            node_cls._proxied = proxy
375
        else:
376
            _CONST_PROXY[cls] = proxy
377

    
378
_astroid_bootstrapping()
379

    
380
# TODO : find a nicer way to handle this situation;
381
# However __proxied introduced an
382
# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
383
def _set_proxied(const):
384
    return _CONST_PROXY[const.value.__class__]
385
Const._proxied = property(_set_proxied)
386

    
387
from types import GeneratorType
388
Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__)
389
Astroid_BUILDER.object_build(Generator._proxied, GeneratorType)
390