Commits

Anonymous committed 971a03e

Using _ast in rope.refactor

Comments (0)

Files changed (9)

docs/dev/workingon.txt

-Using `_ast` instead of `compiler
-=================================
+Using `_ast` instead of `compiler`
+==================================
 
+* `funcutils` might fail on function start and decorators
+* Separating `Defined.get_lineno()` and `Scope.get_start()`?
+* Function parameter line-number can be enhanced; Using `Name.lineno`
+* Updating `_HoldingScopeFinder`; relies on textual approximation
+* `FunctionDef.lineno` is the line that includes the decorators
+* Replacing `.get_ast().lineno` with `get_scope().get_start()`
 * Handle tuple parameters
 * Handling strings in following lines in `patchedast`
 * Using `PyModule.ast` in `similarfinder`
 
 * Should we call `pycore.resource_to_pyobject()` for all modules
+* Caching `CodeTemplate.substitute()`?
 * The ability to limit the files to restructure
-* Caching `CodeTemplate.substitute()`?
-* CheckingFinder and ``{'?a': '?b'}`` checks?
 * Only scanning selected region for matches
 * Consider filling python source
 

rope/base/codeanalyze.py

         word_start = self._find_word_start(offset - 1)
         word_end = self._find_word_end(offset - 1) + 1
         next_char = self._find_first_non_space_char(word_end)
-        return not self.is_a_class_or_function_name_in_header(offset) and \
-               next_char < len(self.source_code) and \
-               self.source_code[next_char] == '('
+        return next_char < len(self.source_code) and \
+               self.source_code[next_char] == '(' and \
+               not self.is_a_class_or_function_name_in_header(offset)
+               
 
     def _find_import_pair_end(self, start):
         next_char = self._find_first_non_space_char(start)

rope/base/pyscopes.py

         self.lines = pymodule.lines
 
     def get_indents(self, lineno):
-        return rope.base.codeanalyze.count_line_indents(self.lines.get_line(lineno))
+        return rope.base.codeanalyze.count_line_indents(
+            self.lines.get_line(lineno))
 
     def get_location(self, offset):
         current_pos = 0

rope/refactor/extract.py

-import compiler
 import re
 
 import rope.base.pyobjects
-from rope.base import codeanalyze
+from rope.base import ast, codeanalyze
 from rope.base.change import ChangeSet, ChangeContents
 from rope.base.exceptions import RefactoringError
 from rope.refactor import sourceutils
         indented_body = self.info.source[self.info.scope_region[0]:
                                          self.info.scope_region[1]]
         body = sourceutils.fix_indentation(indented_body, 0)
-        ast = _parse_text(body)
-        compiler.walk(ast, info_collector)
+        node = _parse_text(body)
+        ast.walk(node, info_collector)
         return info_collector
 
     def _get_function_indents(self):
         if self.end < lineno:
             self.postwritten.add(name)
 
-    def visitFunction(self, node):
+    def _FunctionDef(self, node):
         if not self.is_global and self.host_function:
             self.host_function = False
-            for name in node.argnames:
+            for name in _get_argnames(node.args):
                 self._written_variable(name, node.lineno)
-            compiler.walk(node.code, self)
+            for child in node.body:
+                ast.walk(child, self)
         else:
             self._written_variable(node.name, node.lineno)
             visitor = _VariableReadsAndWritesFinder()
-            compiler.walk(node.code, visitor)
+            for child in node.body:
+                ast.walk(child, visitor)
             for name in visitor.read - visitor.written:
                 self._read_variable(name, node.lineno)
 
-    def visitAssName(self, node):
+    def _Name(self, node):
+        if isinstance(node.ctx, (ast.Store, ast.AugStore)):
+            self._written_variable(node.id, node.lineno)
+        if not isinstance(node.ctx, ast.Store):
+            self._read_variable(node.id, node.lineno)
+
+    def _Assign(self, node):
+        ast.walk(node.value, self)
+        for child in node.targets:
+            ast.walk(child, self)
+
+    def _ClassDef(self, node):
         self._written_variable(node.name, node.lineno)
 
-    def visitAssign(self, node):
-        compiler.walk(node.expr, self)
-        for child in node.nodes:
-            compiler.walk(child, self)
 
-    def visitName(self, node):
-        self._read_variable(node.name, node.lineno)
-
-    def visitClass(self, node):
-        self._written_variable(node.name, node.lineno)
+def _get_argnames(arguments):
+    result = [node.id for node in arguments.args
+              if isinstance(node, ast.Name)]
+    if arguments.vararg:
+        result.append(arguments.vararg)
+    if arguments.kwarg:
+        result.append(arguments.kwarg)
+    return result
 
 
 class _VariableReadsAndWritesFinder(object):
         self.written = set()
         self.read = set()
 
-    def visitAssName(self, node):
-        self.written.add(node.name)
+    def _Name(self, node):
+        if isinstance(node.ctx, (ast.Store, ast.AugStore)):
+            self.written.add(node.id)
+        if not isinstance(node, ast.Store):
+            self.read.add(node.id)
 
-    def visitName(self, node):
-        if node.name not in self.written:
-            self.read.add(node.name)
-
-    def visitFunction(self, node):
+    def _FunctionDef(self, node):
         self.written.add(node.name)
         visitor = _VariableReadsAndWritesFinder()
-        compiler.walk(node.code, visitor)
+        ast.walk(node.code, visitor)
         self.read.update(visitor.read - visitor.written)
 
-    def visitClass(self, node):
+    def _Class(self, node):
         self.written.add(node.name)
 
     @staticmethod
         indented_code = sourceutils.indent_lines(code, -min_indents)
         if isinstance(indented_body, unicode):
             indented_body = indented_body.encode('utf-8')
-        ast = _parse_text(indented_code)
+        node = _parse_text(indented_code)
         visitor = _VariableReadsAndWritesFinder()
-        compiler.walk(ast, visitor)
+        ast.walk(node, visitor)
         return visitor.read, visitor.written
 
     @staticmethod
     def find_reads_for_one_liners(code):
         if code.strip() == '':
             return set(), set()
-        ast = _parse_text(code)
+        node = _parse_text(code)
         visitor = _VariableReadsAndWritesFinder()
-        compiler.walk(ast, visitor)
+        ast.walk(node, visitor)
         return visitor.read
 
 
         if self.loop_count < 1:
             self.error = True
 
-    def visitReturn(self, node):
+    def _Return(self, node):
         self.returns = True
 
-    def visitYield(self, node):
+    def _Yield(self, node):
         self.returns = True
 
-    def visitFunction(self, node):
+    def _Function(self, node):
         pass
 
-    def visitClass(self, node):
+    def _Class(self, node):
         pass
 
     @staticmethod
             return False
         min_indents = sourceutils.find_minimum_indents(code)
         indented_code = sourceutils.indent_lines(code, -min_indents)
-        ast = _parse_text(indented_code)
+        node = _parse_text(indented_code)
         visitor = _ReturnOrYieldFinder()
-        compiler.walk(ast, visitor)
+        ast.walk(node, visitor)
         return visitor.returns
 
 
         self.error = False
         self.loop_count = 0
 
-    def visitFor(self, node):
+    def _For(self, node):
         self.loop_encountered(node)
 
-    def visitWhile(self, node):
+    def _While(self, node):
         self.loop_encountered(node)
 
     def loop_encountered(self, node):
         self.loop_count += 1
-        compiler.walk(node.body, self)
+        for child in node.body:
+            ast.walk(child, self)
         self.loop_count -= 1
-        if node.else_:
-            compiler.walk(node.else_, self)
+        if node.orelse:
+            ast.walk(node.orelse, self)
 
-    def visitBreak(self, node):
+    def _Break(self, node):
         self.check_loop()
 
-    def visitContinue(self, node):
+    def _Continue(self, node):
         self.check_loop()
 
     def check_loop(self):
         if self.loop_count < 1:
             self.error = True
 
-    def visitFunction(self, node):
+    def _FunctionDef(self, node):
         pass
 
-    def visitClass(self, node):
+    def _ClassDef(self, node):
         pass
 
     @staticmethod
             return False
         min_indents = sourceutils.find_minimum_indents(code)
         indented_code = sourceutils.indent_lines(code, -min_indents)
-        ast = _parse_text(indented_code)
+        node = _parse_text(indented_code)
         visitor = _UnmatchedBreakOrContinueFinder()
-        compiler.walk(ast, visitor)
+        ast.walk(node, visitor)
         return visitor.error
 
 
 def _parse_text(body):
     if isinstance(body, unicode):
         body = body.encode('utf-8')
-    ast = compiler.parse(body)
-    return ast
+    node = ast.parse(body)
+    return node
 
 def _join_lines(code):
     lines = []

rope/refactor/functionutils.py

 import rope.base.exceptions
 import rope.base.pyobjects
-import compiler.consts
-from rope.base import codeanalyze
+from rope.base import ast, codeanalyze
 
 
 class _FunctionParser(object):
         decorators = pyname.get_object().get_ast().decorators
         if decorators is None:
             return True
-        for decorator in decorators.nodes:
+        for decorator in decorators:
             if _is_staticmethod_decorator(decorator) or \
                _is_classmethod_decorator(decorator):
                 return False
     return False
 
 def _is_staticmethod_decorator(node):
-    return isinstance(node, compiler.ast.Name) and node.name == 'staticmethod'
+    return isinstance(node, ast.Name) and node.id == 'staticmethod'
 
 def _is_classmethod_decorator(node):
-    return isinstance(node, compiler.ast.Name) and node.name == 'classmethod'
+    return isinstance(node, ast.Name) and node.id == 'classmethod'
 
 
 class ArgumentMapping(object):

rope/refactor/importutils/module_imports.py

         # Getting the line of the first import fails when the first
         # import is not in the first non doc line of module
         nodes = self.pymodule.get_ast().body
-        if nodes:
-            last_index = nodes[0].lineno
+        first_child = 0
+        if self.pymodule.get_doc() is not None:
+            first_child = 1
+        if len(nodes) > first_child:
+            last_index = nodes[first_child].lineno
         return last_index
 
     def _compare_imports(self, stmt1, stmt2):
         pyobject = self.pyobject.get_module().get_scope().\
                    get_inner_scope_for_line(node.lineno).pyobject
         visitor = _LocalUnboundNameFinder(pyobject, self)
-        for child in node.getChildNodes():
+        for child in ast.get_child_nodes(node):
             ast.walk(child, visitor)
 
     def _FunctionDef(self, node):
 
     def _Name(self, node):
         if self._get_root()._is_node_interesting(node) and \
-           not self.is_bound(node.name):
-            self.add_unbound(node.name)
+           not self.is_bound(node.id):
+            self.add_unbound(node.id)
 
     def _Attribute(self, node):
         result = []
         while isinstance(node, ast.Attribute):
-            result.append(node.attrname)
-            node = node.expr
+            result.append(node.attr)
+            node = node.value
         if isinstance(node, ast.Name):
-            result.append(node.name)
+            result.append(node.id)
             primary = '.'.join(reversed(result))
             if self._get_root()._is_node_interesting(node) and \
                not self.is_bound(primary):
     def visit_import(self, node, end_line):
         start_line = node.lineno
         import_statement = importinfo.ImportStatement(
-            importinfo.NormalImport(node.names),start_line, end_line,
-            self._get_text(start_line, end_line),
+            importinfo.NormalImport(self._get_names(node.names)),
+            start_line, end_line, self._get_text(start_line, end_line),
             blank_lines=self._count_empty_lines_before(start_line))
         self.imports.append(import_statement)
     
 
     def visit_from(self, node, end_line):
         level = 0
-        if hasattr(node, 'level'):
+        if node.level:
             level = node.level
-        import_info = importinfo.FromImport(node.modname, level, node.names,
-                                            self.current_folder, self.pycore)
+        import_info = importinfo.FromImport(
+            node.module, level, self._get_names(node.names),
+            self.current_folder, self.pycore)
         start_line = node.lineno
         self.imports.append(importinfo.ImportStatement(
                             import_info, node.lineno, end_line,
                             self._get_text(start_line, end_line),
                             blank_lines=self._count_empty_lines_before(start_line)))
 
+    def _get_names(self, alias_names):
+        result = []
+        for alias in alias_names:
+            result.append((alias.name, alias.asname))
+        return result
+
     def find_import_statements(self):
         nodes = self.pymodule.get_ast().body
         for index, node in enumerate(nodes):

rope/refactor/inline.py

         scope = self.pyfunction.get_scope()
         lines = self.pymodule.lines
         start_line = scope.get_start()
-        if self.pyfunction.get_ast().decorators is not None:
+        if self.pyfunction.get_ast().decorators:
             decorators = self.pyfunction.get_ast().decorators
-            if hasattr(decorators.nodes[0], 'lineno'):
-                start_line = decorators.nodes[0].lineno
+            if hasattr(decorators[0], 'lineno'):
+                start_line = decorators[0].lineno
         start_offset = lines.get_line_start(start_line)
         end_offset = min(lines.get_line_end(scope.get_end()) + 1,
                          len(self.pymodule.source_code))

ropetest/refactor/extracttest.py

         start = code.index('\n') + 1
         end = len(code)
         refactored = self.do_extract_method(code, start, end, 'new_f')
-        expected = 'a = 1\n\ndef new_f(a):\n    a += 1\n\nnew_f(a)\n'
+        expected = 'a = 1\n\ndef new_f():\n    a += 1\n\nnew_f()\n'
         self.assertEquals(expected, refactored)
 
 

ropetest/refactor/importutilstest.py

 
     @testutils.run_only_for_25
     def test_get_import_statements_for_new_relatives(self):
-        self.mod2.write('from .mod3 import *\n')
+        self.mod2.write('from .mod3 import x\n')
         pymod = self.pycore.get_module('pkg2.mod2')
         module_with_imports = self.import_tools.get_module_imports(pymod)
         imports = module_with_imports.get_import_statements()
-        self.assertEquals('from .mod3 import *',
+        self.assertEquals('from .mod3 import x',
                           imports[0].import_info.get_import_statement())
 
     def test_ignoring_indented_imports(self):