Source

rope_py3k / rope / refactor / change_signature.py

Ali Gholami Rudi e9561b4 

Ali Gholami Rudi 73275ae 
Ali Gholami Rudi db1feee 
Ali Gholami Rudi 6c55b3c 
Ali Gholami Rudi 1713f94 
Ali Gholami Rudi e9561b4 

Ali Gholami Rudi 2bc99f0 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 4a31a93 

Ali Gholami Rudi e9561b4 

Ali Gholami Rudi bf8c09d 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 73275ae 
Ali Gholami Rudi 979e5e3 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi bf8c09d 
Ali Gholami Rudi 2b0e3d5 
Ali Gholami Rudi 9646eb6 
Ali Gholami Rudi 08c437a 
Ali Gholami Rudi 9646eb6 
Ali Gholami Rudi da9d25b 



Ali Gholami Rudi 455d704 
Ali Gholami Rudi 9c75bbe 
Ali Gholami Rudi da9d25b 







Ali Gholami Rudi 9c75bbe 
Ali Gholami Rudi bf8c09d 
Ali Gholami Rudi 0a14a1a 
Ali Gholami Rudi 0142bfb 
Ali Gholami Rudi 0a14a1a 

Ali Gholami Rudi 4a31a93 
Ali Gholami Rudi 0a14a1a 
Ali Gholami Rudi 28e097a 
Ali Gholami Rudi 2691081 
Ali Gholami Rudi 28e097a 
Ali Gholami Rudi da9d25b 

Ali Gholami Rudi f379721 
Ali Gholami Rudi 2691081 
Ali Gholami Rudi 465aec3 
Ali Gholami Rudi 0a14a1a 
Ali Gholami Rudi 16a6a06 
Ali Gholami Rudi e9561b4 




Ali Gholami Rudi 0142bfb 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi b8f3b8b 








Ali Gholami Rudi 73275ae 



Ali Gholami Rudi b8f3b8b 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi b8f3b8b 


Ali Gholami Rudi 22696ee 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi d9e0df5 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 0142bfb 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi d9e0df5 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 0142bfb 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi d9e0df5 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 0142bfb 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi d9e0df5 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 0142bfb 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi d9e0df5 
Ali Gholami Rudi df22086 
Ali Gholami Rudi 0142bfb 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi df22086 
Ali Gholami Rudi 0a14a1a 
Ali Gholami Rudi f44ad86 





Ali Gholami Rudi 0a14a1a 


Ali Gholami Rudi f44ad86 

Ali Gholami Rudi b8f3b8b 

Ali Gholami Rudi bc520c6 
Ali Gholami Rudi 0a14a1a 
Ali Gholami Rudi e9561b4 

Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 

Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 








Ali Gholami Rudi 41c4490 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi da9d25b 


Ali Gholami Rudi 22696ee 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 


Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 


Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi e9561b4 










Ali Gholami Rudi 8cb84a4 


Ali Gholami Rudi e9561b4 

Ali Gholami Rudi 41c4490 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 




Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 


Ali Gholami Rudi 979e5e3 
Ali Gholami Rudi 8cb84a4 









Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi bc520c6 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 












Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 1713f94 
Ali Gholami Rudi 77d052b 








Ali Gholami Rudi b6359e8 






Ali Gholami Rudi 77d052b 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 1713f94 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi 8cb84a4 

Ali Gholami Rudi 259c5b8 
Ali Gholami Rudi 8cb84a4 
Ali Gholami Rudi 1713f94 





Ali Gholami Rudi 8cb84a4 


Ali Gholami Rudi e9561b4 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi e9561b4 






Ali Gholami Rudi 687d126 
Ali Gholami Rudi dee5ec0 
Ali Gholami Rudi e9561b4 



Ali Gholami Rudi 97d6da1 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi da9d25b 
Ali Gholami Rudi e9561b4 
Ali Gholami Rudi da9d25b 
Ali Gholami Rudi e9561b4 





