Commits

Anonymous committed 361be47

Sorting imports

  • Participants
  • Parent commits 41c4490

Comments (0)

Files changed (18)

File docs/dev/done.txt

 ===========
 
 
+- Sorting imports; standard, third party, project : January 7, 2007
+
+
 - Enhanced dynamic object inference : January 5, 2007
 
 

File docs/dev/issues.txt

 Hot Topics
 ==========
 
+* `Local History`_
 * `Removing PythonRefactoring facade`_
 
 
 * Indexing source files for faster occurrence finding
 * Saving hard to compute information like class hierarchies to files
 * Using a modification of `compiler` AST for simplifying refactorings
-* Undo Unification
+* Undo unification
+
+
+Local History
+=============
+
+Goals:
+
+* Better undoing mechanisms
+* Undoing unrelated refactorings in any order
+* Undoing file operations performed by editing
+* Undoing individual files
+* Moving undo information from `PythonRefactoring` to `Project`
+* Finding changes to a file
+* Saving undo information across sessions?
+* Supporting virtual `PyModule`\s?
+
+Issues:
+
+* Unanticipated changes
+* Having version control systems in parallel
+
+
+Removing `PythonRefactoring` Facade
+===================================
+
+If we remove `PythonRefactoring`, we need a place for storing
+refactoring undo information.  We can move it either to projects
+or ask the UI for handling its undo information.  The former is
+discussed in `Local History`_ section.
+
+* Changing uses of `PythonRefactoring` to use `rope.refactor` modules
+* Adding a place for saving undo information
+* What to do with import utils
+* Documenting the refactorings `rope.refactor` submodules provide
+* Updating references to `PythonRefactoring` and
+  `PyCore.get_refactoring` to use the new objects.
 
 
 Rope's Preference System
 * Unifying refactoring interfaces
 * Introducing factories for refactorings;
   Reduce the dependence to `rope.refactor` modules
