Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MoveGlobal using string as destination module names #548

Merged
merged 6 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- #607 Remove importing from legacy files with `.pickle` suffix
- #625 Remove support for deprecated ast nodes
- #616, #621 Remove `file` builtins
- #548 Implement MoveGlobal using string as destination module names

# Release 1.6.0

Expand Down
46 changes: 46 additions & 0 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,52 @@ consider we want to move ``attr`` to ``A``. We can do that by renaming
You can move the definition of ``attr`` manually.


Moving Global Classes/Functions/Variables
-----------------------------------------

You can move global classes/function/variables to another module by using the
Move refactoring on a global object:

For instance, in this refactoring, if you are moving ``twice()`` to
``pkg1.mod2``:

.. code-block:: python

# pkg1/mod1.py
def twice(a):
return a * 2

print(twice(4))

.. code-block:: python

# pkg1/mod3.py
import pkg1.mod1
pkg1.mod1.twice(13)


When asked for the destination module, put in ``pkg1.mod2``. Rope will update
all the imports.

.. code-block:: python

# pkg1/mod1.py
import pkg1.mod2
print(pkg1.mod2.twice(4))

.. code-block:: python

# pkg1/mod2.py
def twice(a):
return a * 2

.. code-block:: python

# pkg1/mod3.py
import pkg1.mod2
pkg1.mod2.twice(13)


Extract Method
--------------

Expand Down
3 changes: 2 additions & 1 deletion rope/base/project.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import sys
import warnings
from typing import Optional

import rope.base.fscommands # Use full qualification for clarity.
from rope.base import (
Expand Down Expand Up @@ -141,7 +142,7 @@ def get_relative_module(self, name, folder, level):
raise ModuleNotFoundError("Module %s not found" % name)
return self.pycore.resource_to_pyobject(module)

def find_module(self, modname, folder=None):
def find_module(self, modname, folder=None) -> Optional[File]:
"""Returns a resource corresponding to the given module

returns None if it can not be found
Expand Down
31 changes: 30 additions & 1 deletion rope/refactor/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
based on inputs.

"""
from __future__ import annotations

import typing
from typing import Union

from rope.base import (
codeanalyze,
evaluate,
Expand All @@ -21,6 +26,13 @@
)


if typing.TYPE_CHECKING:
from rope.base import (
project,
resources,
)


def create_move(project, resource, offset=None):
"""A factory for creating Move objects

Expand Down Expand Up @@ -231,6 +243,8 @@ def _is_host_used(self):
class MoveGlobal:
"""For moving global function and classes"""

project: project.Project

def __init__(self, project, resource, offset):
self.project = project
this_pymodule = self.project.get_pymodule(resource)
Expand Down Expand Up @@ -299,8 +313,23 @@ def _is_variable(self, pyname):
return isinstance(pyname, pynames.AssignedName)

def get_changes(
self, dest, resources=None, task_handle=taskhandle.NullTaskHandle()
self,
dest: Union[None, str, resources.Resource],
resources=None,
task_handle=taskhandle.NullTaskHandle(),
):
"""Return the changes needed for this refactoring

Parameters:

- `dest`: the Resource or dotted moddule name of the move destination
- `resources` can be a list of `rope.base.resources.File` to
apply this refactoring on. If `None`, the restructuring
will be applied to all python files.

"""
if isinstance(dest, str):
dest = self.project.find_module(dest)
if resources is None:
resources = self.project.get_python_files()
if dest is None or not dest.exists():
Expand Down
12 changes: 12 additions & 0 deletions ropetest/refactor/movetest.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ def test_move_constant_2(self):
self.assertEqual("bar = 321\n", self.mod1.read())
self.assertEqual("foo = 123\n", self.mod2.read())

def test_move_target_is_module_name(self):
self.mod1.write("foo = 123\n")
self._move(self.mod1, self.mod1.read().index("foo") + 1, "mod2")
self.assertEqual("", self.mod1.read())
self.assertEqual("foo = 123\n", self.mod2.read())

def test_move_target_is_package_name(self):
self.mod1.write("foo = 123\n")
self._move(self.mod1, self.mod1.read().index("foo") + 1, "pkg.mod4")
self.assertEqual("", self.mod1.read())
self.assertEqual("foo = 123\n", self.mod4.read())

def test_move_constant_multiline(self):
self.mod1.write(dedent("""\
foo = (
Expand Down