Ali Gholami Rudi 41c4490 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi ae5e96e 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi db1feee 
Ali Gholami Rudi 41c4490 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi ae5e96e 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi db1feee 



Ali Gholami Rudi e9561b4 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi ae5e96e 
Ali Gholami Rudi bc520c6 
Ali Gholami Rudi db1feee 

Ali Gholami Rudi 465aec3 









zjes 65846d1 
Ali Gholami Rudi 465aec3 
import copy

import rope.base.exceptions
from rope.base import pyobjects, taskhandle, evaluate, worder, codeanalyze, utils
from rope.base.change import ChangeContents, ChangeSet
from rope.refactor import occurrences, functionutils


class ChangeSignature(object):

    def __init__(self, project, resource, offset):
        self.pycore = project.pycore
        self.resource = resource
        self.offset = offset
        self._set_name_and_pyname()
        if self.pyname is None or self.pyname.get_object() is None or \
           not isinstance(self.pyname.get_object(), pyobjects.PyFunction):
            raise rope.base.exceptions.RefactoringError(
                'Change method signature should be performed on functions')

    def _set_name_and_pyname(self):
        self.name = worder.get_name_at(self.resource, self.offset)
        this_pymodule = self.pycore.resource_to_pyobject(self.resource)
        self.primary, self.pyname = evaluate.eval_location2(
            this_pymodule, self.offset)
        if self.pyname is None:
            return
        pyobject = self.pyname.get_object()
        if isinstance(pyobject, pyobjects.PyClass) and \
           '__init__' in pyobject:
            self.pyname = pyobject['__init__']
            self.name = '__init__'
        pyobject = self.pyname.get_object()
        self.others = None
        if self.name == '__init__' and \
           isinstance(pyobject, pyobjects.PyFunction) and \
           isinstance(pyobject.parent, pyobjects.PyClass):
            pyclass = pyobject.parent
            self.others = (pyclass.get_name(),
                           pyclass.parent[pyclass.get_name()])

    def _change_calls(self, call_changer, in_hierarchy=None, resources=None,
                      handle=taskhandle.NullTaskHandle()):
        if resources is None:
            resources = self.pycore.get_python_files()
        changes = ChangeSet('Changing signature of <%s>' % self.name)
        job_set = handle.create_jobset('Collecting Changes', len(resources))
        finder = occurrences.create_finder(
            self.pycore, self.name, self.pyname, instance=self.primary,
            in_hierarchy=in_hierarchy and self.is_method())
        if self.others:
            name, pyname = self.others
            constructor_finder = occurrences.create_finder(
                self.pycore, name, pyname, only_calls=True)
            finder = _MultipleFinders([finder, constructor_finder])
        for file in resources:
            job_set.started_job(file.path)
            change_calls = _ChangeCallsInModule(
                self.pycore, finder, file, call_changer)
            changed_file = change_calls.get_changed_module()
            if changed_file is not None:
                changes.add_change(ChangeContents(file, changed_file))
            job_set.finished_job()
        return changes

    def get_args(self):
        """Get function arguments.

        Return a list of ``(name, default)`` tuples for all but star
        and double star arguments.  For arguments that don't have a
        default, `None` will be used.
        """
        return self._definfo().args_with_defaults

    def is_method(self):
        pyfunction = self.pyname.get_object()
        return isinstance(pyfunction.parent, pyobjects.PyClass)

    @utils.deprecated('Use `ChangeSignature.get_args()` instead')
    def get_definition_info(self):
        return self._definfo()

    def _definfo(self):
        return functionutils.DefinitionInfo.read(self.pyname.get_object())

    @utils.deprecated()
    def normalize(self):
        changer = _FunctionChangers(
            self.pyname.get_object(), self.get_definition_info(),
            [ArgumentNormalizer()])
        return self._change_calls(changer)

    @utils.deprecated()
    def remove(self, index):
        changer = _FunctionChangers(
            self.pyname.get_object(), self.get_definition_info(),
            [ArgumentRemover(index)])
        return self._change_calls(changer)

    @utils.deprecated()
    def add(self, index, name, default=None, value=None):
        changer = _FunctionChangers(
            self.pyname.get_object(), self.get_definition_info(),
            [ArgumentAdder(index, name, default, value)])
        return self._change_calls(changer)

    @utils.deprecated()
    def inline_default(self, index):
        changer = _FunctionChangers(
            self.pyname.get_object(), self.get_definition_info(),
            [ArgumentDefaultInliner(index)])
        return self._change_calls(changer)

    @utils.deprecated()
    def reorder(self, new_ordering):
        changer = _FunctionChangers(
            self.pyname.get_object(), self.get_definition_info(),
            [ArgumentReorderer(new_ordering)])
        return self._change_calls(changer)

    def get_changes(self, changers, in_hierarchy=False, resources=None,
                    task_handle=taskhandle.NullTaskHandle()):
        """Get changes caused by this refactoring

        `changers` is a list of `_ArgumentChanger`\s.  If `in_hierarchy`
        is `True` the changers are applyed to all matching methods in
        the class hierarchy.
        `resources` can be a list of `rope.base.resource.File`\s that
        should be searched for occurrences; if `None` all python files
        in the project are searched.

        """
        function_changer = _FunctionChangers(self.pyname.get_object(),
                                             self._definfo(), changers)
        return self._change_calls(function_changer, in_hierarchy,
                                  resources, task_handle)


