Anonymous avatar Anonymous committed 02ea6a2

Added global_ parameter to extract refactorings

If True, the extracted variable/function will be global and the whole
file is searched for similar pieces.

Comments (0)

Files changed (8)

docs/dev/issues.txt

 Unresolved Issues
 =================
 
-* Commenting based on the line of syntax error in codeassist?
 * objectinfer._infer_returned; should we give up when the first
   attempt returned None or unknown, user config?
 * Review objectinfo module
 * Remove ``change occurreces`` refactoring?
 * Using similarfinder in introduce_parameter?
 * Switching to GPLv3?
-* Removed unused refactorings
+* Remove unused refactorings?
 * Unignored files that are not under version control
 * Removing unused objectdbs?
 

docs/dev/todo.txt

 
 
 * Inlining a single occurrence
+
+
+> Public Release 0.7.3
 ===========
 
 
+- Global extract method/variable : December 13, 2007
+
+
 > Public Release 0.7.2 : December 13, 2007
 
 
 """rope, a python refactoring library"""
 
 INFO = __doc__
-VERSION = '0.7.2'
+VERSION = '0.7.3'
 COPYRIGHT = """\
 Copyright (C) 2006-2007 Ali Gholami Rudi
 

rope/refactor/extract.py

         self.resource = resource
         self.start_offset = self._fix_start(resource.read(), start_offset)
         self.end_offset = self._fix_end(resource.read(), end_offset)
-        self.variable = variable
 
     def _fix_start(self, source, offset):
         while offset < len(source) and source[offset].isspace():
             offset -= 1
         return offset
 
-    def get_changes(self, extracted_name, similar=False):
+    def get_changes(self, extracted_name, similar=False, global_=False):
         """Get the changes this refactoring makes
 
         if `similar` is `True` similar expressions/statements are also
         """
         info = _ExtractInfo(
             self.project, self.resource, self.start_offset, self.end_offset,
-            extracted_name, variable=self.variable, similar=similar)
+            extracted_name, variable=self.kind == 'variable',
+            similar=similar, make_global=global_)
         new_contents = _ExtractPerformer(info).extract()
-        changes = ChangeSet('Extract %s <%s>' % (self._get_name(),
+        changes = ChangeSet('Extract %s <%s>' % (self.kind,
                                                  extracted_name))
         changes.add_change(ChangeContents(self.resource, new_contents))
         return changes
 
-    def _get_name(self):
-        if self.variable:
-            return 'variable'
-        return 'method'
-
 
 class ExtractMethod(_ExtractRefactoring):
 
     def __init__(self, *args, **kwds):
         super(ExtractMethod, self).__init__(*args, **kwds)
 
+    kind = 'method'
+
 
 class ExtractVariable(_ExtractRefactoring):
 
         kwds['variable'] = True
         super(ExtractVariable, self).__init__(*args, **kwds)
 
+    kind = 'variable'
+
 
 class _ExtractInfo(object):
     """Holds information about the extract to be performed"""
 
     def __init__(self, project, resource, start, end, new_name,
-                 variable=False, similar=False):
+                 variable, similar, make_global):
         self.pycore = project.pycore
         self.resource = resource
         self.pymodule = self.pycore.resource_to_pyobject(resource)
         self.similar = similar
         self._init_parts(start, end)
         self._init_scope()
+        self.make_global = make_global
 
     def _init_parts(self, start, end):
         self.logical_lines = codeanalyze.LogicalLineFinder(self.lines)
             return min(line_end, len(self.source))
         return offset
 
-    def _is_one_line(self):
+    @property
+    def one_line(self):
         return self.region != self.lines_region and \
                (self.logical_lines.get_logical_line_in(self.region_lines[0]) ==
                 self.logical_lines.get_logical_line_in(self.region_lines[1]))
 
-    def _is_global(self):
+    @property
+    def global_(self):
         return self.scope.parent is None
 
-    def _is_method(self):
+    @property
+    def method(self):
         return self.scope.parent is not None and \
                self.scope.parent.get_kind() == 'Class'
 
-    one_line = property(_is_one_line)
-    global_ = property(_is_global)
-    method = property(_is_method)
-
-    def _get_indents(self):
+    @property
+    def indents(self):
         return sourceutils.get_indents(self.pymodule.lines,
                                        self.region_lines[0])
 
-    def _get_scope_indents(self):
+    @property
+    def scope_indents(self):
         if self.global_:
             return 0
         return sourceutils.get_indents(self.pymodule.lines,
                                        self.scope.get_start())
 
-    def _get_extracted(self):
+    @property
+    def extracted(self):
         return self.source[self.region[0]:self.region[1]]
 
-    indents = property(_get_indents)
-    scope_indents = property(_get_scope_indents)
-    extracted = property(_get_extracted)
-
 
 class _ExtractCollector(object):
     """Collects information needed for performing the extract"""
 
     def _where_to_search(self):
         if self.info.similar:
+            if self.info.make_global:
+                return [(0, len(self.info.pymodule.source_code))]
             if self.info.method and not self.info.variable:
                 class_scope = self.info.scope.parent
                 regions = []
         self.matched_lines = matched_lines
 
     def find_lineno(self):
+        if self.info.make_global and not self.info.global_:
+            toplevel = self._find_toplevel(self.info.scope)
+            ast = self.info.pymodule.get_ast()
+            newlines = sorted(self.matched_lines + [toplevel.get_end() + 1])
+            return suites.find_visible(ast, newlines)
         if self.info.global_ or self.info.variable:
             return self._get_before_line()
         return self._get_after_scope()
 
+    def _find_toplevel(self, scope):
+        toplevel = scope
+        if toplevel.parent is not None:
+            while toplevel.parent.parent is not None:
+                toplevel = toplevel.parent
+            return toplevel
+
     def find_indents(self):
+        if self.info.make_global:
+            return 0
         if self.info.global_ or self.info.variable:
             return sourceutils.get_indents(self.info.lines,
                                            self._get_before_line())
         return similarfinder.make_pattern(body, variables)
 
     def get_checks(self):
-        if self.info.method:
+        if self.info.method and not self.info.make_global:
             if _get_function_kind(self.info.scope) == 'method':
                 return {'?%s.type' % self._get_self_name():
                         self.info.scope.parent.pyobject}
         args = self._find_function_arguments()
         returns = self._find_function_returns()
         result = []
-        if self.info.method and _get_function_kind(self.info.scope) != 'method':
+        if self.info.method and not self.info.make_global and \
+           _get_function_kind(self.info.scope) != 'method':
             result.append('@staticmethod\n')
         result.append('def %s:\n' % self._get_function_signature(args))
         unindented_body = self._get_unindented_function_body(returns)
     def _get_function_signature(self, args):
         args = list(args)
         prefix = ''
-        if self.info.method and _get_function_kind(self.info.scope) == 'method':
+        if self.info.method and not self.info.make_global and \
+                _get_function_kind(self.info.scope) == 'method':
             self_name = self._get_self_name()
             if self_name in args:
                 args.remove(self_name)
 
     def _get_function_call(self, args):
         prefix = ''
-        if self.info.method:
+        if self.info.method and not self.info.make_global:
             if _get_function_kind(self.info.scope) == 'method':
                 self_name = self._get_self_name()
                 if  self_name in args:

rope/refactor/suites.py

     line2 = find_visible_for_suite(root, lines[1:])
     suite1 = root.find_suite(line1)
     suite2 = root.find_suite(line2)
+    def valid(suite):
+        return suite is not None and not suite.ignored
+    if valid(suite1) and not valid(suite2):
+        return line1
+    if not valid(suite1) and valid(suite2):
+        return line2
+    if not valid(suite1) and not valid(suite2):
+        return None
     while suite1 != suite2 and suite1.parent != suite2.parent:
         if suite1._get_level() < suite2._get_level():
             line2 = suite2.get_start()
 
 class Suite(object):
 
-    def __init__(self, child_nodes, lineno, parent=None):
+    def __init__(self, child_nodes, lineno, parent=None, ignored=False):
         self.parent = parent
         self.lineno = lineno
         self.child_nodes = child_nodes
         self._children = None
+        self.ignored = ignored
 
     def get_start(self):
         if self.parent is None:
         return end
 
     def find_suite(self, line):
+        if line is None:
+            return None
         for child in self.get_children():
             if child.local_start() <= line <= child.local_end():
                 return child.find_suite(line)
             self.suites.append(Suite(node.orelse, node.lineno, self.suite))
 
     def _FunctionDef(self, node):
-        pass
+        self.suites.append(Suite(node.body, node.lineno,
+                                 self.suite, ignored=True))
 
     def _ClassDef(self, node):
-        pass
+        self.suites.append(Suite(node.body, node.lineno,
+                                 self.suite, ignored=True))

ropetest/refactor/extracttest.py

                    'def g():\n    func = a.func()\n    return func\n'
         self.assertEquals(expected, refactored)
 
+    def test_global_option_for_extract_method(self):
+        code = "def a_func():\n    print('one')\n"
+        start, end = self._convert_line_range_to_offset(code, 2, 2)
+        refactored = self.do_extract_method(code, start, end,
+                                            'extracted', global_=True)
+        expected = "def a_func():\n    extracted()\n\n" \
+                   "def extracted():\n    print('one')\n"
+        self.assertEquals(expected, refactored)
+
+    def test_simple_extract_method(self):
+        code = 'class AClass(object):\n\n' \
+               '    def a_func(self):\n        print(1)\n'
+        start, end = self._convert_line_range_to_offset(code, 4, 4)
+        refactored = self.do_extract_method(code, start, end,
+                                            'new_func', global_=True)
+        expected = 'class AClass(object):\n\n' \
+                   '    def a_func(self):\n        new_func()\n\n' \
+                   'def new_func():\n    print(1)\n'
+        self.assertEquals(expected, refactored)
+
+    def test_extract_method_with_multiple_methods(self):
+        code = "class AClass(object):\n" \
+               "    def a_func(self):\n" \
+               "        print(1)\n\n" \
+               "    def another_func(self):\n" \
+               "        pass\n"
+        start, end = self._convert_line_range_to_offset(code, 3, 3)
+        refactored = self.do_extract_method(code, start, end,
+                                            'new_func', global_=True)
+        expected = "class AClass(object):\n" \
+                   "    def a_func(self):\n" \
+                   "        new_func()\n\n" \
+                   "    def another_func(self):\n" \
+                   "        pass\n\n" \
+                   "def new_func():\n" \
+                   "    print(1)\n"
+        self.assertEquals(expected, refactored)
+
+    def test_where_to_seach_when_extracting_global_names(self):
+        code = 'def a():\n    return 1\ndef b():\n    return 1\nb = 1\n'
+        start = code.index('1')
+        end = start + 1
+        refactored = self.do_extract_variable(code, start, end, 'one',
+                                              similar=True, global_=True)
+        expected = 'def a():\n    return one\none = 1\n' \
+            'def b():\n    return one\nb = one\n'
+        self.assertEquals(expected, refactored)
+
 
 if __name__ == '__main__':
     unittest.main()

ropetest/refactor/suitestest.py

         self.assertEquals(2, len(root.get_children()))
         self.assertEquals(1, root.get_children()[1].get_start())
 
-    def test_ignoring_function_and_class_defs(self):
-        root = source_suite_tree(
-            'def f():\n    if True:        pass\nclass C(object):\n    pass\n')
-        self.assertEquals(0, len(root.get_children()))
-
     def test_for(self):
         root = source_suite_tree(
             '\nfor i in range(10):\n    pass\nelse:\n    pass\n')
         self.assertEquals(3, suites.find_visible_for_suite(root, [3, 5]))
         self.assertEquals(3, suites.find_visible_for_suite(root, [4, 5]))
 
+    def test_ignoring_functions(self):
+        root = source_suite_tree(
+            'def f():\n    pass\na = 1\n')
+        self.assertEquals(3, suites.find_visible_for_suite(root, [2, 3]))
+
+    def test_ignoring_classes(self):
+        root = source_suite_tree(
+            'a = 1\nclass C():\n    pass\n')
+        self.assertEquals(1, suites.find_visible_for_suite(root, [1, 3]))
+
 
 def source_suite_tree(source):
     return suites.ast_suite_tree(ast.parse(source))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.