diff options
author | Ceridwen <ceridwenv@gmail.com> | 2015-08-21 12:30:06 -0400 |
---|---|---|
committer | Ceridwen <ceridwenv@gmail.com> | 2015-08-21 12:30:06 -0400 |
commit | 7fe6eff8b7bec34bf04226fae8e68bad11900e42 (patch) | |
tree | 4e68578c458541df9b30f2b7a610c1ca721d6f28 | |
parent | 57551b823a3f1b68085d02f6f6321dbe84e1d074 (diff) | |
download | astroid-git-7fe6eff8b7bec34bf04226fae8e68bad11900e42.tar.gz |
Add tests for aliases
-rw-r--r-- | astroid/as_string.py | 10 | ||||
-rw-r--r-- | astroid/bases.py | 15 | ||||
-rw-r--r-- | astroid/brain/py2gi.py | 2 | ||||
-rw-r--r-- | astroid/brain/py2stdlib.py | 6 | ||||
-rw-r--r-- | astroid/helpers.py | 2 | ||||
-rw-r--r-- | astroid/inference.py | 6 | ||||
-rw-r--r-- | astroid/node_classes.py | 8 | ||||
-rw-r--r-- | astroid/rebuilder.py | 47 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 18 | ||||
-rw-r--r-- | astroid/tests/unittest_inference.py | 3 | ||||
-rw-r--r-- | astroid/tests/unittest_nodes.py | 108 | ||||
-rw-r--r-- | astroid/tests/unittest_scoped_nodes.py | 2 | ||||
-rw-r--r-- | astroid/tests/unittest_transforms.py | 1 |
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
|