diff --git a/CHANGELOG.md b/CHANGELOG.md index ba328bd9a..78b21632f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # **Upcoming release** +- #743 Import default-value dependencies when inlining methods across modules. (@Narek-Khachikyan) + - #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) diff --git a/rope/refactor/inline.py b/rope/refactor/inline.py index 768d30b7d..99a4603dc 100644 --- a/rope/refactor/inline.py +++ b/rope/refactor/inline.py @@ -112,6 +112,15 @@ def _init_imports(self): self.others_generator = _DefinitionGenerator( self.project, self.pyfunction, body=body ) + for name, default in self.others_generator.definition_params.items(): + if default is not None: + default, imports = move.moving_code_with_imports( + self.project, self.resource, f"{name} = ({default})\n" + ) + self.imports.extend(imports) + self.others_generator.definition_params[name] = default.split("=", 1)[ + 1 + ].strip() def _get_scope_range(self): scope = self.pyfunction.get_scope() diff --git a/ropetest/refactor/inlinetest.py b/ropetest/refactor/inlinetest.py index 585214b41..effb9ea26 100644 --- a/ropetest/refactor/inlinetest.py +++ b/ropetest/refactor/inlinetest.py @@ -1,5 +1,8 @@ +import sys +import types import unittest from textwrap import dedent +from unittest.mock import patch import rope.base.exceptions from rope.refactor import inline @@ -826,6 +829,62 @@ def ver(): self.mod2.read(), ) + def test_inlining_multiline_default(self): + code = dedent("""\ + def f(x=(1 + + 2)): + return x + result = f() + """) + namespace = {} + exec(self._inline(code, code.index("f(")), namespace) + self.assertEqual(3, namespace["result"]) + + def test_inlining_string_default_with_future_import(self): + self.mod.write(dedent("""\ + from __future__ import annotations + + def f(sep=' '): + return sep + """)) + self.mod2.write(dedent("""\ + from __future__ import annotations + from mod import f + + result = f() + """)) + self._inline2(self.mod, self.mod.read().index("f(")) + module = types.ModuleType("mod") + exec(self.mod.read(), module.__dict__) + namespace = {} + with patch.dict(sys.modules, {"mod": module}): + exec(self.mod2.read(), namespace) + self.assertEqual(" ", namespace["result"]) + + def test_inlining_method_imports_default_dependencies(self): + self.mod.write(dedent("""\ + import sys + + class Base: + def __init__(self, text): + self.raw = text + + def split(self, sep=None, maxsplit=sys.maxsize): + return self.raw.split(sep, maxsplit)[0] + """)) + self.mod2.write(dedent("""\ + from mod import Base + + result = Base('Beautiful is better').split() + """)) + self._inline2(self.mod, self.mod.read().index("def split") + 5) + module = types.ModuleType("mod") + exec(self.mod.read(), module.__dict__) + namespace = {} + with patch.dict(sys.modules, {"mod": module}): + exec(self.mod2.read(), namespace) + self.assertEqual("Beautiful", namespace["result"]) + def test_adding_needed_imports_in_the_dest_module_removing_selfs(self): self.mod.write(dedent("""\ import mod2