summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCeridwen <ceridwenv@gmail.com>2015-08-21 12:30:06 -0400
committerCeridwen <ceridwenv@gmail.com>2015-08-21 12:30:06 -0400
commit7fe6eff8b7bec34bf04226fae8e68bad11900e42 (patch)
tree4e68578c458541df9b30f2b7a610c1ca721d6f28
parent57551b823a3f1b68085d02f6f6321dbe84e1d074 (diff)
downloadastroid-git-7fe6eff8b7bec34bf04226fae8e68bad11900e42.tar.gz
Add tests for aliases
-rw-r--r--astroid/as_string.py10
-rw-r--r--astroid/bases.py15
-rw-r--r--astroid/brain/py2gi.py2
-rw-r--r--astroid/brain/py2stdlib.py6
-rw-r--r--astroid/helpers.py2
-rw-r--r--astroid/inference.py6
-rw-r--r--astroid/node_classes.py8
-rw-r--r--astroid/rebuilder.py47
-rw-r--r--astroid/scoped_nodes.py18
-rw-r--r--astroid/tests/unittest_inference.py3
-rw-r--r--astroid/tests/unittest_nodes.py108
-rw-r--r--astroid/tests/unittest_scoped_nodes.py2
-rw-r--r--astroid/tests/unittest_transforms.py1
13 files changed, 178 insertions, 50 deletions
diff --git a/astroid/as_string.py b/astroid/as_string.py
index cc0051cf..19ab1364 100644
--- a/astroid/as_string.py
+++ b/astroid/as_string.py
@@ -116,7 +116,7 @@ class AsStringVisitor(object):
return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
def visit_repr(self, node):
- """return an astroid.Backquote node as string"""
+ """return an astroid.Repr node as string"""
return '`%s`' % node.value.accept(self)
def visit_binop(self, node):
@@ -133,7 +133,7 @@ class AsStringVisitor(object):
return 'break'
def visit_call(self, node):
- """return an astroid.CallFunc node as string"""
+ """return an astroid.Call node as string"""
expr_str = node.func.accept(self)
args = [arg.accept(self) for arg in node.args]
if node.keywords:
@@ -149,7 +149,7 @@ class AsStringVisitor(object):
return '%s(%s)' % (expr_str, ', '.join(args))
def visit_classdef(self, node):
- """return an astroid.Class node as string"""
+ """return an astroid.ClassDef node as string"""
decorate = node.decorators and node.decorators.accept(self) or ''
bases = ', '.join([n.accept(self) for n in node.bases])
if sys.version_info[0] == 2:
@@ -267,7 +267,7 @@ class AsStringVisitor(object):
return fors
def visit_importfrom(self, node):
- """return an astroid.From node as string"""
+ """return an astroid.ImportFrom node as string"""
return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
_import_string(node.names))
@@ -290,7 +290,7 @@ class AsStringVisitor(object):
body=self._stmt_list(node.body))
def visit_generatorexp(self, node):
- """return an astroid.GenExpr node as string"""
+ """return an astroid.GeneratorExp node as string"""
return '(%s %s)' % (node.elt.accept(self),
' '.join([n.accept(self) for n in node.generators]))
diff --git a/astroid/bases.py b/astroid/bases.py
index 146d8e7a..309ab7ba 100644
--- a/astroid/bases.py
+++ b/astroid/bases.py
@@ -390,8 +390,8 @@ class NodeNG(object):
It represents a node of the new abstract syntax tree.
"""
is_statement = False
- optional_assign = False # True for For (and for Comprehension if py <3.0)
- is_function = False # True for Function nodes
+ optional_assign = False # True for For (and for Comprehension if py <3.0)
+ is_function = False # True for FunctionDef nodes
# attributes below are set by the builder module or by raw factories
lineno = None
col_offset = None
@@ -490,13 +490,16 @@ class NodeNG(object):
return self.parent.statement()
def frame(self):
- """return the first parent frame node (i.e. Module, Function or Class)
+ """return the first parent frame node (i.e. Module, FunctionDef or
+ ClassDef)
+
"""
return self.parent.frame()
def scope(self):
- """return the first node defining a new scope (i.e. Module, Function,
- Class, Lambda but also GenExpr)
+ """return the first node defining a new scope (i.e. Module,
+ FunctionDef, ClassDef, Lambda but also GenExpr)
+
"""
return self.parent.scope()
@@ -647,7 +650,7 @@ class NodeNG(object):
return self.inferred()
def instanciate_class(self):
- """instanciate a node if it is a Class node, else return self"""
+ """instanciate a node if it is a ClassDef node, else return self"""
return self
def has_base(self, node):
diff --git a/astroid/brain/py2gi.py b/astroid/brain/py2gi.py
index e5c8174d..f8acb428 100644
--- a/astroid/brain/py2gi.py
+++ b/astroid/brain/py2gi.py
@@ -192,4 +192,4 @@ def _register_require_version(node):
return node
MANAGER.register_failed_import_hook(_import_gi_module)
-MANAGER.register_transform(nodes.CallFunc, _register_require_version, _looks_like_require_version)
+MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version)
diff --git a/astroid/brain/py2stdlib.py b/astroid/brain/py2stdlib.py
index b742823c..a3bd4ec1 100644
--- a/astroid/brain/py2stdlib.py
+++ b/astroid/brain/py2stdlib.py
@@ -36,7 +36,7 @@ def infer_func_form(node, base_type, context=None, enum=False):
except StopIteration:
raise InferenceError()
- # node is a CallFunc node, class name as first argument and generated class
+ # node is a Call node, class name as first argument and generated class
# attributes as second argument
if len(node.args) != 2:
# something weird here, go back to class implementation
@@ -263,7 +263,7 @@ def looks_like_namedtuple(node):
return False
def infer_named_tuple(node, context=None):
- """Specific inference function for namedtuple CallFunc node"""
+ """Specific inference function for namedtuple Call node"""
class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied,
context=context)
fake = AstroidBuilder(MANAGER).string_build('''
@@ -285,7 +285,7 @@ class %(name)s(tuple):
return iter([class_node])
def infer_enum(node, context=None):
- """ Specific inference function for enum CallFunc node. """
+ """ Specific inference function for enum Call node. """
enum_meta = nodes.ClassDef("EnumMeta", 'docstring')
class_node = infer_func_form(node, enum_meta,
context=context, enum=True)[0]
diff --git a/astroid/helpers.py b/astroid/helpers.py
index 9fe8bd8d..2b318410 100644
--- a/astroid/helpers.py
+++ b/astroid/helpers.py
@@ -126,7 +126,7 @@ def has_known_bases(klass, context=None):
for base in klass.bases:
result = safe_infer(base, context=context)
# TODO: check for A->B->A->B pattern in class structure too?
- if (not isinstance(result, scoped_nodes.Class) or
+ if (not isinstance(result, scoped_nodes.ClassDef) or
result is klass or
not has_known_bases(result, context=context)):
klass._all_bases_known = False
diff --git a/astroid/inference.py b/astroid/inference.py
index afba7fb8..052c1b9e 100644
--- a/astroid/inference.py
+++ b/astroid/inference.py
@@ -41,7 +41,9 @@ MANAGER = manager.AstroidManager()
def infer_end(self, context=None):
- """inference's end for node such as Module, Class, Function, Const...
+ """inference's end for node such as Module, ClassDef, FunctionDef,
+ Const...
+
"""
yield self
nodes.Module._infer = infer_end
@@ -603,7 +605,7 @@ nodes.Arguments._infer = infer_arguments
@bases.path_wrapper
def infer_assign(self, context=None):
- """infer a AssName/AssAttr: need to inspect the RHS part of the
+ """infer a AssignName/AssignAttr: need to inspect the RHS part of the
assign node
"""
stmt = self.statement()
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index 9e424f74..ce453a63 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -1341,9 +1341,15 @@ def const_factory(value):
# Backward-compatibility aliases
+def instancecheck(cls, other):
+ wrapped = cls.__wrapped__
+ other_cls = other.__class__
+ return wrapped is other_cls or issubclass(other_cls, wrapped)
+
def proxy_alias(alias_name, node_type):
proxy = type(alias_name, (lazy_object_proxy.Proxy,),
- {'__class__': object.__dict__['__class__']})
+ {'__class__': object.__dict__['__class__'],
+ '__instancecheck__': instancecheck})
return proxy(lambda: node_type)
Backquote = proxy_alias('Backquote', Repr)
diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py
index d55eb765..4d8e3d6e 100644
--- a/astroid/rebuilder.py
+++ b/astroid/rebuilder.py
@@ -19,6 +19,8 @@
order to get a single Astroid representation
"""
+import inspect
+
import _ast
import sys
@@ -90,15 +92,15 @@ def _get_doc(node):
pass # ast built from scratch
return node, None
-def _visit_or_none(node, attr, visitor, parent, assign_ctx, visit='visit'):
+def _visit_or_none(node, attr, visitor, parent, assign_ctx, visit='visit',
+ **kws):
"""If the given node has an attribute, visits the attribute, and
otherwise returns None.
"""
-
value = getattr(node, attr, None)
if value:
- return getattr(visitor, visit)(value, parent, assign_ctx)
+ return getattr(visitor, visit)(value, parent, assign_ctx, **kws)
else:
return None
@@ -220,8 +222,10 @@ class TreeRebuilder(object):
self.visit(node.value, newnode, None))
return newnode
- def visit_assignname(self, node, parent, node_name=None):
+ def visit_assignname(self, node, parent, assign_ctx=None, node_name=None):
'''visit a node and return a AssignName node'''
+ # assign_ctx is not used here, it takes that argument only to
+ # maintain consistency with the other visit functions.
newnode = nodes.AssignName(node_name, getattr(node, 'lineno', None),
getattr(node, 'col_offset', None), parent)
self._save_assignment(newnode)
@@ -299,7 +303,7 @@ class TreeRebuilder(object):
return newnode
def visit_classdef(self, node, parent, assign_ctx=None, newstyle=None):
- """visit a Class node to become astroid"""
+ """visit a ClassDef node to become astroid"""
node, doc = _get_doc(node)
newnode = nodes.ClassDef(node.name, doc, node.lineno,
node.col_offset, parent)
@@ -351,7 +355,7 @@ class TreeRebuilder(object):
def visit_decorators(self, node, parent, assign_ctx=None):
"""visit a Decorators node by returning a fresh instance of it"""
- # /!\ node is actually a _ast.Function node while
+ # /!\ node is actually a _ast.FunctionDef node while
# parent is a astroid.nodes.FunctionDef node
newnode = nodes.Decorators(node.lineno, node.col_offset, parent)
newnode.postinit([self.visit(child, newnode, assign_ctx)
@@ -439,7 +443,7 @@ class TreeRebuilder(object):
return newnode
def visit_importfrom(self, node, parent, assign_ctx=None):
- """visit a From node by returning a fresh instance of it"""
+ """visit an ImportFrom node by returning a fresh instance of it"""
names = [(alias.name, alias.asname) for alias in node.names]
newnode = nodes.ImportFrom(node.module or '', names, node.level or None,
getattr(node, 'lineno', None),
@@ -449,7 +453,7 @@ class TreeRebuilder(object):
return newnode
def visit_functiondef(self, node, parent, assign_ctx=None):
- """visit an Function node to become astroid"""
+ """visit an FunctionDef node to become astroid"""
self._global_names.append({})
node, doc = _get_doc(node)
newnode = nodes.FunctionDef(node.name, doc, node.lineno,
@@ -470,7 +474,7 @@ class TreeRebuilder(object):
return newnode
def visit_generatorexp(self, node, parent, assign_ctx=None):
- """visit a GenExpr node by returning a fresh instance of it"""
+ """visit a GeneratorExp node by returning a fresh instance of it"""
newnode = nodes.GeneratorExp(node.lineno, node.col_offset, parent)
newnode.postinit(self.visit(node.elt, newnode, assign_ctx),
[self.visit(child, newnode, assign_ctx)
@@ -730,8 +734,8 @@ class TreeRebuilder3(TreeRebuilder):
def visit_arg(self, node, parent, assign_ctx=None):
"""visit a arg node by returning a fresh AssName instance"""
- # TODO(cpopa): introduce an Arg node instead of using AssName.
- return self.visit_assignname(node, parent, node.arg)
+ # TODO(cpopa): introduce an Arg node instead of using AssignName.
+ return self.visit_assignname(node, parent, assign_ctx, node.arg)
def visit_nameconstant(self, node, parent, assign_ctx=None):
# in Python 3.4 we have NameConstant for True / False / None
@@ -741,8 +745,12 @@ class TreeRebuilder3(TreeRebuilder):
def visit_excepthandler(self, node, parent, assign_ctx=None):
"""visit an ExceptHandler node by returning a fresh instance of it"""
newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent)
+ if node.name:
+ name = self.visit_assignname(node, newnode, assign_ctx, node.name)
+ else:
+ name = None
newnode.postinit(_visit_or_none(node, 'type', self, newnode, assign_ctx),
- _visit_or_none(node, 'name', self, newnode, 'Assign', visit='visit_assignname'),
+ name,
[self.visit(child, newnode, assign_ctx)
for child in node.body])
return newnode
@@ -773,10 +781,12 @@ class TreeRebuilder3(TreeRebuilder):
# TryFinally/TryExcept nodes
if node.finalbody:
newnode = nodes.TryFinally(node.lineno, node.col_offset, parent)
- newnode.postinit([self.visit_tryexcept(node, newnode, assign_ctx)]
- if node.handlers else
- [self.visit(child, newnode, assign_ctx)
- for child in node.body],
+ if node.handlers:
+ body = [self.visit_tryexcept(node, newnode, assign_ctx)]
+ else:
+ body = [self.visit(child, newnode, assign_ctx)
+ for child in node.body]
+ newnode.postinit(body,
[self.visit(n, newnode, assign_ctx)
for n in node.finalbody])
return newnode
@@ -792,7 +802,8 @@ class TreeRebuilder3(TreeRebuilder):
newnode = nodes.With(node.lineno, node.col_offset, parent)
def visit_child(child):
expr = self.visit(child.context_expr, newnode, assign_ctx)
- var = _visit_or_none(child, 'optional_vars', self, newnode, 'Assign')
+ var = _visit_or_none(child, 'optional_vars', self, newnode,
+ 'Assign')
return expr, var
newnode.postinit([visit_child(child) for child in node.items],
[self.visit(child, newnode, None)
@@ -808,7 +819,7 @@ class TreeRebuilder3(TreeRebuilder):
def visit_classdef(self, node, parent, assign_ctx=None):
return super(TreeRebuilder3, self).visit_classdef(node, parent,
assign_ctx,
- True)
+ newstyle=True)
if sys.version_info >= (3, 0):
TreeRebuilder = TreeRebuilder3
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 7a312bbe..4e2c31fa 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -1342,14 +1342,16 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
"""Get an attribute from this class, using Python's attribute semantic
This method doesn't look in the instance_attrs dictionary
- since it's done by an Instance proxy at inference time.
- It may return a YES object if the attribute has not been actually
+ since it's done by an Instance proxy at inference time. It
+ may return a YES object if the attribute has not been actually
found but a __getattr__ or __getattribute__ method is defined.
- If *class_context* is given, then it's considered that the attribute
- is accessed from a class context, e.g. Class.attribute, otherwise
- it might have been accessed from an instance as well.
- If *class_context* is used in that case, then a lookup in the
- implicit metaclass and the explicit metaclass will be done.
+ If *class_context* is given, then it's considered that the
+ attribute is accessed from a class context,
+ e.g. ClassDef.attribute, otherwise it might have been accessed
+ from an instance as well. If *class_context* is used in that
+ case, then a lookup in the implicit metaclass and the explicit
+ metaclass will be done.
+
"""
values = self.locals.get(name, [])
if name in self.special_attributes:
@@ -1395,7 +1397,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
return
for attr in bases._infer_stmts(attrs, context, frame=cls):
- if not isinstance(attr, Function):
+ if not isinstance(attr, FunctionDef):
yield attr
continue
diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py
index 6ce6c458..dd3cd91b 100644
--- a/astroid/tests/unittest_inference.py
+++ b/astroid/tests/unittest_inference.py
@@ -2138,7 +2138,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
('frozenset()', False),
('frozenset((1, 2))', True),
]
- # import pdb; pdb.set_trace()
for code, expected in pairs:
node = test_utils.extract_node(code)
inferred = next(node.infer())
@@ -2569,8 +2568,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
f = A()
f += B() #@
''')
- print('TEST HERE', ast_node)
- import pdb; pdb.set_trace()
inferred = next(ast_node.infer())
self.assertIsInstance(inferred, Instance)
self.assertEqual(inferred.name, 'A')
diff --git a/astroid/tests/unittest_nodes.py b/astroid/tests/unittest_nodes.py
index 2a13556b..c18a8542 100644
--- a/astroid/tests/unittest_nodes.py
+++ b/astroid/tests/unittest_nodes.py
@@ -17,6 +17,8 @@
# with astroid. If not, see <http://www.gnu.org/licenses/>.
"""tests for specific behaviour of astroid nodes
"""
+from astroid.as_string import dump
+
import os
import sys
import textwrap
@@ -30,8 +32,10 @@ from astroid import context as contextmod
from astroid import exceptions
from astroid import node_classes
from astroid import nodes
+from astroid import parse
from astroid import util
from astroid import test_utils
+from astroid import transforms
from astroid.tests import resources
@@ -473,6 +477,7 @@ class ArgumentsNodeTC(unittest.TestCase):
pass
''')
new = cls.getattr('__new__')[-1]
+ # print(dump(new))
self.assertEqual(new.args.fromlineno, 0)
@@ -549,5 +554,108 @@ class BoundMethodNodeTest(unittest.TestCase):
self.assertIsInstance(inferred, bases.BoundMethod)
+class AliasesTest(unittest.TestCase):
+
+ def setUp(self):
+ self.transformer = transforms.TransformVisitor()
+
+ def parse_transform(self, code):
+ module = parse(code, apply_transforms=False)
+ return self.transformer.visit(module)
+
+ def test_aliases(self):
+ def test_from(node):
+ node.names = node.names + [('absolute_import', None)]
+ return node
+
+ def test_class(node):
+ node.name = 'Bar'
+ return node
+
+ def test_function(node):
+ node.name = 'another_test'
+ return node
+
+ def test_callfunc(node):
+ if node.func.name == 'Foo':
+ node.func.name = 'Bar'
+ return node
+
+ def test_assname(node):
+ if node.name == 'foo':
+ return nodes.AssignName('bar', node.lineno, node.col_offset,
+ node.parent)
+ def test_assattr(node):
+ if node.attrname == 'a':
+ node.attrname = 'b'
+ return node
+
+ def test_getattr(node):
+ if node.attrname == 'a':
+ node.attrname = 'b'
+ return node
+
+ def test_genexpr(node):
+ if node.elt.value == 1:
+ node.elt = nodes.Const(2, node.lineno, node.col_offset,
+ node.parent)
+ return node
+
+ self.transformer.register_transform(nodes.From, test_from)
+ self.transformer.register_transform(nodes.Class, test_class)
+ self.transformer.register_transform(nodes.Function, test_function)
+ self.transformer.register_transform(nodes.CallFunc, test_callfunc)
+ self.transformer.register_transform(nodes.AssName, test_assname)
+ self.transformer.register_transform(nodes.AssAttr, test_assattr)
+ self.transformer.register_transform(nodes.Getattr, test_getattr)
+ self.transformer.register_transform(nodes.GenExpr, test_genexpr)
+
+ string = '''
+ from __future__ import print_function
+
+ class Foo: pass
+
+ def test(a): return a
+
+ foo = Foo()
+ foo.a = test(42)
+ foo.a
+ (1 for _ in range(0, 42))
+ '''
+
+ module = self.parse_transform(string)
+
+ self.assertEqual(len(module.body[0].names), 2)
+ self.assertIsInstance(module.body[0], nodes.From)
+ self.assertEqual(module.body[1].name, 'Bar')
+ self.assertIsInstance(module.body[1], nodes.Class)
+ self.assertEqual(module.body[2].name, 'another_test')
+ self.assertIsInstance(module.body[2], nodes.Function)
+ self.assertEqual(module.body[3].targets[0].name, 'bar')
+ self.assertIsInstance(module.body[3].targets[0], nodes.AssName)
+ self.assertEqual(module.body[3].value.func.name, 'Bar')
+ self.assertIsInstance(module.body[3].value, nodes.CallFunc)
+ self.assertEqual(module.body[4].targets[0].attrname, 'b')
+ self.assertIsInstance(module.body[4].targets[0], nodes.AssAttr)
+ self.assertIsInstance(module.body[5], nodes.Discard)
+ self.assertEqual(module.body[5].value.attrname, 'b')
+ self.assertIsInstance(module.body[5].value, nodes.Getattr)
+ self.assertEqual(module.body[6].value.elt.value, 2)
+ self.assertIsInstance(module.body[6].value, nodes.GenExpr)
+
+ @unittest.skipIf(six.PY3, "Python 3 doesn't have Repr nodes.")
+ def test_repr(self):
+ def test_backquote(node):
+ node.value.name = 'bar'
+ return node
+
+ self.transformer.register_transform(nodes.Backquote, test_backquote)
+
+ module = self.parse_transform('`foo`')
+
+ self.assertEqual(module.body[0].value.value.name, 'bar')
+ self.assertIsInstance(module.body[0].value, nodes.Backquote)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py
index 1a9c20bd..19aea1e8 100644
--- a/astroid/tests/unittest_scoped_nodes.py
+++ b/astroid/tests/unittest_scoped_nodes.py
@@ -343,7 +343,7 @@ class FunctionNodeTest(ModuleLoader, unittest.TestCase):
## def test_raises(self):
## method = self.module2['AbstractClass']['to_override']
## self.assertEqual([str(term) for term in method.raises()],
-## ["CallFunc(Name('NotImplementedError'), [], None, None)"] )
+## ["Call(Name('NotImplementedError'), [], None, None)"] )
## def test_returns(self):
## method = self.module2['AbstractClass']['return_something']
diff --git a/astroid/tests/unittest_transforms.py b/astroid/tests/unittest_transforms.py
index fe206a1e..b94d60fb 100644
--- a/astroid/tests/unittest_transforms.py
+++ b/astroid/tests/unittest_transforms.py
@@ -17,7 +17,6 @@
# with astroid. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
-import sys
import contextlib
import time