Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- #119, #928 Fix IndexError in rename and find occurrences when a `from` import is the last thing in a module without a trailing newline (@mcepl)
- #921 Patch the else suite of try statements that also have a finally (@marlon-costa-dc)
- #937 Preserve non-ASCII argument expressions and default values when changing function signatures (@Jinyong-Li)
- #935 Rename variables declared `nonlocal` in inner functions together with the enclosing function's variable (@ryanchou1994)

# Release 1.15.1

Expand Down
16 changes: 16 additions & 0 deletions rope/base/pyobjectsdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,22 @@ def _Global(self, node):
pyname = pynamesdef.AssignedName(node.lineno)
self.names[name] = pyname

def _Nonlocal(self, node):
for name in node.names:
pyname = self._lookup_enclosing_function(name)
if pyname is not None:
self.names[name] = pyname

def _lookup_enclosing_function(self, name):
enclosing = self.owner_object.parent
while isinstance(enclosing, (PyFunction, PyClass)):
if isinstance(enclosing, PyFunction):
names = enclosing.get_scope().get_names()
if name in names:
return names[name]
enclosing = enclosing.parent
return None


class _ComprehensionVisitor(_ScopeVisitor):
def _comprehension(self, node):
Expand Down
44 changes: 44 additions & 0 deletions ropetest/refactor/renametest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,50 @@ def a_func():
refactored,
)

_nonlocal_code = dedent("""\
var_x = "global"
def outer():
def inner1():
nonlocal var_x
var_x = 1
def inner2():
nonlocal var_x
var_x = 2
def inner3():
print(var_x)
def inner4():
var_x = 4
var_x = None
return var_x
""")

_nonlocal_expected = dedent("""\
var_x = "global"
def outer():
def inner1():
nonlocal new_var
new_var = 1
def inner2():
nonlocal new_var
new_var = 2
def inner3():
print(new_var)
def inner4():
var_x = 4
new_var = None
return new_var
""")

def test_renaming_nonlocal_variables(self):
code = self._nonlocal_code
refactored = self._local_rename(code, code.index("var_x", code.index("nonlocal")), "new_var")
self.assertEqual(self._nonlocal_expected, refactored)

def test_renaming_nonlocal_variables_from_enclosing_function(self):
code = self._nonlocal_code
refactored = self._local_rename(code, code.index("var_x = None"), "new_var")
self.assertEqual(self._nonlocal_expected, refactored)

def test_renaming_when_unsure(self):
code = dedent("""\
class C(object):
Expand Down