PageRenderTime 73ms CodeModel.GetById 45ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 1ms

/packages/logilab-astng/raw_building.py

https://github.com/mozilla/doozer-lib
Python | 212 lines | 110 code | 27 blank | 75 comment | 34 complexity | 10bf5742c5f0af49a1b7610fb6f6b7e1 MD5 | raw file
  1# This program is free software; you can redistribute it and/or modify it under
  2# the terms of the GNU Lesser 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 Lesser General Public License for more details.
  9#
 10# You should have received a copy of the GNU Lesser 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# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 14# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 15# copyright 2003-2010 Sylvain Thenault, all rights reserved.
 16# contact mailto:thenault@gmail.com
 17#
 18# This file is part of logilab-astng.
 19#
 20# logilab-astng is free software: you can redistribute it and/or modify it
 21# under the terms of the GNU Lesser General Public License as published by the
 22# Free Software Foundation, either version 2.1 of the License, or (at your
 23# option) any later version.
 24#
 25# logilab-astng is distributed in the hope that it will be useful, but
 26# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 27# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 28# for more details.
 29#
 30# You should have received a copy of the GNU Lesser General Public License along
 31# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
 32"""this module contains a set of functions to create astng trees from scratch
 33(build_* functions) or from living object (object_build_* functions)
 34
 35"""
 36
 37__docformat__ = "restructuredtext en"
 38
 39import sys
 40from inspect import getargspec
 41
 42from logilab.astng import nodes
 43
 44
 45def _attach_local_node(parent, node, name):
 46    node.name = name # needed by add_local_node
 47    parent.add_local_node(node)
 48
 49_marker = object()
 50
 51def attach_dummy_node(node, name, object=_marker):
 52    """create a dummy node and register it in the locals of the given
 53    node with the specified name
 54    """
 55    enode = nodes.EmptyNode()
 56    enode.object = object
 57    _attach_local_node(node, enode, name)
 58
 59nodes.EmptyNode.has_underlying_object = lambda self: self.object is not _marker
 60
 61def attach_const_node(node, name, value):
 62    """create a Const node and register it in the locals of the given
 63    node with the specified name
 64    """
 65    if not name in node.special_attributes:
 66        _attach_local_node(node, nodes.const_factory(value), name)
 67
 68def attach_import_node(node, modname, membername):
 69    """create a From node and register it in the locals of the given
 70    node with the specified name
 71    """
 72    from_node = nodes.From(modname, [(membername, None)])
 73    _attach_local_node(node, from_node, membername)
 74
 75
 76def build_module(name, doc=None):
 77    """create and initialize a astng Module node"""
 78    node = nodes.Module(name, doc, pure_python=False)
 79    node.package = False
 80    node.parent = None
 81    return node
 82
 83def build_class(name, basenames=(), doc=None):
 84    """create and initialize a astng Class node"""
 85    node = nodes.Class(name, doc)
 86    for base in basenames:
 87        basenode = nodes.Name()
 88        basenode.name = base
 89        node.bases.append(basenode)
 90        basenode.parent = node
 91    return node
 92
 93def build_function(name, args=None, defaults=None, flag=0, doc=None):
 94    """create and initialize a astng Function node"""
 95    args, defaults = args or [], defaults or []
 96    # first argument is now a list of decorators
 97    func = nodes.Function(name, doc)
 98    func.args = argsnode = nodes.Arguments()
 99    argsnode.args = []
100    for arg in args:
101        argsnode.args.append(nodes.Name())
102        argsnode.args[-1].name = arg
103        argsnode.args[-1].parent = argsnode
104    argsnode.defaults = []
105    for default in defaults:
106        argsnode.defaults.append(nodes.const_factory(default))
107        argsnode.defaults[-1].parent = argsnode
108    argsnode.kwarg = None
109    argsnode.vararg = None
110    argsnode.parent = func
111    if args:
112        register_arguments(func)
113    return func
114
115
116# def build_name_assign(name, value):
117#     """create and initialize an astng Assign for a name assignment"""
118#     return nodes.Assign([nodes.AssName(name, 'OP_ASSIGN')], nodes.Const(value))
119
120# def build_attr_assign(name, value, attr='self'):
121#     """create and initialize an astng Assign for an attribute assignment"""
122#     return nodes.Assign([nodes.AssAttr(nodes.Name(attr), name, 'OP_ASSIGN')],
123#                         nodes.Const(value))
124
125
126def build_from_import(fromname, names):
127    """create and initialize an astng From import statement"""
128    return nodes.From(fromname, [(name, None) for name in names])
129
130def register_arguments(func, args=None):
131    """add given arguments to local
132
133    args is a list that may contains nested lists
134    (i.e. def func(a, (b, c, d)): ...)
135    """
136    if args is None:
137        args = func.args.args
138        if func.args.vararg:
139            func.set_local(func.args.vararg, func.args)
140        if func.args.kwarg:
141            func.set_local(func.args.kwarg, func.args)
142    for arg in args:
143        if isinstance(arg, nodes.Name):
144            func.set_local(arg.name, arg)
145        else:
146            register_arguments(func, arg.elts)
147
148def object_build_class(node, member, localname):
149    """create astng for a living class object"""
150    basenames = [base.__name__ for base in member.__bases__]
151    return _base_class_object_build(node, member, basenames,
152                                    localname=localname)
153
154def object_build_function(node, member, localname):
155    """create astng for a living function object"""
156    args, varargs, varkw, defaults = getargspec(member)
157    if varargs is not None:
158        args.append(varargs)
159    if varkw is not None:
160        args.append(varkw)
161    func = build_function(getattr(member, '__name__', None) or localname, args,
162                          defaults, member.func_code.co_flags, member.__doc__)
163    node.add_local_node(func, localname)
164
165def object_build_datadescriptor(node, member, name):
166    """create astng for a living data descriptor object"""
167    return _base_class_object_build(node, member, [], name)
168
169def object_build_methoddescriptor(node, member, localname):
170    """create astng for a living method descriptor object"""
171    # FIXME get arguments ?
172    func = build_function(getattr(member, '__name__', None) or localname,
173                          doc=member.__doc__)
174    # set node's arguments to None to notice that we have no information, not
175    # and empty argument list
176    func.args.args = None
177    node.add_local_node(func, localname)
178
179def _base_class_object_build(node, member, basenames, name=None, localname=None):
180    """create astng for a living class object, with a given set of base names
181    (e.g. ancestors)
182    """
183    klass = build_class(name or getattr(member, '__name__', None) or localname,
184                        basenames, member.__doc__)
185    klass._newstyle = isinstance(member, type)
186    node.add_local_node(klass, localname)
187    try:
188        # limit the instantiation trick since it's too dangerous
189        # (such as infinite test execution...)
190        # this at least resolves common case such as Exception.args,
191        # OSError.errno
192        if issubclass(member, Exception):
193            instdict = member().__dict__
194        else:
195            raise TypeError
196    except:
197        pass
198    else:
199        for name, obj in instdict.items():
200            valnode = nodes.EmptyNode()
201            valnode.object = obj
202            valnode.parent = klass
203            valnode.lineno = 1
204            klass.instance_attrs[name] = [valnode]
205    return klass
206
207
208__all__ = ('register_arguments',  'build_module',
209           'object_build_class', 'object_build_function',
210           'object_build_datadescriptor', 'object_build_methoddescriptor',
211           'attach_dummy_node',
212           'attach_const_node', 'attach_import_node')