Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing scopes region by offset #429

Merged
merged 5 commits into from Oct 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 13 additions & 4 deletions rope/base/pyscopes.py
Expand Up @@ -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"""

Expand All @@ -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)
Expand Down
11 changes: 3 additions & 8 deletions rope/refactor/patchedast.py
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @climbus, is there any special reason why you removed this warning?

I'm a bit hesitant of removing this warning without understanding the context of why it was added in the first place, and especially since this does not just remove the warning but by removing the return statement, it would've also allowed re-patching an already patched ast.

I've merged the rest of the PR which I think is good, but restored this warning for now. If you strongly believe that this warning is no longer relevant in current rope and should be removed, please re-open a new ticket so we can discuss it properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll check if it should or shouldn't be raised.

base_children = collections.deque(base_children)
self.children_stack.append(base_children)
children = collections.deque()
Expand Down Expand Up @@ -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):
Expand Down
47 changes: 47 additions & 0 deletions ropetest/pyscopestest.py
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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))