class _FunctionChangers(object):

    def __init__(self, pyfunction, definition_info, changers=None):
        self.pyfunction = pyfunction
        self.definition_info = definition_info
        self.changers = changers
        self.changed_definition_infos = self._get_changed_definition_infos()

    def _get_changed_definition_infos(self):
        result = []
        definition_info = self.definition_info
        result.append(definition_info)
        for changer in self.changers:
            definition_info = copy.deepcopy(definition_info)
            changer.change_definition_info(definition_info)
            result.append(definition_info)
        return result

    def change_definition(self, call):
        return self.changed_definition_infos[-1].to_string()

    def change_call(self, primary, pyname, call):
        call_info = functionutils.CallInfo.read(
            primary, pyname, self.definition_info, call)
        mapping = functionutils.ArgumentMapping(self.definition_info, call_info)

        for definition_info, changer in zip(self.changed_definition_infos, self.changers):
            changer.change_argument_mapping(definition_info, mapping)

        return mapping.to_call_info(self.changed_definition_infos[-1]).to_string()


class _ArgumentChanger(object):

    def change_definition_info(self, definition_info):
        pass

    def change_argument_mapping(self, definition_info, argument_mapping):
        pass


class ArgumentNormalizer(_ArgumentChanger):
    pass


class ArgumentRemover(_ArgumentChanger):

    def __init__(self, index):
        self.index = index

    def change_definition_info(self, call_info):
        if self.index < len(call_info.args_with_defaults):
            del call_info.args_with_defaults[self.index]
        elif self.index == len(call_info.args_with_defaults) and \
           call_info.args_arg is not None:
            call_info.args_arg = None
        elif (self.index == len(call_info.args_with_defaults) and
            call_info.args_arg is None and call_info.keywords_arg is not None) or \
           (self.index == len(call_info.args_with_defaults) + 1 and
            call_info.args_arg is not None and call_info.keywords_arg is not None):
            call_info.keywords_arg = None

    def change_argument_mapping(self, definition_info, mapping):
        if self.index < len(definition_info.args_with_defaults):
            name = definition_info.args_with_defaults[0]
            if name in mapping.param_dict:
                del mapping.param_dict[name]