-* Removing `rope.refactor.PythonRefactoring`
 
 
 A Common Refactoring Interface
       """
 
       # Other functions for getting information after construction
-      # for helping IDE's.
+      # for helping IDEs.
 
 
 Note that the arguments of `__init__` and `get_changes` are not the
   renames but we have to call their constructors separately.
 
 
-Removing `PythonRefactoring` Facade
------------------------------------
-
-* Changing uses of `PythonRefactoring` to use `rope.refactor` modules
-* Adding a place for saving undo information
-* What to do with import utils
-* Documenting the refactorings `rope.refactor` submodules provide
-* Updating references to `PythonRefactoring` and
-  `PyCore.get_refactoring` to use the new objects.
-
-
 Moving Fields/Methods
 =====================
 

File docs/dev/stories.txt

 * Saving preferences
 
 
-> Public Release 0.4m5 : January 7, 2007
+> Public Release 0.4m5 : January 14, 2007
 
 
 * Commanding buffer
 
 
 * Transforming to ``from w.x.y import z`` for long imports
-
-
-* Sorting imports; standard, third party, project

File docs/dev/workingon.txt

-Removing `PythonRefactoring`
-============================
+Sorting imports; standard, third party, project
+===============================================
 
-* changing uses of `PythonRefactoring` to use `rope.refactor` modules
-* adding a place for saving undo information
-* what to do with import utils
-* documenting the refactorings `rope.refactor` submodules provide
-* updating references to `PythonRefactoring` and
-  `PyCore.get_refactoring` to use the new objects.
+- alphabetical
+- from imports
+- standard
+- Preventing unlimited insertion of blank lines
+- Holding the number of blank lines after each import statement
+- Third party imports
+- Refactor `importutils`
 
-* do something about long lines
+* Adding sort imports to organize imports?
+* Handling `super` and `range` functions
+
+
+Local History
+=============
+
+* Adding `rope.base.history` module
 
 
 Remaining Stories
 =================
 
+* Comments should be indented
 * Caching calculated parameters and returned object in `PyFunction`\s
 * Handling the return type of generator functions
 * Fixing multiple function definition problems

File rope/base/codeanalyze.py

         #ast = compiler.parse(name)
         # parenthesizing for handling cases like 'a_var.\nattr'
         ast = compiler.parse('(%s)' % name)
-        result = evaluate.StatementEvaluator.get_statement_result(holding_scope, ast)
+        result = evaluate.get_statement_result(holding_scope, ast)
         return result
 
 

File rope/base/evaluate.py

         self.result = self.scope.lookup(node.name)
 
     def visitGetattr(self, node):
-        pyname = StatementEvaluator.get_statement_result(self.scope, node.expr)
+        pyname = get_statement_result(self.scope, node.expr)
         if pyname is not None:
             try:
                 self.result = pyname.get_object().get_attribute(node.attrname)
             pyobject=rope.base.builtins.get_tuple(*objects))
 
     def _get_object_for_node(self, stmt):
-        pyname = StatementEvaluator.get_statement_result(self.scope, stmt)
+        pyname = get_statement_result(self.scope, stmt)
         pyobject = None
         if pyname is not None:
             pyobject = pyname.get_object()
             self.result = rope.base.pynames.AssignedName(
                 pyobject=call_function.get_object().get_returned_object())
 
-    @staticmethod
-    def get_statement_result(scope, node):
-        evaluator = StatementEvaluator(scope)
-        compiler.walk(node, evaluator)
-        return evaluator.result
 
-    @staticmethod
-    def get_string_result(scope, string):
-        evaluator = StatementEvaluator(scope)
-        node = compiler.parse(string)
-        compiler.walk(node, evaluator)
-        return evaluator.result
+def get_statement_result(scope, node):
+    evaluator = StatementEvaluator(scope)
+    compiler.walk(node, evaluator)
+    return evaluator.result
+
+
+def get_string_result(scope, string):
+    evaluator = StatementEvaluator(scope)
+    node = compiler.parse(string)
+    compiler.walk(node, evaluator)
+    return evaluator.result
 
 
 class Arguments(object):
         result = [None] * len(parameters)
         for index, arg in enumerate(self.args):
             if isinstance(arg, compiler.ast.Keyword) and arg.name in parameters:
-                pyname = StatementEvaluator.get_statement_result(self.scope, arg.expr)
+                pyname = self._evaluate(arg.expr)
                 if pyname is not None:
                     result[parameters.index(arg.name)] = pyname.get_object()
             else:
-                pyname = StatementEvaluator.get_statement_result(self.scope, arg)
+                pyname = self._evaluate(arg)
                 if pyname is not None:
                     result[index] = pyname.get_object()
         return result
+
+    def _evaluate(self, ast_node):
+        return get_statement_result(self.scope, ast_node)

File rope/base/oi/staticoi.py

             if hasattr(assign_node, 'lineno') and assign_node.lineno is not None:
                 lineno = assign_node.lineno
             holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
-            return evaluate.StatementEvaluator.\
-                   get_statement_result(holding_scope, assign_node)
+            return evaluate.get_statement_result(holding_scope, assign_node)
         except pyobjects.IsBeingInferredException:
             pass
 
             return
         for returned_node in reversed(scope._get_returned_asts()):
             try:
-                resulting_pyname = evaluate.StatementEvaluator.\
-                                   get_statement_result(scope, returned_node)
+                resulting_pyname = evaluate.get_statement_result(scope,
+                                                                 returned_node)
                 if resulting_pyname is None:
                     return None
                 return resulting_pyname.get_object()

File rope/base/pyobjects.py

     def _get_bases(self):
         result = []
         for base_name in self.ast_node.bases:
-            base = rope.base.evaluate.StatementEvaluator.\
-                   get_statement_result(self.parent.get_scope(), base_name)
+            base = rope.base.evaluate.get_statement_result(
+                self.parent.get_scope(), base_name)
             if base:
                 result.append(base.get_object())
         return result

File rope/refactor/__init__.py

             changes.add_change(ChangeContents(resource, result))
             self.refactoring.add_and_commit_changes(changes)
 
+    def sort_imports(self, resource):
+        pymodule = self.pycore.resource_to_pyobject(resource)
+        result = self.import_tools.sort_imports(pymodule)
+        if result is not None:
+            changes = ChangeSet()
+            changes.add_change(ChangeContents(resource, result))
+            self.refactoring.add_and_commit_changes(changes)
+
 
 class Undo(object):
 

File rope/refactor/importutils/__init__.py

         return True
 
     def organize_imports(self, pymodule):
-        module_with_imports = self.get_module_with_imports(pymodule)
-        module_with_imports.remove_unused_imports()
-        module_with_imports.remove_duplicates()
-        before_removing_self_import = module_with_imports.get_changed_source()
-        to_be_fixed, to_be_renamed = module_with_imports.get_self_import_fix_and_rename_list()
-        source = module_with_imports.get_changed_source()
+        module_imports = self.get_module_with_imports(pymodule)
+        module_imports.remove_unused_imports()
+        module_imports.remove_duplicates()
+        before_removing_self_import = module_imports.get_changed_source()
+        to_be_fixed, to_be_renamed = module_imports.get_self_import_fix_and_rename_list()
+        source = module_imports.get_changed_source()
         if source is not None:
             pymodule = self.pycore.get_string_module(source, pymodule.get_resource())
         for name in to_be_fixed:
 
     def _rename_in_module(self, pymodule, name, new_name, till_dot=False):
         old_name = name.split('.')[-1]
-        old_pyname = rope.base.evaluate.StatementEvaluator.get_string_result(
+        old_pyname = rope.base.evaluate.get_string_result(
             pymodule.get_scope(), name)
         occurrence_finder = rope.refactor.occurrences.FilteredOccurrenceFinder(
             self.pycore, old_name, [old_pyname], imports=False)
         if source is not None:
             pymodule = self.pycore.get_string_module(source, pymodule.get_resource())
         return pymodule
+
+    def sort_imports(self, pymodule):
+        module_imports = self.get_module_with_imports(pymodule)
+        module_imports.sort_imports()
+        return module_imports.get_changed_source()

File rope/refactor/importutils/actions.py

+import os
+import sys
+
 from rope.base import pyobjects
 from rope.base import exceptions
 from rope.refactor.importutils import importinfo
         return result
 
     def visitFromImport(self, import_stmt, import_info):
-        if import_info.level == 0:
-            resource = self.pycore.find_module(import_info.module_name,
-                                               current_folder=self.current_folder)
-        else:
-            resource = self.pycore.find_relative_module(
-                import_info.module_name, self.current_folder, import_info.level)
+        resource = import_info.get_imported_resource()
         if resource is None:
             return None
         absolute_name = importinfo.get_module_name(self.pycore, resource)
             import_stmt.import_info = importinfo.NormalImport(new_pairs)
 
     def visitFromImport(self, import_stmt, import_info):
-        if import_info.level == 0:
-            resource = self.pycore.find_module(import_info.module_name,
-                                               current_folder=self.current_folder)
-        else:
-            resource = self.pycore.find_relative_module(
-                import_info.module_name, self.current_folder, import_info.level)
+        resource = import_info.get_imported_resource()
         if resource is None:
             return
         if resource == self.resource:
                 if alias is not None:
                     self.to_be_renamed.add((alias, name))
         import_stmt.empty_import()
+
+
+class SortingVisitor(ImportInfoVisitor):
+
+    def __init__(self, pycore, current_folder):
+        self.pycore = pycore
+        self.current_folder = current_folder
+        self.standard = set()
+        self.third_party = set()
+        self.in_project = set()
+
+    def visitNormalImport(self, import_stmt, import_info):
+        if import_info.names_and_aliases:
+            name, alias = import_info.names_and_aliases[0]
+            resource = self.pycore.find_module(
+                name, current_folder=self.current_folder)
+            self._check_imported_resource(import_stmt, resource, name)
+
+    def visitFromImport(self, import_stmt, import_info):
+        resource = import_info.get_imported_resource()
+        self._check_imported_resource(import_stmt, resource,
+                                      import_info.module_name)
+
+    def _check_imported_resource(self, import_stmt, resource, imported_name):
+        if resource is not None and resource.project == self.pycore.project:
+            self.in_project.add(import_stmt)
+        else:
+            if imported_name.split('.')[0] in SortingVisitor.standard_modules():
+                self.standard.add(import_stmt)
+            else:
+                self.third_party.add(import_stmt)
+
+    @classmethod
+    def standard_modules(cls):
+        if not hasattr(cls, '_standard_modules'):
+            result = set(sys.builtin_module_names)
+            lib_path = os.path.join(
+                sys.prefix, 'lib%spython%s' % (os.sep, sys.version[:3]))
+            for name in os.listdir(lib_path):
+                path = os.path.join(lib_path, name)
+                if os.path.isdir(path):
+                    if '-' not in name:
+                        result.add(name)
+                else:
+                    if name.endswith('.py'):
+                        result.add(name[:-3])
+            cls._standard_modules = result
+        return cls._standard_modules

File rope/refactor/importutils/importinfo.py

 class ImportStatement(object):
 
-    def __init__(self, import_info, start_line, end_line, main_statement=None):
+    def __init__(self, import_info, start_line, end_line,
+                 main_statement=None, blank_lines=0):
         self.start_line = start_line
         self.end_line = end_line
         self.main_statement = main_statement
         self._import_info = None
         self.import_info = import_info
-        self.is_changed = False
+        self._is_changed = False
+        self.new_start = None
+        self.blank_lines = blank_lines
 
     def _get_import_info(self):
         return self._import_info
 
     def _set_import_info(self, new_import):
         if new_import is not None and not new_import == self._import_info:
-            self.is_changed = True
+            self._is_changed = True
             self._import_info = new_import
 
     import_info = property(_get_import_info, _set_import_info)
 
     def get_import_statement(self):
-        if self.is_changed or self.main_statement is None:
+        if self._is_changed or self.main_statement is None:
             return self.import_info.get_import_statement()
         else:
             return self.main_statement
 
     def empty_import(self):
         self.import_info = ImportInfo.get_empty_import()
+    
+    def move(self, lineno, blank_lines=0):
+        self.new_start = lineno
+        self.blank_lines = blank_lines
+
+    def get_old_location(self):
+        return self.start_line, self.end_line
+
+    def get_new_start(self):
+        return self.new_start
+
+    def is_changed(self):
+        return self._is_changed or (self.new_start is not None or
+                                    self.new_start != self.start_line)
 
     def accept(self, visitor):
         return visitor.dispatch(self)
             return self.pycore.get_relative_module(
                 self.module_name, self.current_folder, self.level)
 
+    def get_imported_resource(self):
+        if self.level == 0:
+            return self.pycore.find_module(self.module_name,
+                                           current_folder=self.current_folder)
+        else:
+            return self.pycore.find_relative_module(
+                self.module_name, self.current_folder, self.level)
+
     def get_import_statement(self):
         result = 'from ' + '.' * self.level + self.module_name + ' import '
         for name, alias in self.names_and_aliases:

File rope/refactor/importutils/module_imports.py

         return result
 
     def get_changed_source(self):
-        lines = self.pymodule.source_code.splitlines(True)
+        imports = [stmt for stmt in self.get_import_statements()
+                   if stmt.is_changed()]
+        after_removing = self._remove_imports(imports)
+        
         result = []
         last_index = 0
-        for import_statement in self.get_import_statements():
-            start = import_statement.start_line - 1
-            result.extend(lines[last_index:start])
-            last_index = import_statement.end_line - 1
-            if not import_statement.import_info.is_empty():
-                result.append(import_statement.get_import_statement() + '\n')
-        result.extend(lines[last_index:])
+        for stmt in sorted(imports, self._compare_import_locations):
+            start = self._get_import_location(stmt)
+            result.extend(after_removing[last_index:start - 1])
+            last_index = self._first_non_blank_line(after_removing, start - 1)
+            if not stmt.import_info.is_empty():
+                result.append(stmt.get_import_statement() + '\n')
+                for i in range(stmt.blank_lines):
+                    result.append('\n')
+        result.extend(after_removing[last_index:])
         return ''.join(result)
 
+    def _get_import_location(self, stmt):
+        start = stmt.get_new_start()
+        if start is None:
+            start = stmt.get_old_location()[0]
+        return start
+
+    def _compare_import_locations(self, stmt1, stmt2):
+        def get_location(stmt):
+            if stmt.get_new_start() is not None:
+                return stmt.get_new_start()
+            else:
+                return stmt.get_old_location()[0]
+        return cmp(get_location(stmt1), get_location(stmt2))
+
+    def _remove_imports(self, imports):
+        lines = self.pymodule.source_code.splitlines(True)
+        after_removing = []
+        last_index = 0
+        for stmt in imports:
+            start, end = stmt.get_old_location()
+            after_removing.extend(lines[last_index:start - 1])
+            last_index = end - 1
+            for i in range(start, end):
+                after_removing.append('')
+        after_removing.extend(lines[last_index:])
+        return after_removing
+    
+    def _first_non_blank_line(self, lines, lineno):
+        result = lineno
+        for line in lines[lineno:]:
+            if line.strip() == '':
+                result += 1
+            else:
+                break
+        return result
+
     def add_import(self, import_info):
         visitor = actions.AddingVisitor(self.pycore, import_info)
         for import_statement in self.get_import_statements():
             last_line = 1
             if all_imports:
                 last_line = all_imports[-1].end_line
-            all_imports.append(importinfo.ImportStatement(import_info, last_line, last_line))
+            all_imports.append(importinfo.ImportStatement(
+                               import_info, last_line, last_line))
 
     def filter_names(self, can_select):
         visitor = actions.RemovingVisitor(self.pycore, can_select)
 
     def get_relative_to_absolute_list(self):
         visitor = rope.refactor.importutils.actions.RelativeToAbsoluteVisitor(
-            self.pycore, self.pymodule.get_resource().get_parent())
+            self.pycore, self._current_folder())
         for import_stmt in self.get_import_statements():
             import_stmt.accept(visitor)
         return visitor.to_be_absolute
 
     def get_self_import_fix_and_rename_list(self):
         visitor = rope.refactor.importutils.actions.SelfImportVisitor(
-            self.pycore, self.pymodule.get_resource().get_parent(),
-            self.pymodule.get_resource())
+            self.pycore, self._current_folder(), self.pymodule.get_resource())
         for import_stmt in self.get_import_statements():
             import_stmt.accept(visitor)
         return visitor.to_be_fixed, visitor.to_be_renamed
 
+    def _current_folder(self):
+        return self.pymodule.get_resource().get_parent()
+
+    def sort_imports(self):
+        all_import_statements = self.get_import_statements()
+        visitor = actions.SortingVisitor(self.pycore, self._current_folder())
+        for import_statement in all_import_statements:
+            import_statement.accept(visitor)
+        last_index = 1
+        if all_import_statements:
+            last_index = all_import_statements[0].start_line
+        in_projects = sorted(visitor.in_project, self._compare_imports)
+        third_party = sorted(visitor.third_party, self._compare_imports)
+        standards = sorted(visitor.standard, self._compare_imports)
+        blank_lines = 1
+        if not in_projects and not third_party:
+            blank_lines = 2
+        last_index = self._move_imports(standards, last_index, blank_lines)
+        last_index = self._move_imports(third_party, last_index, blank_lines)
+        last_index = self._move_imports(in_projects, last_index, 2)
+
+    def _compare_imports(self, stmt1, stmt2):
+        str1 = stmt1.get_import_statement()
+        str2 = stmt2.get_import_statement()
+        if str1.startswith('from ') and not str2.startswith('from '):
+            return 1
+        if not str1.startswith('from ') and str2.startswith('from '):
+            return -1
+        return cmp(str1, str2)
+
+    def _move_imports(self, imports, index, blank_lines):
+        if imports:
+            for stmt in imports[:-1]:
+                stmt.move(index)
+                index += 1
+            imports[-1].move(index, blank_lines)
+            index += 1
+        return index
+
 
 class _OneTimeSelector(object):
 
         start_line = node.lineno
         import_statement = importinfo.ImportStatement(
             importinfo.NormalImport(node.names),start_line, end_line,
-            self._get_text(start_line, end_line))
+            self._get_text(start_line, end_line),
+            blank_lines=self._count_empty_lines_after(start_line))
         self.imports.append(import_statement)
+    
+    def _count_empty_lines_after(self, lineno):
+        result = 0
+        for current in range(lineno + 1, self.lines.length()):
+            line = self.lines.get_line(current)
+            if line.strip() == '':
+                result += 1
+            else:
+                break
+        return result
 
     def _get_text(self, start_line, end_line):
         result = []
         import_info = importinfo.FromImport(node.modname, level, 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)))
+        self.imports.append(importinfo.ImportStatement(
+                            import_info, node.lineno, end_line,
+                            self._get_text(start_line, end_line),
+                            blank_lines=self._count_empty_lines_after(start_line)))
 
     def find_import_statements(self):
         nodes = self.pymodule._get_ast().node.nodes

File rope/refactor/move.py

 import rope.base.codeanalyze
 import rope.base.pyobjects
-import rope.refactor.importutils
 import rope.base.exceptions
+from rope.refactor import importutils
 from rope.refactor import rename
 from rope.refactor import occurrences
 from rope.refactor.change import (ChangeSet, ChangeContents,
         self.old_pyname = pyname
         self.old_name = old_name
         self.new_name = new_name
-        self.import_tools = rope.refactor.importutils.ImportTools(self.pycore)
+        self.import_tools = importutils.ImportTools(self.pycore)
         self._check_exceptional_conditions()
 
     def _check_exceptional_conditions(self):
         old_name = pyname.get_object()._get_ast().name
         pymodule = pyname.get_object().get_module()
         source = pymodule.get_resource()
-        new_name = rope.refactor.importutils.get_module_name(
+        new_name = importutils.get_module_name(
             pycore, destination) + '.' + old_name
         if destination.is_folder() and destination.has_child('__init__.py'):
             destination = destination.get_child('__init__.py')
             old_name = source.get_name()
         else:
             old_name = source.get_name()[:-3]
-        package = rope.refactor.importutils.get_module_name(pycore, destination)
+        package = importutils.get_module_name(pycore, destination)
         if package:
             new_name = package + '.' + old_name
         else:
             new_name = old_name
         super(_ModuleMover, self).__init__(pycore, source, destination,
                                            pyname, old_name, new_name)
-        self.new_import = rope.refactor.importutils.NormalImport([(self.new_name, None)])
+        self.new_import = importutils.NormalImport([(self.new_name, None)])
 
     def _check_exceptional_conditions(self):
         moving_pyobject = self.old_pyname.get_object()

File rope/ui/codeassist.py

     toplevel.bind('<Escape>', close)
     toplevel.bind('<Control-g>', close)
     enhanced_list.list.focus_set()
-    toplevel.grab_set()
 
 
 # Registering code assist actions

File rope/ui/refactor.py

     if import_organizer:
         import_organizer.transform_relatives_to_absolute(file_editor.get_file())
 
+def sort_imports(context):
+    if not context.get_active_editor():
+        return
+    file_editor = context.get_active_editor()
+    import_organizer = _get_refactoring(context).get_import_organizer()
+    if import_organizer:
+        import_organizer.sort_imports(file_editor.get_file())
+
 def inline(context):
     if context.get_active_editor():
         fileeditor = context.get_active_editor()
                             ConfirmAllEditorsAreSaved(transform_froms_to_imports), None,
                             MenuAddress(['Refactor', 'Transform Froms to Imports'], 'r', 2),
                             ['python']))
+actions.append(SimpleAction('Sort Imports',
+                            ConfirmAllEditorsAreSaved(sort_imports), None,
+                            MenuAddress(['Refactor', 'Sort Imports'], None, 2),
+                            ['python']))
 actions.append(SimpleAction('Undo Refactoring',
                             ConfirmAllEditorsAreSaved(undo_refactoring), None,
                             MenuAddress(['Refactor', 'Undo Refactoring'], 'u', 3), ['python']))

File ropetest/refactor/importutilstest.py

         pymod = self.pycore.get_module('mod')
         module_with_imports = self.import_tools.get_module_with_imports(pymod)
         module_with_imports.remove_unused_imports()
-        self.assertEquals('\n', module_with_imports.get_changed_source())
+        self.assertEquals('', module_with_imports.get_changed_source())
 
     def test_simple_removing_unused_imports_for_froms(self):
         self.mod1.write('def a_func():\n    pass\ndef another_func():\n    pass\n')
         pymod = self.pycore.get_module('mod')
         module_with_imports = self.import_tools.get_module_with_imports(pymod)
         module_with_imports.remove_unused_imports()
-        self.assertEquals('\n', module_with_imports.get_changed_source())
+        self.assertEquals('', module_with_imports.get_changed_source())
 
     def test_simple_removing_unused_imports_for_nested_modules(self):
         self.mod1.write('def a_func():\n    pass\n')
         new_import = self.import_tools.get_import_for_module(
             self.pycore.resource_to_pyobject(self.mod1))
         module_with_imports.add_import(new_import)
-        self.assertEquals('import pkg1.mod1\n\n', module_with_imports.get_changed_source())
+        self.assertEquals('import pkg1.mod1\n', module_with_imports.get_changed_source())
 
     def test_adding_from_imports(self):
         self.mod1.write('def a_func():\n    pass\ndef another_func():\n    pass\n')
         self.assertEquals('a_var = 1\nprint a_var\n',
                           self.import_tools.organize_imports(pymod))
 
+    def test_sorting_empty_imports(self):
+        self.mod.write('')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('', self.import_tools.sort_imports(pymod))
+
+    def test_sorting_one_import(self):
+        self.mod.write('import pkg1.mod1\n\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import pkg1.mod1\n\n\n', self.import_tools.sort_imports(pymod))
+
+    def test_sorting_imports_alphabetically(self):
+        self.mod.write('import pkg2.mod2\nimport pkg1.mod1\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import pkg1.mod1\nimport pkg2.mod2\n\n\n',
+                          self.import_tools.sort_imports(pymod))
+
+    def test_sorting_imports_and_froms(self):
+        self.mod.write('import pkg2.mod2\nfrom pkg1 import mod1\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import pkg2.mod2\nfrom pkg1 import mod1\n\n\n',
+                          self.import_tools.sort_imports(pymod))
+
+    def test_sorting_imports_and_standard_modles(self):
+        self.mod.write('import pkg1\nimport sys\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import sys\n\nimport pkg1\n\n\n',
+                          self.import_tools.sort_imports(pymod))
+
+    def test_sorting_only_standard_modles(self):
+        self.mod.write('import sys\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import sys\n\n\n',
+                          self.import_tools.sort_imports(pymod))
+
+    def test_sorting_third_party(self):
+        self.mod.write('import pkg1\nimport a_third_party\n')
+        pymod = self.pycore.resource_to_pyobject(self.mod)
+        self.assertEquals('import a_third_party\n\nimport pkg1\n\n\n',
+                          self.import_tools.sort_imports(pymod))
+
 
 if __name__ == '__main__':
     unittest.main()

File ropetest/refactor/movetest.py

         self.mod1.write('a_var = 1\n')
         self.mod2.write('import mod1\ndef a_func():\n    var = mod1.a_var\n')
         self.refactoring.move(self.mod2, self.mod2.read().index('a_func') + 1, self.mod1)
-        self.assertEquals('\n\ndef a_func():\n    var = a_var\na_var = 1\n', self.mod1.read())
+        self.assertEquals('def a_func():\n    var = a_var\na_var = 1\n', self.mod1.read())
 
     def test_moving_resources_using_move_module_refactoring(self):
         self.mod1.write('a_var = 1')