diff --git a/rope/base/pyscopes.py b/rope/base/pyscopes.py index 25a850696..c6050d19b 100644 --- a/rope/base/pyscopes.py +++ b/rope/base/pyscopes.py @@ -101,14 +101,15 @@ def get_logical_end(self): def get_kind(self): pass - @utils.saveit def get_region(self): - node = patchedast.patch_ast( - self.pyobject.get_ast(), self.pyobject.get_module().source_code - ) + self._calculate_scope_regions_for_module() + node = self.pyobject.get_ast() region = patchedast.node_region(node) return region + def _calculate_scope_regions_for_module(self): + self._get_global_scope()._calculate_scope_regions() + def in_region(self, offset): """Checks if offset is in scope region""" @@ -135,6 +136,14 @@ def get_name(self, name): return self.builtin_names[name] raise exceptions.NameNotFoundError("name %s not found" % name) + @utils.saveit + def _calculate_scope_regions(self): + source = self._get_source() + patchedast.patch_ast(self.pyobject.get_ast(), source) + + def _get_source(self): + return self.pyobject.source_code + def get_names(self): if self.names.get() is None: result = dict(self.builtin_names) diff --git a/rope/refactor/patchedast.py b/rope/refactor/patchedast.py index 36ce4a3a6..5a8c59dd7 100644 --- a/rope/refactor/patchedast.py +++ b/rope/refactor/patchedast.py @@ -97,14 +97,6 @@ def __call__(self, node): node.sorted_children = ast.get_children(node) def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): - if hasattr(node, "region"): - # ???: The same node was seen twice; what should we do? - warnings.warn( - "Node <%s> has been already patched; please report!" - % node.__class__.__name__, - RuntimeWarning, - ) - return base_children = collections.deque(base_children) self.children_stack.append(base_children) children = collections.deque() @@ -884,6 +876,9 @@ def _With(self, node): children.extend(node.body) self._handle(node, children) + def _AsyncWith(self, node): + return self._With(node) + def _child_nodes(self, nodes, separator): children = [] for index, child in enumerate(nodes): diff --git a/ropetest/pyscopestest.py b/ropetest/pyscopestest.py index 0003be260..9c21f66b3 100644 --- a/ropetest/pyscopestest.py +++ b/ropetest/pyscopestest.py @@ -3,6 +3,8 @@ except ImportError: import unittest +from textwrap import dedent + from rope.base import libutils from rope.base.pyobjects import get_base_type from ropetest import testutils @@ -328,6 +330,19 @@ def test_get_scope_for_offset_for_scope_with_indent(self): inner_scope = scope.get_scopes()[0] self.assertEqual(inner_scope, scope.get_inner_scope_for_offset(10)) + @testutils.only_for("3.5") + def test_get_scope_for_offset_for_function_scope_and_async_with_statement(self): + scope = libutils.get_string_scope( + self.project, + dedent("""\ + async def func(): + async with a_func() as var: + print(var) + """) + ) + inner_scope = scope.get_scopes()[0] + self.assertEqual(inner_scope, scope.get_inner_scope_for_offset(27)) + def test_getting_overwritten_scopes(self): scope = libutils.get_string_scope( self.project, "def f():\n pass\ndef f():\n pass\n" @@ -414,3 +429,35 @@ def test_get_inner_scope_for_nested_list_comprhension(self): self.assertEqual(len(scope.get_scopes()[0].get_scopes()), 1) self.assertIn("j", scope.get_scopes()[0].get_scopes()[0]) self.assertIn("i", scope.get_scopes()[0].get_scopes()[0]) + + def test_get_scope_region(self): + scope = libutils.get_string_scope( + self.project, + dedent( + """ + def func1(ala): + pass + + def func2(o): + pass""" + ), + ) + + self.assertEqual(scope.get_region(), (0, 47)) + self.assertEqual(scope.get_scopes()[0].get_region(), (1, 24)) + self.assertEqual(scope.get_scopes()[1].get_region(), (26, 47)) + + def test_only_get_inner_scope_region(self): + scope = libutils.get_string_scope( + self.project, + dedent( + """ + def func1(ala): + pass + + def func2(o): + pass""" + ), + ) + + self.assertEqual(scope.get_scopes()[1].get_region(), (26, 47))