diff options
author | Ceridwen <ceridwenv@gmail.com> | 2016-02-21 16:25:01 -0500 |
---|---|---|
committer | Ceridwen <ceridwenv@gmail.com> | 2016-02-21 16:25:01 -0500 |
commit | 51bd977f064520f5efe2795e484d2c0728128e08 (patch) | |
tree | 236714776386c16d04cb9ea83c0e9148091a2da5 | |
parent | 54e5ddc279c4281e1612380af67051734ae30064 (diff) | |
download | astroid-git-51bd977f064520f5efe2795e484d2c0728128e08.tar.gz |
Revise zipper tests for speed and to be more accurate about what is being tested
-rw-r--r-- | astroid/interpreter/lookup.py | 13 | ||||
-rw-r--r-- | astroid/tests/unittest_zipper.py | 108 | ||||
-rw-r--r-- | astroid/tree/base.py | 20 |
3 files changed, 77 insertions, 64 deletions
diff --git a/astroid/interpreter/lookup.py b/astroid/interpreter/lookup.py index 20fda77a..a2fb88e1 100644 --- a/astroid/interpreter/lookup.py +++ b/astroid/interpreter/lookup.py @@ -32,8 +32,7 @@ except ImportError: from dictproxyhack import dictproxy as MappingProxyType -class LocalsDictNode(treebase.LookupMixIn, - treebase.NodeNG): +class LocalsDictNode(treebase.LookupMixIn, treebase.NodeNG): """Provides locals handling common to Module, FunctionDef and ClassDef nodes, including a dict like interface for direct access to locals information @@ -109,11 +108,11 @@ class LocalsDictNode(treebase.LookupMixIn, """ return self.locals[item][0] - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.locals) + # def __iter__(self): + # """method from the `dict` interface returning an iterator on + # `self.keys()` + # """ + # return iter(self.locals) def keys(self): """method from the `dict` interface returning a tuple containing diff --git a/astroid/tests/unittest_zipper.py b/astroid/tests/unittest_zipper.py index 7ba57378..578146be 100644 --- a/astroid/tests/unittest_zipper.py +++ b/astroid/tests/unittest_zipper.py @@ -1,6 +1,8 @@ -import cProfile +import collections import itertools +import pprint import os +import unittest import hypothesis from hypothesis import strategies @@ -9,68 +11,60 @@ import astroid from astroid.tree import zipper -# __init__.py screens out the empty init files, testdata because of -# the deliberately-broken files, and unittests because of #310. +# This screens out the empty init files. astroid_file = strategies.sampled_from(os.path.join(p, n) for p, _, ns in os.walk('astroid/') for n in ns if n.endswith('.py') and '__init__.py' not in n) -def parse_ast(name): +MapNode = collections.namedtuple('MapNode', 'children parent moves') + +def ast_from_file_name(name): with open(name, 'r') as source_file: print(name) - ast = zipper.Zipper(astroid.parse(source_file.read())) - return (ast.down,) - -base_case = strategies.builds(parse_ast, astroid_file) - -def possible_moves(path): - # position = path[-1]() - # previous_position = path[-1].__self__ - # if not len(previous_position): - # return () - # length = 0 - # # If the zipper produces a position that is not the child of the - # # previous position, this is a bug and thus should crash. - # for child in previous_position: - # length += 1 - # if child == position: - # index = length - # break - # else: - # print(position) - # print([repr(c) for c in previous_position]) - # raise astroid.exceptions.AstroidError( - # 'Invalid AST: {child!r} is not a child of {parent!r}', - # child=position, parent=previous_position) - # moves = [] - # if position._self_path: - # moves.append(position.up) - # if length > 0: - # moves.append(position.down) - # if index < length: - # moves.append(position.right) - # if index > 0: - # moves.append(position.left) - position = path[-1]() - return tuple(m for m in (position.down, position.up, position.left, position.right) if m() is not None) - -extend = lambda path_strategy: path_strategy.flatmap(lambda path: strategies.sampled_from(possible_moves(path)).map(lambda move: path + (move,))) - -walk_ast = strategies.recursive(base_case, extend) + root = astroid.parse(source_file.read()) + to_visit = [(root, None)] + ast = {} + while to_visit: + node, parent = to_visit.pop() + children = tuple(iter(node)) if node else () + to_visit.extend((c, node) for c in children) + moves = [] + if children: + moves.append(zipper.Zipper.down) + if parent: + moves.append(zipper.Zipper.up) + index = ast[id(parent)].children.index(node) + if index > 0: + moves.append(zipper.Zipper.left) + if index < len(ast[id(parent)].children) - 1: + moves.append(zipper.Zipper.right) + ast[id(node)] = MapNode(children, parent, tuple(moves)) + return ast, root -# print(walk_ast.example()) +ast_strategy = strategies.builds(ast_from_file_name, astroid_file) -# cProfile.run('print(walk_ast.example())') +# pprint.pprint(ast_strategy.example()) -@hypothesis.given(walk_ast) -def test(moves): - print(moves) - moves = tuple(reversed(moves)) - visited_positions = {id(moves[0].__self__): moves[0].__self__} - for move in moves: - position = move() - if id(position) in visited_positions: - assert(position == visited_positions[id(position)]) - visited_positions[id(position)] = position +class TestZipper(unittest.TestCase): + @hypothesis.settings(perform_health_check=False) + @hypothesis.given(ast_strategy, strategies.integers(min_value=0, max_value=100), strategies.choices()) + def test(self, ast_root, length, choice): + ast, root = ast_root + old_position = zipper.Zipper(root) + for _ in range(length): + move = choice(ast[id(old_position.__wrapped__)].moves) + new_position = move(old_position) + if move is zipper.Zipper.down: + assert(new_position.__wrapped__ is next(iter(old_position))) + if move is zipper.Zipper.up: + assert(new_position.__wrapped__ is ast[id(old_position.__wrapped__)].parent) + if move is zipper.Zipper.left or move is zipper.Zipper.right: + parent = ast[id(old_position.__wrapped__)].parent + siblings = ast[id(parent)].children + index = siblings.index(old_position.__wrapped__) + if move is zipper.Zipper.left: + assert(new_position.__wrapped__ is siblings[index - 1]) + if move is zipper.Zipper.right: + assert(new_position.__wrapped__ is siblings[index + 1]) + old_position = new_position if __name__ == '__main__': - test() - # pass + unittest.main() diff --git a/astroid/tree/base.py b/astroid/tree/base.py index 58298b8d..dc5119e1 100644 --- a/astroid/tree/base.py +++ b/astroid/tree/base.py @@ -61,6 +61,26 @@ class NodeNG(object): self.lineno = lineno self.col_offset = col_offset self.parent = parent + + def __iter__(self): + for field in self._astroid_fields: + yield getattr(self, field) + + # def __eq__(self, other): + # if self.__class__ is other.__class__: + # return (all(getattr(self, f) == getattr(other, f) + # for f in self._astroid_fields) and + # all(getattr(self, f) == getattr(other, f) + # for f in self._other_fields)) + # else: + # return False + + # def __ne__(self, other): + # return not self == other + + # # Must be defined to retain object.__hash__, see + # # https://docs.python.org/3/reference/datamodel.html#object.__hash__ + # __hash__ = object.__hash__ def infer(self, context=None, **kwargs): """main interface to the interface system, return a generator on inferred |