init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
845
.venv/lib/python3.8/site-packages/rope/refactor/move.py
Normal file
845
.venv/lib/python3.8/site-packages/rope/refactor/move.py
Normal file
|
|
@ -0,0 +1,845 @@
|
|||
"""A module containing classes for move refactoring
|
||||
|
||||
`create_move()` is a factory for creating move refactoring objects
|
||||
based on inputs.
|
||||
|
||||
"""
|
||||
from rope.base import (
|
||||
pyobjects,
|
||||
codeanalyze,
|
||||
exceptions,
|
||||
pynames,
|
||||
taskhandle,
|
||||
evaluate,
|
||||
worder,
|
||||
libutils,
|
||||
)
|
||||
from rope.base.change import ChangeSet, ChangeContents, MoveResource
|
||||
from rope.refactor import importutils, rename, occurrences, sourceutils, functionutils
|
||||
|
||||
|
||||
def create_move(project, resource, offset=None):
|
||||
"""A factory for creating Move objects
|
||||
|
||||
Based on `resource` and `offset`, return one of `MoveModule`,
|
||||
`MoveGlobal` or `MoveMethod` for performing move refactoring.
|
||||
|
||||
"""
|
||||
if offset is None:
|
||||
return MoveModule(project, resource)
|
||||
this_pymodule = project.get_pymodule(resource)
|
||||
pyname = evaluate.eval_location(this_pymodule, offset)
|
||||
if pyname is not None:
|
||||
pyobject = pyname.get_object()
|
||||
if isinstance(pyobject, pyobjects.PyModule) or isinstance(
|
||||
pyobject, pyobjects.PyPackage
|
||||
):
|
||||
return MoveModule(project, pyobject.get_resource())
|
||||
if isinstance(pyobject, pyobjects.PyFunction) and isinstance(
|
||||
pyobject.parent, pyobjects.PyClass
|
||||
):
|
||||
return MoveMethod(project, resource, offset)
|
||||
if (
|
||||
isinstance(pyobject, pyobjects.PyDefinedObject)
|
||||
and isinstance(pyobject.parent, pyobjects.PyModule)
|
||||
or isinstance(pyname, pynames.AssignedName)
|
||||
):
|
||||
return MoveGlobal(project, resource, offset)
|
||||
raise exceptions.RefactoringError(
|
||||
"Move only works on global classes/functions/variables, modules and " "methods."
|
||||
)
|
||||
|
||||
|
||||
class MoveMethod(object):
|
||||
"""For moving methods
|
||||
|
||||
It makes a new method in the destination class and changes
|
||||
the body of the old method to call the new method. You can
|
||||
inline the old method to change all of its occurrences.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project, resource, offset):
|
||||
self.project = project
|
||||
this_pymodule = self.project.get_pymodule(resource)
|
||||
pyname = evaluate.eval_location(this_pymodule, offset)
|
||||
self.method_name = worder.get_name_at(resource, offset)
|
||||
self.pyfunction = pyname.get_object()
|
||||
if self.pyfunction.get_kind() != "method":
|
||||
raise exceptions.RefactoringError("Only normal methods" " can be moved.")
|
||||
|
||||
def get_changes(
|
||||
self,
|
||||
dest_attr,
|
||||
new_name=None,
|
||||
resources=None,
|
||||
task_handle=taskhandle.NullTaskHandle(),
|
||||
):
|
||||
"""Return the changes needed for this refactoring
|
||||
|
||||
Parameters:
|
||||
|
||||
- `dest_attr`: the name of the destination attribute
|
||||
- `new_name`: the name of the new method; if `None` uses
|
||||
the old name
|
||||
- `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.
|
||||
|
||||
"""
|
||||
changes = ChangeSet("Moving method <%s>" % self.method_name)
|
||||
if resources is None:
|
||||
resources = self.project.get_python_files()
|
||||
if new_name is None:
|
||||
new_name = self.get_method_name()
|
||||
resource1, start1, end1, new_content1 = self._get_changes_made_by_old_class(
|
||||
dest_attr, new_name
|
||||
)
|
||||
collector1 = codeanalyze.ChangeCollector(resource1.read())
|
||||
collector1.add_change(start1, end1, new_content1)
|
||||
|
||||
resource2, start2, end2, new_content2 = self._get_changes_made_by_new_class(
|
||||
dest_attr, new_name
|
||||
)
|
||||
if resource1 == resource2:
|
||||
collector1.add_change(start2, end2, new_content2)
|
||||
else:
|
||||
collector2 = codeanalyze.ChangeCollector(resource2.read())
|
||||
collector2.add_change(start2, end2, new_content2)
|
||||
result = collector2.get_changed()
|
||||
import_tools = importutils.ImportTools(self.project)
|
||||
new_imports = self._get_used_imports(import_tools)
|
||||
if new_imports:
|
||||
goal_pymodule = libutils.get_string_module(
|
||||
self.project, result, resource2
|
||||
)
|
||||
result = _add_imports_to_module(
|
||||
import_tools, goal_pymodule, new_imports
|
||||
)
|
||||
if resource2 in resources:
|
||||
changes.add_change(ChangeContents(resource2, result))
|
||||
|
||||
if resource1 in resources:
|
||||
changes.add_change(ChangeContents(resource1, collector1.get_changed()))
|
||||
return changes
|
||||
|
||||
def get_method_name(self):
|
||||
return self.method_name
|
||||
|
||||
def _get_used_imports(self, import_tools):
|
||||
return importutils.get_imports(self.project, self.pyfunction)
|
||||
|
||||
def _get_changes_made_by_old_class(self, dest_attr, new_name):
|
||||
pymodule = self.pyfunction.get_module()
|
||||
indents = self._get_scope_indents(self.pyfunction)
|
||||
body = "return self.%s.%s(%s)\n" % (
|
||||
dest_attr,
|
||||
new_name,
|
||||
self._get_passed_arguments_string(),
|
||||
)
|
||||
region = sourceutils.get_body_region(self.pyfunction)
|
||||
return (
|
||||
pymodule.get_resource(),
|
||||
region[0],
|
||||
region[1],
|
||||
sourceutils.fix_indentation(body, indents),
|
||||
)
|
||||
|
||||
def _get_scope_indents(self, pyobject):
|
||||
pymodule = pyobject.get_module()
|
||||
return sourceutils.get_indents(
|
||||
pymodule.lines, pyobject.get_scope().get_start()
|
||||
) + sourceutils.get_indent(self.project)
|
||||
|
||||
def _get_changes_made_by_new_class(self, dest_attr, new_name):
|
||||
old_pyclass = self.pyfunction.parent
|
||||
if dest_attr not in old_pyclass:
|
||||
raise exceptions.RefactoringError(
|
||||
"Destination attribute <%s> not found" % dest_attr
|
||||
)
|
||||
pyclass = old_pyclass[dest_attr].get_object().get_type()
|
||||
if not isinstance(pyclass, pyobjects.PyClass):
|
||||
raise exceptions.RefactoringError(
|
||||
"Unknown class type for attribute <%s>" % dest_attr
|
||||
)
|
||||
pymodule = pyclass.get_module()
|
||||
resource = pyclass.get_module().get_resource()
|
||||
start, end = sourceutils.get_body_region(pyclass)
|
||||
pre_blanks = "\n"
|
||||
if pymodule.source_code[start:end].strip() != "pass":
|
||||
pre_blanks = "\n\n"
|
||||
start = end
|
||||
indents = self._get_scope_indents(pyclass)
|
||||
body = pre_blanks + sourceutils.fix_indentation(
|
||||
self.get_new_method(new_name), indents
|
||||
)
|
||||
return resource, start, end, body
|
||||
|
||||
def get_new_method(self, name):
|
||||
return "%s\n%s" % (
|
||||
self._get_new_header(name),
|
||||
sourceutils.fix_indentation(
|
||||
self._get_body(), sourceutils.get_indent(self.project)
|
||||
),
|
||||
)
|
||||
|
||||
def _get_unchanged_body(self):
|
||||
return sourceutils.get_body(self.pyfunction)
|
||||
|
||||
def _get_body(self, host="host"):
|
||||
self_name = self._get_self_name()
|
||||
body = self_name + " = None\n" + self._get_unchanged_body()
|
||||
pymodule = libutils.get_string_module(self.project, body)
|
||||
finder = occurrences.create_finder(self.project, self_name, pymodule[self_name])
|
||||
result = rename.rename_in_module(finder, host, pymodule=pymodule)
|
||||
if result is None:
|
||||
result = body
|
||||
return result[result.index("\n") + 1 :]
|
||||
|
||||
def _get_self_name(self):
|
||||
return self.pyfunction.get_param_names()[0]
|
||||
|
||||
def _get_new_header(self, name):
|
||||
header = "def %s(self" % name
|
||||
if self._is_host_used():
|
||||
header += ", host"
|
||||
definition_info = functionutils.DefinitionInfo.read(self.pyfunction)
|
||||
others = definition_info.arguments_to_string(1)
|
||||
if others:
|
||||
header += ", " + others
|
||||
return header + "):"
|
||||
|
||||
def _get_passed_arguments_string(self):
|
||||
result = ""
|
||||
if self._is_host_used():
|
||||
result = "self"
|
||||
definition_info = functionutils.DefinitionInfo.read(self.pyfunction)
|
||||
others = definition_info.arguments_to_string(1)
|
||||
if others:
|
||||
if result:
|
||||
result += ", "
|
||||
result += others
|
||||
return result
|
||||
|
||||
def _is_host_used(self):
|
||||
return self._get_body("__old_self") != self._get_unchanged_body()
|
||||
|
||||
|
||||
class MoveGlobal(object):
|
||||
"""For moving global function and classes"""
|
||||
|
||||
def __init__(self, project, resource, offset):
|
||||
self.project = project
|
||||
this_pymodule = self.project.get_pymodule(resource)
|
||||
self.old_pyname = evaluate.eval_location(this_pymodule, offset)
|
||||
if self.old_pyname is None:
|
||||
raise exceptions.RefactoringError(
|
||||
"Move refactoring should be performed on a class/function/variable."
|
||||
)
|
||||
if self._is_variable(self.old_pyname):
|
||||
self.old_name = worder.get_name_at(resource, offset)
|
||||
pymodule = this_pymodule
|
||||
else:
|
||||
self.old_name = self.old_pyname.get_object().get_name()
|
||||
pymodule = self.old_pyname.get_object().get_module()
|
||||
self._check_exceptional_conditions()
|
||||
self.source = pymodule.get_resource()
|
||||
self.tools = _MoveTools(
|
||||
self.project, self.source, self.old_pyname, self.old_name
|
||||
)
|
||||
self.import_tools = self.tools.import_tools
|
||||
|
||||
def _import_filter(self, stmt):
|
||||
module_name = libutils.modname(self.source)
|
||||
|
||||
if isinstance(stmt.import_info, importutils.NormalImport):
|
||||
# Affect any statement that imports the source module
|
||||
return any(
|
||||
module_name == name
|
||||
for name, alias in stmt.import_info.names_and_aliases
|
||||
)
|
||||
elif isinstance(stmt.import_info, importutils.FromImport):
|
||||
# Affect statements importing from the source package
|
||||
if "." in module_name:
|
||||
package_name, basename = module_name.rsplit(".", 1)
|
||||
if stmt.import_info.module_name == package_name and any(
|
||||
basename == name
|
||||
for name, alias in stmt.import_info.names_and_aliases
|
||||
):
|
||||
return True
|
||||
return stmt.import_info.module_name == module_name
|
||||
return False
|
||||
|
||||
def _check_exceptional_conditions(self):
|
||||
if self._is_variable(self.old_pyname):
|
||||
pymodule = self.old_pyname.get_definition_location()[0]
|
||||
try:
|
||||
pymodule.get_scope().get_name(self.old_name)
|
||||
except exceptions.NameNotFoundError:
|
||||
self._raise_refactoring_error()
|
||||
elif not (
|
||||
isinstance(self.old_pyname.get_object(), pyobjects.PyDefinedObject)
|
||||
and self._is_global(self.old_pyname.get_object())
|
||||
):
|
||||
self._raise_refactoring_error()
|
||||
|
||||
def _raise_refactoring_error(self):
|
||||
raise exceptions.RefactoringError(
|
||||
"Move refactoring should be performed on a global class, function "
|
||||
"or variable."
|
||||
)
|
||||
|
||||
def _is_global(self, pyobject):
|
||||
return pyobject.get_scope().parent == pyobject.get_module().get_scope()
|
||||
|
||||
def _is_variable(self, pyname):
|
||||
return isinstance(pyname, pynames.AssignedName)
|
||||
|
||||
def get_changes(
|
||||
self, dest, resources=None, task_handle=taskhandle.NullTaskHandle()
|
||||
):
|
||||
if resources is None:
|
||||
resources = self.project.get_python_files()
|
||||
if dest is None or not dest.exists():
|
||||
raise exceptions.RefactoringError("Move destination does not exist.")
|
||||
if dest.is_folder() and dest.has_child("__init__.py"):
|
||||
dest = dest.get_child("__init__.py")
|
||||
if dest.is_folder():
|
||||
raise exceptions.RefactoringError(
|
||||
"Move destination for non-modules should not be folders."
|
||||
)
|
||||
if self.source == dest:
|
||||
raise exceptions.RefactoringError(
|
||||
"Moving global elements to the same module."
|
||||
)
|
||||
return self._calculate_changes(dest, resources, task_handle)
|
||||
|
||||
def _calculate_changes(self, dest, resources, task_handle):
|
||||
changes = ChangeSet("Moving global <%s>" % self.old_name)
|
||||
job_set = task_handle.create_jobset("Collecting Changes", len(resources))
|
||||
for file_ in resources:
|
||||
job_set.started_job(file_.path)
|
||||
if file_ == self.source:
|
||||
changes.add_change(self._source_module_changes(dest))
|
||||
elif file_ == dest:
|
||||
changes.add_change(self._dest_module_changes(dest))
|
||||
elif self.tools.occurs_in_module(resource=file_):
|
||||
pymodule = self.project.get_pymodule(file_)
|
||||
# Changing occurrences
|
||||
placeholder = "__rope_renaming_%s_" % self.old_name
|
||||
source = self.tools.rename_in_module(placeholder, resource=file_)
|
||||
should_import = source is not None
|
||||
# Removing out of date imports
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
source = self.import_tools.organize_imports(
|
||||
pymodule, sort=False, import_filter=self._import_filter
|
||||
)
|
||||
# Adding new import
|
||||
if should_import:
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
source, imported = importutils.add_import(
|
||||
self.project, pymodule, self._new_modname(dest), self.old_name
|
||||
)
|
||||
source = source.replace(placeholder, imported)
|
||||
source = self.tools.new_source(pymodule, source)
|
||||
if source != file_.read():
|
||||
changes.add_change(ChangeContents(file_, source))
|
||||
job_set.finished_job()
|
||||
return changes
|
||||
|
||||
def _source_module_changes(self, dest):
|
||||
placeholder = "__rope_moving_%s_" % self.old_name
|
||||
handle = _ChangeMoveOccurrencesHandle(placeholder)
|
||||
occurrence_finder = occurrences.create_finder(
|
||||
self.project, self.old_name, self.old_pyname
|
||||
)
|
||||
start, end = self._get_moving_region()
|
||||
renamer = ModuleSkipRenamer(occurrence_finder, self.source, handle, start, end)
|
||||
source = renamer.get_changed_module()
|
||||
pymodule = libutils.get_string_module(self.project, source, self.source)
|
||||
source = self.import_tools.organize_imports(pymodule, sort=False)
|
||||
if handle.occurred:
|
||||
pymodule = libutils.get_string_module(self.project, source, self.source)
|
||||
# Adding new import
|
||||
source, imported = importutils.add_import(
|
||||
self.project, pymodule, self._new_modname(dest), self.old_name
|
||||
)
|
||||
source = source.replace(placeholder, imported)
|
||||
return ChangeContents(self.source, source)
|
||||
|
||||
def _new_modname(self, dest):
|
||||
return libutils.modname(dest)
|
||||
|
||||
def _dest_module_changes(self, dest):
|
||||
# Changing occurrences
|
||||
pymodule = self.project.get_pymodule(dest)
|
||||
source = self.tools.rename_in_module(self.old_name, pymodule)
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
|
||||
moving, imports = self._get_moving_element_with_imports()
|
||||
pymodule, has_changed = self._add_imports2(pymodule, imports)
|
||||
|
||||
module_with_imports = self.import_tools.module_imports(pymodule)
|
||||
source = pymodule.source_code
|
||||
lineno = 0
|
||||
if module_with_imports.imports:
|
||||
lineno = module_with_imports.imports[-1].end_line - 1
|
||||
else:
|
||||
while lineno < pymodule.lines.length() and pymodule.lines.get_line(
|
||||
lineno + 1
|
||||
).lstrip().startswith("#"):
|
||||
lineno += 1
|
||||
if lineno > 0:
|
||||
cut = pymodule.lines.get_line_end(lineno) + 1
|
||||
result = source[:cut] + "\n\n" + moving + source[cut:]
|
||||
else:
|
||||
result = moving + source
|
||||
|
||||
# Organizing imports
|
||||
source = result
|
||||
pymodule = libutils.get_string_module(self.project, source, dest)
|
||||
source = self.import_tools.organize_imports(pymodule, sort=False, unused=False)
|
||||
# Remove unused imports of the old module
|
||||
pymodule = libutils.get_string_module(self.project, source, dest)
|
||||
source = self.import_tools.organize_imports(
|
||||
pymodule,
|
||||
sort=False,
|
||||
selfs=False,
|
||||
unused=True,
|
||||
import_filter=self._import_filter,
|
||||
)
|
||||
return ChangeContents(dest, source)
|
||||
|
||||
def _get_moving_element_with_imports(self):
|
||||
return moving_code_with_imports(
|
||||
self.project, self.source, self._get_moving_element()
|
||||
)
|
||||
|
||||
def _get_module_with_imports(self, source_code, resource):
|
||||
pymodule = libutils.get_string_module(self.project, source_code, resource)
|
||||
return self.import_tools.module_imports(pymodule)
|
||||
|
||||
def _get_moving_element(self):
|
||||
start, end = self._get_moving_region()
|
||||
moving = self.source.read()[start:end]
|
||||
return moving.rstrip() + "\n"
|
||||
|
||||
def _get_moving_region(self):
|
||||
pymodule = self.project.get_pymodule(self.source)
|
||||
lines = pymodule.lines
|
||||
if self._is_variable(self.old_pyname):
|
||||
logical_lines = pymodule.logical_lines
|
||||
lineno = logical_lines.logical_line_in(
|
||||
self.old_pyname.get_definition_location()[1]
|
||||
)[0]
|
||||
start = lines.get_line_start(lineno)
|
||||
end_line = logical_lines.logical_line_in(lineno)[1]
|
||||
else:
|
||||
scope = self.old_pyname.get_object().get_scope()
|
||||
start = lines.get_line_start(scope.get_start())
|
||||
end_line = scope.get_end()
|
||||
|
||||
# Include comment lines before the definition
|
||||
start_line = lines.get_line_number(start)
|
||||
while start_line > 1 and lines.get_line(start_line - 1).startswith("#"):
|
||||
start_line -= 1
|
||||
start = lines.get_line_start(start_line)
|
||||
|
||||
while end_line < lines.length() and lines.get_line(end_line + 1).strip() == "":
|
||||
end_line += 1
|
||||
end = min(lines.get_line_end(end_line) + 1, len(pymodule.source_code))
|
||||
return start, end
|
||||
|
||||
def _add_imports2(self, pymodule, new_imports):
|
||||
source = self.tools.add_imports(pymodule, new_imports)
|
||||
if source is None:
|
||||
return pymodule, False
|
||||
else:
|
||||
resource = pymodule.get_resource()
|
||||
pymodule = libutils.get_string_module(self.project, source, resource)
|
||||
return pymodule, True
|
||||
|
||||
|
||||
class MoveModule(object):
|
||||
"""For moving modules and packages"""
|
||||
|
||||
def __init__(self, project, resource):
|
||||
self.project = project
|
||||
if not resource.is_folder() and resource.name == "__init__.py":
|
||||
resource = resource.parent
|
||||
if resource.is_folder() and not resource.has_child("__init__.py"):
|
||||
raise exceptions.RefactoringError("Cannot move non-package folder.")
|
||||
dummy_pymodule = libutils.get_string_module(self.project, "")
|
||||
self.old_pyname = pynames.ImportedModule(dummy_pymodule, resource=resource)
|
||||
self.source = self.old_pyname.get_object().get_resource()
|
||||
if self.source.is_folder():
|
||||
self.old_name = self.source.name
|
||||
else:
|
||||
self.old_name = self.source.name[:-3]
|
||||
self.tools = _MoveTools(
|
||||
self.project, self.source, self.old_pyname, self.old_name
|
||||
)
|
||||
self.import_tools = self.tools.import_tools
|
||||
|
||||
def get_changes(
|
||||
self, dest, resources=None, task_handle=taskhandle.NullTaskHandle()
|
||||
):
|
||||
if resources is None:
|
||||
resources = self.project.get_python_files()
|
||||
if dest is None or not dest.is_folder():
|
||||
raise exceptions.RefactoringError(
|
||||
"Move destination for modules should be packages."
|
||||
)
|
||||
return self._calculate_changes(dest, resources, task_handle)
|
||||
|
||||
def _calculate_changes(self, dest, resources, task_handle):
|
||||
changes = ChangeSet("Moving module <%s>" % self.old_name)
|
||||
job_set = task_handle.create_jobset("Collecting changes", len(resources))
|
||||
for module in resources:
|
||||
job_set.started_job(module.path)
|
||||
if module == self.source:
|
||||
self._change_moving_module(changes, dest)
|
||||
else:
|
||||
source = self._change_occurrences_in_module(dest, resource=module)
|
||||
if source is not None:
|
||||
changes.add_change(ChangeContents(module, source))
|
||||
job_set.finished_job()
|
||||
if self.project == self.source.project:
|
||||
changes.add_change(MoveResource(self.source, dest.path))
|
||||
return changes
|
||||
|
||||
def _new_modname(self, dest):
|
||||
destname = libutils.modname(dest)
|
||||
if destname:
|
||||
return destname + "." + self.old_name
|
||||
return self.old_name
|
||||
|
||||
def _new_import(self, dest):
|
||||
return importutils.NormalImport([(self._new_modname(dest), None)])
|
||||
|
||||
def _change_moving_module(self, changes, dest):
|
||||
if not self.source.is_folder():
|
||||
pymodule = self.project.get_pymodule(self.source)
|
||||
source = self.import_tools.relatives_to_absolutes(pymodule)
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
source = self._change_occurrences_in_module(dest, pymodule)
|
||||
source = self.tools.new_source(pymodule, source)
|
||||
if source != self.source.read():
|
||||
changes.add_change(ChangeContents(self.source, source))
|
||||
|
||||
def _change_occurrences_in_module(self, dest, pymodule=None, resource=None):
|
||||
if not self.tools.occurs_in_module(pymodule=pymodule, resource=resource):
|
||||
return
|
||||
if pymodule is None:
|
||||
pymodule = self.project.get_pymodule(resource)
|
||||
new_name = self._new_modname(dest)
|
||||
module_imports = importutils.get_module_imports(self.project, pymodule)
|
||||
changed = False
|
||||
source = None
|
||||
if libutils.modname(dest):
|
||||
changed = self._change_import_statements(dest, new_name, module_imports)
|
||||
if changed:
|
||||
source = module_imports.get_changed_source()
|
||||
source = self.tools.new_source(pymodule, source)
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
|
||||
new_import = self._new_import(dest)
|
||||
source = self.tools.rename_in_module(
|
||||
new_name,
|
||||
imports=True,
|
||||
pymodule=pymodule,
|
||||
resource=resource if not changed else None,
|
||||
)
|
||||
should_import = self.tools.occurs_in_module(
|
||||
pymodule=pymodule, resource=resource, imports=False
|
||||
)
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
source = self.tools.remove_old_imports(pymodule)
|
||||
if should_import:
|
||||
pymodule = self.tools.new_pymodule(pymodule, source)
|
||||
source = self.tools.add_imports(pymodule, [new_import])
|
||||
source = self.tools.new_source(pymodule, source)
|
||||
if source is not None and source != pymodule.resource.read():
|
||||
return source
|
||||
return None
|
||||
|
||||
def _change_import_statements(self, dest, new_name, module_imports):
|
||||
moving_module = self.source
|
||||
parent_module = moving_module.parent
|
||||
|
||||
changed = False
|
||||
for import_stmt in module_imports.imports:
|
||||
if not any(
|
||||
name_and_alias[0] == self.old_name
|
||||
for name_and_alias in import_stmt.import_info.names_and_aliases
|
||||
) and not any(
|
||||
name_and_alias[0] == libutils.modname(self.source)
|
||||
for name_and_alias in import_stmt.import_info.names_and_aliases
|
||||
):
|
||||
continue
|
||||
|
||||
# Case 1: Look for normal imports of the moving module.
|
||||
if isinstance(import_stmt.import_info, importutils.NormalImport):
|
||||
continue
|
||||
|
||||
# Case 2: The moving module is from-imported.
|
||||
changed = (
|
||||
self._handle_moving_in_from_import_stmt(
|
||||
dest, import_stmt, module_imports, parent_module
|
||||
)
|
||||
or changed
|
||||
)
|
||||
|
||||
# Case 3: Names are imported from the moving module.
|
||||
context = importutils.importinfo.ImportContext(self.project, None)
|
||||
if (
|
||||
not import_stmt.import_info.is_empty()
|
||||
and import_stmt.import_info.get_imported_resource(context)
|
||||
== moving_module
|
||||
):
|
||||
import_stmt.import_info = importutils.FromImport(
|
||||
new_name,
|
||||
import_stmt.import_info.level,
|
||||
import_stmt.import_info.names_and_aliases,
|
||||
)
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
def _handle_moving_in_from_import_stmt(
|
||||
self, dest, import_stmt, module_imports, parent_module
|
||||
):
|
||||
changed = False
|
||||
context = importutils.importinfo.ImportContext(self.project, None)
|
||||
if import_stmt.import_info.get_imported_resource(context) == parent_module:
|
||||
imports = import_stmt.import_info.names_and_aliases
|
||||
new_imports = []
|
||||
for name, alias in imports:
|
||||
# The moving module was imported.
|
||||
if name == self.old_name:
|
||||
changed = True
|
||||
new_import = importutils.FromImport(
|
||||
libutils.modname(dest), 0, [(self.old_name, alias)]
|
||||
)
|
||||
module_imports.add_import(new_import)
|
||||
else:
|
||||
new_imports.append((name, alias))
|
||||
|
||||
# Update the imports if the imported names were changed.
|
||||
if new_imports != imports:
|
||||
changed = True
|
||||
if new_imports:
|
||||
import_stmt.import_info = importutils.FromImport(
|
||||
import_stmt.import_info.module_name,
|
||||
import_stmt.import_info.level,
|
||||
new_imports,
|
||||
)
|
||||
else:
|
||||
import_stmt.empty_import()
|
||||
return changed
|
||||
|
||||
|
||||
class _ChangeMoveOccurrencesHandle(object):
|
||||
def __init__(self, new_name):
|
||||
self.new_name = new_name
|
||||
self.occurred = False
|
||||
|
||||
def occurred_inside_skip(self, change_collector, occurrence):
|
||||
pass
|
||||
|
||||
def occurred_outside_skip(self, change_collector, occurrence):
|
||||
start, end = occurrence.get_primary_range()
|
||||
change_collector.add_change(start, end, self.new_name)
|
||||
self.occurred = True
|
||||
|
||||
|
||||
class _MoveTools(object):
|
||||
def __init__(self, project, source, pyname, old_name):
|
||||
self.project = project
|
||||
self.source = source
|
||||
self.old_pyname = pyname
|
||||
self.old_name = old_name
|
||||
self.import_tools = importutils.ImportTools(self.project)
|
||||
|
||||
def remove_old_imports(self, pymodule):
|
||||
old_source = pymodule.source_code
|
||||
module_with_imports = self.import_tools.module_imports(pymodule)
|
||||
|
||||
class CanSelect(object):
|
||||
changed = False
|
||||
old_name = self.old_name
|
||||
old_pyname = self.old_pyname
|
||||
|
||||
def __call__(self, name):
|
||||
try:
|
||||
if (
|
||||
name == self.old_name
|
||||
and pymodule[name].get_object() == self.old_pyname.get_object()
|
||||
):
|
||||
self.changed = True
|
||||
return False
|
||||
except exceptions.AttributeNotFoundError:
|
||||
pass
|
||||
return True
|
||||
|
||||
can_select = CanSelect()
|
||||
module_with_imports.filter_names(can_select)
|
||||
new_source = module_with_imports.get_changed_source()
|
||||
if old_source != new_source:
|
||||
return new_source
|
||||
|
||||
def rename_in_module(self, new_name, pymodule=None, imports=False, resource=None):
|
||||
occurrence_finder = self._create_finder(imports)
|
||||
source = rename.rename_in_module(
|
||||
occurrence_finder,
|
||||
new_name,
|
||||
replace_primary=True,
|
||||
pymodule=pymodule,
|
||||
resource=resource,
|
||||
)
|
||||
return source
|
||||
|
||||
def occurs_in_module(self, pymodule=None, resource=None, imports=True):
|
||||
finder = self._create_finder(imports)
|
||||
for occurrence in finder.find_occurrences(pymodule=pymodule, resource=resource):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_finder(self, imports):
|
||||
return occurrences.create_finder(
|
||||
self.project,
|
||||
self.old_name,
|
||||
self.old_pyname,
|
||||
imports=imports,
|
||||
keywords=False,
|
||||
)
|
||||
|
||||
def new_pymodule(self, pymodule, source):
|
||||
if source is not None:
|
||||
return libutils.get_string_module(
|
||||
self.project, source, pymodule.get_resource()
|
||||
)
|
||||
return pymodule
|
||||
|
||||
def new_source(self, pymodule, source):
|
||||
if source is None:
|
||||
return pymodule.source_code
|
||||
return source
|
||||
|
||||
def add_imports(self, pymodule, new_imports):
|
||||
return _add_imports_to_module(self.import_tools, pymodule, new_imports)
|
||||
|
||||
|
||||
def _add_imports_to_module(import_tools, pymodule, new_imports):
|
||||
module_with_imports = import_tools.module_imports(pymodule)
|
||||
for new_import in new_imports:
|
||||
module_with_imports.add_import(new_import)
|
||||
return module_with_imports.get_changed_source()
|
||||
|
||||
|
||||
def moving_code_with_imports(project, resource, source):
|
||||
import_tools = importutils.ImportTools(project)
|
||||
pymodule = libutils.get_string_module(project, source, resource)
|
||||
|
||||
# Strip comment prefix, if any. These need to stay before the moving
|
||||
# section, but imports would be added between them.
|
||||
lines = codeanalyze.SourceLinesAdapter(source)
|
||||
start = 1
|
||||
while start < lines.length() and lines.get_line(start).startswith("#"):
|
||||
start += 1
|
||||
moving_prefix = source[: lines.get_line_start(start)]
|
||||
pymodule = libutils.get_string_module(
|
||||
project, source[lines.get_line_start(start) :], resource
|
||||
)
|
||||
|
||||
origin = project.get_pymodule(resource)
|
||||
|
||||
imports = []
|
||||
for stmt in import_tools.module_imports(origin).imports:
|
||||
imports.append(stmt.import_info)
|
||||
|
||||
back_names = []
|
||||
for name in origin:
|
||||
if name not in pymodule:
|
||||
back_names.append(name)
|
||||
imports.append(import_tools.get_from_import(resource, back_names))
|
||||
|
||||
source = _add_imports_to_module(import_tools, pymodule, imports)
|
||||
pymodule = libutils.get_string_module(project, source, resource)
|
||||
|
||||
source = import_tools.relatives_to_absolutes(pymodule)
|
||||
pymodule = libutils.get_string_module(project, source, resource)
|
||||
source = import_tools.organize_imports(pymodule, selfs=False)
|
||||
pymodule = libutils.get_string_module(project, source, resource)
|
||||
|
||||
# extracting imports after changes
|
||||
module_imports = import_tools.module_imports(pymodule)
|
||||
imports = [import_stmt.import_info for import_stmt in module_imports.imports]
|
||||
start = 1
|
||||
if module_imports.imports:
|
||||
start = module_imports.imports[-1].end_line
|
||||
lines = codeanalyze.SourceLinesAdapter(source)
|
||||
while start < lines.length() and not lines.get_line(start).strip():
|
||||
start += 1
|
||||
|
||||
# Reinsert the prefix which was removed at the beginning
|
||||
moving = moving_prefix + source[lines.get_line_start(start) :]
|
||||
return moving, imports
|
||||
|
||||
|
||||
class ModuleSkipRenamerHandle(object):
|
||||
def occurred_outside_skip(self, change_collector, occurrence):
|
||||
pass
|
||||
|
||||
def occurred_inside_skip(self, change_collector, occurrence):
|
||||
pass
|
||||
|
||||
|
||||
class ModuleSkipRenamer(object):
|
||||
"""Rename occurrences in a module
|
||||
|
||||
This class can be used when you want to treat a region in a file
|
||||
separately from other parts when renaming.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
occurrence_finder,
|
||||
resource,
|
||||
handle=None,
|
||||
skip_start=0,
|
||||
skip_end=0,
|
||||
replacement="",
|
||||
):
|
||||
"""Constructor
|
||||
|
||||
if replacement is `None` the region is not changed. Otherwise
|
||||
it is replaced with `replacement`.
|
||||
|
||||
"""
|
||||
self.occurrence_finder = occurrence_finder
|
||||
self.resource = resource
|
||||
self.skip_start = skip_start
|
||||
self.skip_end = skip_end
|
||||
self.replacement = replacement
|
||||
self.handle = handle
|
||||
if self.handle is None:
|
||||
self.handle = ModuleSkipRenamerHandle()
|
||||
|
||||
def get_changed_module(self):
|
||||
source = self.resource.read()
|
||||
change_collector = codeanalyze.ChangeCollector(source)
|
||||
if self.replacement is not None:
|
||||
change_collector.add_change(
|
||||
self.skip_start, self.skip_end, self.replacement
|
||||
)
|
||||
for occurrence in self.occurrence_finder.find_occurrences(self.resource):
|
||||
start, end = occurrence.get_primary_range()
|
||||
if self.skip_start <= start < self.skip_end:
|
||||
self.handle.occurred_inside_skip(change_collector, occurrence)
|
||||
else:
|
||||
self.handle.occurred_outside_skip(change_collector, occurrence)
|
||||
result = change_collector.get_changed()
|
||||
if result is not None and result != source:
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue