Skip to content

Define functions extracted from a class body before the class - #878

Open
ethanstoner wants to merge 2 commits into
python-rope:masterfrom
ethanstoner:fix/extract-method-in-class-body
Open

ethanstoner wants to merge 2 commits into
python-rope:masterfrom
ethanstoner:fix/extract-method-in-class-body

Conversation

@ethanstoner

Copy link
Copy Markdown

Description

Extract Method on an expression or statements in a class body put the new function after the class. Class bodies run when the class is defined, so the class raised NameError.

Root cause: in _DefinitionLocationFinder.find_lineno(), a class scope at module level is neither global_ nor method, so it fell through to _get_after_scope(), which inserts after the class. With global_=True it went after the top-level class for the same reason.

Changes in rope/refactor/extract.py:

  • New _ExtractInfo.class_to_define_before: when the scope is a class, the function is defined before the outermost class in the chain of enclosing classes (before any decorators), at that class's indentation. Nested classes can't see names in an enclosing class body, so the outermost class is used. With global_=True, this applies only when that class is at module level. If the class is inside a function, the existing "after the top-level function" placement is kept, since that code runs later.
  • _ExtractInfo.method now requires the scope to be a function. Before, extracting from a class nested in a class crashed with AttributeError: 'PyClass' object has no attribute 'get_kind'.
  • _FunctionInformationCollector._ClassDef now visits the body when the class is the host scope. Class variables read by the extracted code (e.g. b = a + 2) are passed as arguments. Otherwise, moving the function before the class would still raise NameError.

Fixes #825

Checklist (delete if not relevant):

  • I have added tests that prove my fix is effective or that my feature works
  • I have updated CHANGELOG.md

Tests: 7 new tests in ropetest/refactor/extracttest.py cover the issue case, class variables, a decorated class, a class inside a function, a nested class, and global_=True. 6 fail without the fix. The seventh (global_=True on a class inside a function) already passed and checks that case isn't changed. Ran pytest ropetest (2155 passed, 7 skipped, 5 xfailed), plus black 26.5.1 and isort.

Generated with Claude Code on behalf of @ethanstoner.

Extract Method on code in a class body inserted the new function after
the class, but class bodies run at definition time, so the class raised
NameError. Insert the function before the (outermost) class instead, and
collect class-body variables so they are passed as arguments.

Fixes python-rope#825
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The extracted method was defined outside of class scope causing 'NameError'

1 participant