From cddcbf3a67b924898fa66f1f5e8514347537a0e2 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Thu, 1 Oct 2026 21:11:01 -0700 Subject: [PATCH] Resolve function-local constructor names through their scope --- CHANGELOG.md | 1 + rope/refactor/change_signature.py | 5 +- ropetest/refactor/change_signature_test.py | 92 ++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff6f27ed8..ed59c09c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # **Upcoming release** - ... +- Fix changing constructor signatures for classes defined inside functions (#692). # Release 1.15.0 diff --git a/rope/refactor/change_signature.py b/rope/refactor/change_signature.py index c6671b084..c0f506e29 100644 --- a/rope/refactor/change_signature.py +++ b/rope/refactor/change_signature.py @@ -39,7 +39,10 @@ def _set_name_and_pyname(self): and isinstance(pyobject.parent, pyobjects.PyClass) ): pyclass = pyobject.parent - self.others = (pyclass.get_name(), pyclass.parent[pyclass.get_name()]) + self.others = ( + pyclass.get_name(), + pyclass.parent.get_scope()[pyclass.get_name()], + ) def _change_calls( self, diff --git a/ropetest/refactor/change_signature_test.py b/ropetest/refactor/change_signature_test.py index be14f2d71..9a4048927 100644 --- a/ropetest/refactor/change_signature_test.py +++ b/ropetest/refactor/change_signature_test.py @@ -775,6 +775,98 @@ def __init__(self, p): self.mod.read(), ) + def test_changing_signature_for_function_local_constructor(self): + code = dedent("""\ + def make(): + class Local: + def __init__(self, value): + pass + item = Local(1) + item.__init__(2) + return item + """) + expected = dedent("""\ + def make(): + class Local: + def __init__(self): + pass + item = Local() + item.__init__() + return item + """) + offsets = ( + code.index("Local"), + code.index("__init__"), + code.rindex("Local"), + code.rindex("__init__"), + ) + for offset in offsets: + with self.subTest(offset=offset): + self.mod.write(code) + signature = change_signature.ChangeSignature( + self.project, self.mod, offset + ) + self.project.do( + signature.get_changes([change_signature.ArgumentRemover(1)]) + ) + self.assertEqual(expected, self.mod.read()) + + def test_function_local_constructors_with_the_same_name_are_distinct(self): + self.mod.write(dedent("""\ + def first(): + class Local: + def __init__(self, value): + pass + return Local(1) + def second(): + class Local: + def __init__(self, value): + pass + return Local(2) + """)) + signature = change_signature.ChangeSignature( + self.project, self.mod, self.mod.read().index("__init__") + ) + self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) + self.assertEqual( + dedent("""\ + def first(): + class Local: + def __init__(self): + pass + return Local() + def second(): + class Local: + def __init__(self, value): + pass + return Local(2) + """), + self.mod.read(), + ) + + def test_changing_signature_for_class_nested_constructor(self): + self.mod.write(dedent("""\ + class Outer: + class Inner: + def __init__(self, value): + pass + item = Outer.Inner(1) + """)) + signature = change_signature.ChangeSignature( + self.project, self.mod, self.mod.read().index("__init__") + ) + self.project.do(signature.get_changes([change_signature.ArgumentRemover(1)])) + self.assertEqual( + dedent("""\ + class Outer: + class Inner: + def __init__(self): + pass + item = Outer.Inner() + """), + self.mod.read(), + ) + def test_redordering_arguments_reported_by_mft(self): self.mod.write(dedent("""\ def f(a, b, c):