class ArgumentAdder(_ArgumentChanger):

    def __init__(self, index, name, default=None, value=None):
        self.index = index
        self.name = name
        self.default = default
        self.value = value

    def change_definition_info(self, definition_info):
        for pair in definition_info.args_with_defaults:
            if pair[0] == self.name:
                raise rope.base.exceptions.RefactoringError(
                    'Adding duplicate parameter: <%s>.' % self.name)
        definition_info.args_with_defaults.insert(self.index,
                                                  (self.name, self.default))

    def change_argument_mapping(self, definition_info, mapping):
        if self.value is not None:
            mapping.param_dict[self.name] = self.value


class ArgumentDefaultInliner(_ArgumentChanger):

    def __init__(self, index):
        self.index = index
        self.remove = False

    def change_definition_info(self, definition_info):
        if self.remove:
            definition_info.args_with_defaults[self.index] = \
                (definition_info.args_with_defaults[self.index][0], None)

    def change_argument_mapping(self, definition_info, mapping):
        default = definition_info.args_with_defaults[self.index][1]
        name = definition_info.args_with_defaults[self.index][0]
        if default is not None and name not in mapping.param_dict:
            mapping.param_dict[name] = default


class ArgumentReorderer(_ArgumentChanger):

    def __init__(self, new_order, autodef=None):
        """Construct an `ArgumentReorderer`

        Note that the `new_order` is a list containing the new
        position of parameters; not the position each parameter
        is going to be moved to. (changed in ``0.5m4``)

        For example changing ``f(a, b, c)`` to ``f(c, a, b)``
        requires passing ``[2, 0, 1]`` and *not* ``[1, 2, 0]``.

        The `autodef` (automatic default) argument, forces rope to use
        it as a default if a default is needed after the change.  That
        happens when an argument without default is moved after
        another that has a default value.  Note that `autodef` should
        be a string or `None`; the latter disables adding automatic
        default.

        """
        self.new_order = new_order
        self.autodef = autodef

    def change_definition_info(self, definition_info):
        new_args = list(definition_info.args_with_defaults)
        for new_index, index in enumerate(self.new_order):
            new_args[new_index] = definition_info.args_with_defaults[index]
        seen_default = False
        for index, (arg, default) in enumerate(list(new_args)):
            if default is not None:
                seen_default = True
            if seen_default and default is None and self.autodef is not None:
                new_args[index] = (arg, self.autodef)
        definition_info.args_with_defaults = new_args


class _ChangeCallsInModule(object):

    def __init__(self, pycore, occurrence_finder, resource, call_changer):
        self.pycore = pycore
        self.occurrence_finder = occurrence_finder
        self.resource = resource
        self.call_changer = call_changer

    def get_changed_module(self):
        word_finder = worder.Worder(self.source)
        change_collector = codeanalyze.ChangeCollector(self.source)
        for occurrence in self.occurrence_finder.find_occurrences(self.resource):
            if not occurrence.is_called() and not occurrence.is_defined():
                continue
            start, end = occurrence.get_primary_range()
            begin_parens, end_parens = word_finder.get_word_parens_range(end - 1)
            if occurrence.is_called():
                primary, pyname = occurrence.get_primary_and_pyname()
                changed_call = self.call_changer.change_call(
                    primary, pyname, self.source[start:end_parens])
            else:
                changed_call = self.call_changer.change_definition(
                    self.source[start:end_parens])
            if changed_call is not None:
                change_collector.add_change(start, end_parens, changed_call)
        return change_collector.get_changed()

    @property
    @utils.saveit
    def pymodule(self):
        return self.pycore.resource_to_pyobject(self.resource)

    @property
    @utils.saveit
    def source(self):
        if self.resource is not None:
            return self.resource.read()
        else:
            return self.pymodule.source_code

    @property
    @utils.saveit
    def lines(self):
        return self.pymodule.lines


class _MultipleFinders(object):

    def __init__(self, finders):
        self.finders = finders

    def find_occurrences(self, resource=None, pymodule=None):
        all_occurrences = []
        for finder in self.finders:
            all_occurrences.extend(finder.find_occurrences(resource, pymodule))
        all_occurrences.sort(key = lambda o: o.get_primary_range())
        return all_occurrences