# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""this module contains a set of functions to create astng trees from scratch
(build_* functions) or from living object (object_build_* functions)
:author: Sylvain Thenault
:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
:copyright: 2003-2007 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
__docformat__ = "restructuredtext en"
import sys
from inspect import getargspec
from logilab.astng import nodes
def attach___dict__(node):
"""attach the __dict__ attribute to Class and Module objects"""
dictn = nodes.Dict([])
dictn.parent = node
node.locals['__dict__'] = [dictn]
_marker = object()
def attach_dummy_node(node, name, object=_marker):
"""create a dummy node and register it in the locals of the given
node with the specified name
"""
enode = nodes.EmptyNode()
enode.object = object
_attach_local_node(node, enode, name)
nodes.EmptyNode.has_underlying_object = lambda self: self.object is not _marker
def attach_const_node(node, name, value):
"""create a Const node and register it in the locals of the given
node with the specified name
"""
_attach_local_node(node, nodes.Const(value), name)
if sys.version_info < (2, 5):
def attach_import_node(node, modname, membername):
"""create a From node and register it in the locals of the given
node with the specified name
"""
_attach_local_node(node,
nodes.From(modname, ( (membername, None), ) ),
membername)
else:
def attach_import_node(node, modname, membername):
"""create a From node and register it in the locals of the given
node with the specified name
"""
_attach_local_node(node,
nodes.From(modname, ( (membername, None), ), 0),
membername)
def _attach_local_node(parent, node, name):
node.name = name # needed by add_local_node
node.parent = parent
node.lineno = 1
parent.add_local_node(node)
def build_module(name, doc=None):
"""create and initialize a astng Module node"""
node = nodes.Module(doc, nodes.Stmt([]))
node.node.parent = node
node.name = name
node.pure_python = False
node.package = False
node.parent = None
node.globals = node.locals = {}
return node
def build_class(name, basenames=None, doc=None):
"""create and initialize a astng Class node"""
klass = nodes.Class(name, [], doc, nodes.Stmt([]))
bases = [nodes.Name(base) for base in basenames]
for base in bases:
base.parent = klass
klass.basenames = basenames
klass.bases = bases
klass.code.parent = klass
klass.locals = {}
klass.instance_attrs = {}
for name, value in ( ('__name__', name),
#('__module__', node.root().name),
):
const = nodes.Const(value)
const.parent = klass
klass.locals[name] = [const]
return klass
# introduction of decorators has changed the Function initializer arguments
if sys.version_info >= (2, 4):
try:
from compiler.ast import Decorators as BaseDecorators
class Decorators(BaseDecorators):
def __init__(self):
BaseDecorators.__init__(self, [], 0)
except ImportError:
Decorators = list
def build_function(name, args=None, defaults=None, flag=0, doc=None):
"""create and initialize a astng Function node"""
args, defaults = args or [], defaults or []
# first argument is now a list of decorators
func = nodes.Function(Decorators(), name, args, defaults, flag, doc,
nodes.Stmt([]))
func.code.parent = func
func.locals = {}
if args:
register_arguments(func, args)
return func
else:
def build_function(name, args=None, defaults=None, flag=0, doc=None):
"""create and initialize a astng Function node"""
args, defaults = args or [], defaults or []
func = nodes.Function(name, args, defaults, flag, doc, nodes.Stmt([]))
func.code.parent = func
func.locals = {}
if args:
register_arguments(func, args)
return func
def build_name_assign(name, value):
"""create and initialize an astng Assign for a name assignment"""
return nodes.Assign([nodes.AssName(name, 'OP_ASSIGN')], nodes.Const(value))
def build_attr_assign(name, value, attr='self'):
"""create and initialize an astng Assign for an attribute assignment"""
return nodes.Assign([nodes.AssAttr(nodes.Name(attr), name, 'OP_ASSIGN')],
nodes.Const(value))
if sys.version_info < (2, 5):
def build_from_import(fromname, names):
"""create and intialize an astng From import statement"""
return nodes.From(fromname, [(name, None) for name in names])
else:
def build_from_import(fromname, names):
"""create and intialize an astng From import statement"""
return nodes.From(fromname, [(name, None) for name in names], 0)
def register_arguments(node, args):
"""add given arguments to local
args is a list that may contains nested lists
(i.e. def func(a, (b, c, d)): ...)
"""
for arg in args:
if type(arg) is type(''):
node.set_local(arg, node)
else:
register_arguments(node, arg)
def object_build_class(node, member):
"""create astng for a living class object"""
basenames = [base.__name__ for base in member.__bases__]
return _base_class_object_build(node, member, basenames)
def object_build_function(node, member):
"""create astng for a living function object"""
args, varargs, varkw, defaults = getargspec(member)
if varargs is not None:
args.append(varargs)
if varkw is not None:
args.append(varkw)
func = build_function(member.__name__, args, defaults,
member.func_code.co_flags, member.__doc__)
node.add_local_node(func)
def object_build_datadescriptor(node, member, name):
"""create astng for a living data descriptor object"""
return _base_class_object_build(node, member, [], name)
def object_build_methoddescriptor(node, member):
"""create astng for a living method descriptor object"""
# FIXME get arguments ?
func = build_function(member.__name__, doc=member.__doc__)
# set argnames to None to notice that we have no information, not
# and empty argument list
func.argnames = None
node.add_local_node(func)
def _base_class_object_build(node, member, basenames, name=None):
"""create astng for a living class object, with a given set of base names
(e.g. ancestors)
"""
klass = build_class(name or member.__name__, basenames, member.__doc__)
klass._newstyle = isinstance(member, type)
node.add_local_node(klass)
try:
# limit the instantiation trick since it's too dangerous
# (such as infinite test execution...)
# this at least resolves common case such as Exception.args,
# OSError.errno
if issubclass(member, Exception):
instdict = member().__dict__
else:
raise TypeError
except:
pass
else:
for name, obj in instdict.items():
valnode = nodes.EmptyNode()
valnode.object = obj
valnode.parent = klass
valnode.lineno = 1
klass.instance_attrs[name] = [valnode]
return klass
__all__ = ('register_arguments', 'build_module',
'object_build_class', 'object_build_function',
'object_build_datadescriptor', 'object_build_methoddescriptor',
'attach___dict__', 'attach_dummy_node',
'attach_const_node', 'attach_import_node')
syntax highlighted by Code2HTML, v. 0.9.1