From 57fb251b67c399071023d2786ce00871108e280f Mon Sep 17 00:00:00 2001 From: aliassar Date: Tue, 3 Nov 2026 18:05:57 +0100 Subject: [PATCH] fix(extract): raise RefactoringError for empty or whitespace-only region ExtractMethod and ExtractVariable raised IndexError for an empty region and ModuleSyntaxError for a whitespace-only one. Check for this in _ExceptionalConditionChecker.base_conditions() so both report a RefactoringError like other bad regions. Fixes #926 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + rope/refactor/extract.py | 4 ++++ ropetest/refactor/extracttest.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee7ddf653..41c7f042d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - #895 patchedast Starred and keyword now consumes their syntactically expected and ** (@lieryan) - #896 patchedast cleanup and refactoring (@lieryan) - #905 Ignore `node_modules` directories by default when scanning project files and source folders. (@marcoeangeli) +- #926, #929 Raise RefactoringError instead of IndexError/ModuleSyntaxError when extracting an empty or whitespace-only region (@aliassar) # Release 1.15.0 diff --git a/rope/refactor/extract.py b/rope/refactor/extract.py index 1b1659fad..ea373613d 100644 --- a/rope/refactor/extract.py +++ b/rope/refactor/extract.py @@ -442,6 +442,10 @@ def __call__(self, info): self.multi_line_conditions(info) def base_conditions(self, info): + if not info.extracted.strip(): + raise RefactoringError( + "Extracted piece should contain at least one expression or statement." + ) if info.region[1] > info.scope_region[1]: raise RefactoringError("Bad region selected for extract method") diff --git a/ropetest/refactor/extracttest.py b/ropetest/refactor/extracttest.py index 4b34d58d3..53a225d6e 100644 --- a/ropetest/refactor/extracttest.py +++ b/ropetest/refactor/extracttest.py @@ -472,6 +472,36 @@ class AClass(object): with self.assertRaises(rope.base.exceptions.RefactoringError): self.do_extract_method(code, start, end, "new_func") + def test_extract_method_empty_region(self): + code = dedent("""\ + def a_func(arg): + var = arg + 1 + return var + """) + start = code.index("arg + 1") + with self.assertRaises(rope.base.exceptions.RefactoringError): + self.do_extract_method(code, start, start, "new_func") + + def test_extract_method_whitespace_only_region(self): + code = dedent("""\ + def a_func(arg): + var = arg + 1 + return var + """) + start = code.index(" var") + with self.assertRaises(rope.base.exceptions.RefactoringError): + self.do_extract_method(code, start, start + 4, "new_func") + + def test_extract_variable_empty_region(self): + code = dedent("""\ + def a_func(arg): + var = arg + 1 + return var + """) + start = code.index("arg + 1") + with self.assertRaises(rope.base.exceptions.RefactoringError): + self.do_extract_variable(code, start, start, "new_var") + def test_extract_method_containing_return(self): code = dedent("""\ def a_func(arg):