Repository: timothycrosley/isort
Pull request #605 (2017-10-20 14:56:33)

Remove unconventional space after shebang
While allowed by most tools, it is very unconventional.


Patch:
diff --git a/.env b/.env
index 4f53fc5c..a7dbe409 100644
--- a/.env
+++ b/.env
@@ -16,18 +16,17 @@ if [ ! -d "venv" ]; then
      if ! hash pyvenv 2>/dev/null; then
         function pyvenv()
         {
-            if hash pyvenv-3.6 2>/dev/null; then
-                pyvenv-3.6 $@
-            elif hash pyvenv-3.5 2>/dev/null; then
+            if hash pyvenv-3.5 2>/dev/null; then
                 pyvenv-3.5 $@
-            elif hash pyvenv-3.4 2>/dev/null; then
+            fi
+            if hash pyvenv-3.4 2>/dev/null; then
                 pyvenv-3.4 $@
-            elif hash pyvenv-3.3 2>/dev/null; then
+            fi
+            if hash pyvenv-3.3 2>/dev/null; then
                 pyvenv-3.3 $@
-            elif hash pyvenv-3.2 2>/dev/null; then
+            fi
+            if hash pyvenv-3.2 2>/dev/null; then
                 pyvenv-3.2 $@
-            else
-                python3 -m venv $@
             fi
         }
     fi
@@ -86,12 +85,6 @@ function interact {
 }
 
 
-function open {
-    (root
-     $CODE_EDITOR isort/*.py setup.py test_*.py README.md tox.ini .gitignore CHANGELOG.md setup.cfg .editorconfig .env .coveragerc .travis.yml requirements.txt)
-}
-
-
 function _leave_project()
 {
     export PROJECT_NAME=""
@@ -106,7 +99,6 @@ function _leave_project()
 
     unset -f load
     unset -f interact
-    unset -f open
 
     deactivate
 }
diff --git a/.travis.yml b/.travis.yml
index d1cdbf23..e79ee219 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,12 @@
 sudo: false
 language: python
-cache: pip
 matrix:
   include:
   - env: TOXENV=isort-check
   - python: 3.6
     env: TOXENV=lint
+  - python: 2.6
+    env: TOXENV=py26
   - python: 2.7
     env: TOXENV=py27
   - python: 3.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee8501fc..747c03bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,19 +1,13 @@
 Changelog
 =========
 ### 4.3.0 - In progress
+- Fixed #559: Added `this` to known standard library
 - Fixed #557: `force_alphabetical_sort` and `force_sort_within_sections` can now be utilized together without extra new lines
-- Implemented `--version-number` to retrieve just the version number without the isort logo
 - Breaking changes
     - Python 2.7+ only (dropped 2.6) allowing various code simplifications and improvements.
 
-### 4.2.15 - June 6, 2017 - hotfix release
-IMPORTANT NOTE: This will be the last release with Python 2.6 support, subsequent releases will be 2.7+ only
-- Fixed certain one line imports not being successfully wrapped
-
-### 4.2.14 - June 5, 2017 - hotfix release
-- Fixed #559 & #565: Added missing standard library imports
-
 ### 4.2.13 - June 2, 2017 - hotfix release
+IMPORTANT NOTE: This will be the last release with Python 2.6 support, subsequent releases will be 2.7+ only
 - Fixed #553: Check only and --diff now work together again
 
 ### 4.2.12 - June 1, 2017 - hotfix release
diff --git a/README.rst b/README.rst
index bc83c515..f185178d 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,7 @@
 ########
 
 .. image:: https://badge.fury.io/py/isort.svg
-    :target: https://badge.fury.io/py/isort
+    :target: http://badge.fury.io/py/isort
     :alt: PyPI version
 
 .. image:: https://travis-ci.org/timothycrosley/isort.svg?branch=master
@@ -29,7 +29,7 @@ isort your python imports for you so you don't have to.
 
 isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections.
 It provides a command line utility, Python library and `plugins for various editors <https://github.com/timothycrosley/isort/wiki/isort-Plugins>`_ to quickly sort all your imports.
-It currently cleanly supports Python 2.7 - 3.6 without any dependencies.
+It currently cleanly supports Python 2.6 - 3.5 without any dependencies.
 
 .. image:: https://raw.github.com/timothycrosley/isort/develop/example.gif
    :alt: Example Usage
diff --git a/appveyor.yml b/appveyor.yml
index 3b6746b7..003d2e58 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,4 +9,4 @@ install:
   - pip install tox
 
 test_script:
-  - tox -e isort-check,py27,py33,py34,py35,py36
+  - tox -e isort-check,py26,py27,py33,py34,py35,py36
diff --git a/isort/__init__.py b/isort/__init__.py
index 13a8549a..6501ebd2 100644
--- a/isort/__init__.py
+++ b/isort/__init__.py
@@ -25,4 +25,4 @@ from __future__ import absolute_import, division, print_function, unicode_litera
 from . import settings  # noqa: F401
 from .isort import SortImports  # noqa: F401
 
-__version__ = "4.2.14"
+__version__ = "4.2.13"
diff --git a/isort/isort.py b/isort/isort.py
index b25318ed..16b40b73 100644
--- a/isort/isort.py
+++ b/isort/isort.py
@@ -32,8 +32,7 @@ import itertools
 import os
 import re
 import sys
-import sysconfig
-from collections import OrderedDict, namedtuple
+from collections import namedtuple
 from datetime import datetime
 from difflib import unified_diff
 from fnmatch import fnmatch
@@ -41,7 +40,7 @@ from glob import glob
 
 from . import settings
 from .natural import nsorted
-from .pie_slice import OrderedSet, input, itemsview
+from .pie_slice import OrderedDict, OrderedSet, input, itemsview
 
 KNOWN_SECTION_MAPPING = {
     'STDLIB': 'STANDARD_LIBRARY',
@@ -88,8 +87,6 @@ class SortImports(object):
                 indent = "\t"
         self.config['indent'] = indent
 
-        self.config['comment_prefix'] = self.config['comment_prefix'].strip("'").strip('"')
-
         self.place_imports = {}
         self.import_placements = {}
         self.remove_imports = [self._format_simplified(removal) for removal in self.config['remove_imports']]
@@ -115,7 +112,6 @@ class SortImports(object):
                     file_contents = file_to_import_sort.read()
 
         if file_contents is None or ("isort:" + "skip_file") in file_contents:
-            self.skipped = True
             return
 
         self.in_lines = file_contents.split("\n")
@@ -197,7 +193,7 @@ class SortImports(object):
                 self._show_diff(file_contents)
                 answer = None
                 while answer not in ('yes', 'y', 'no', 'n', 'quit', 'q'):
-                    answer = input("Apply suggested changes to '{0}' [y/n/q]? ".format(self.file_path)).lower()
+                    answer = input("Apply suggested changes to '{0}' [y/n/q]?".format(self.file_path)).lower()
                     if answer in ('no', 'n'):
                         return
                     if answer in ('quit', 'q'):
@@ -265,7 +261,7 @@ class SortImports(object):
             virtual_env_src = '{0}/src/'.format(virtual_env)
 
         # handle case-insensitive paths on windows
-        stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()['stdlib'])
+        stdlib_lib_prefix = os.path.normcase(get_stdlib_path())
 
         for prefix in paths:
             module_path = "/".join((prefix, module_name.replace(".", "/")))
@@ -327,8 +323,7 @@ class SortImports(object):
         """
             Returns a string with comments added
         """
-        return comments and "{0}{1} {2}".format(self._strip_comments(original_string)[0],
-                                                self.config['comment_prefix'],
+        return comments and "{0}  # {1}".format(self._strip_comments(original_string)[0],
                                                 "; ".join(comments)) or original_string
 
     def _wrap(self, line):
@@ -337,16 +332,10 @@ class SortImports(object):
         """
         wrap_mode = self.config['multi_line_output']
         if len(line) > self.config['line_length'] and wrap_mode != settings.WrapModes.NOQA:
-            line_without_comment = line
-            comment = None
-            if '#' in line:
-                line_without_comment, comment = line.split('#', 1)
-            for splitter in ("import ", ".", "as "):
+            for splitter in ("import", ".", "as"):
                 exp = r"\b" + re.escape(splitter) + r"\b"
-                if re.search(exp, line_without_comment) and not line_without_comment.strip().startswith(splitter):
-                    line_parts = re.split(exp, line_without_comment)
-                    if comment:
-                        line_parts[-1] = '{0}#{1}'.format(line_parts[-1], comment)
+                if re.search(exp, line) and not line.strip().startswith(splitter):
+                    line_parts = re.split(exp, line)
                     next_line = []
                     while (len(line) + 2) > (self.config['wrap_length'] or self.config['line_length']) and line_parts:
                         next_line.append(line_parts.pop())
@@ -356,7 +345,7 @@ class SortImports(object):
 
                     cont_line = self._wrap(self.config['indent'] + splitter.join(next_line).lstrip())
                     if self.config['use_parentheses']:
-                        output = "{0}{1}(\n{2}{3}{4})".format(
+                        output = "{0}{1} (\n{2}{3}{4})".format(
                             line, splitter, cont_line,
                             "," if self.config['include_trailing_comma'] else "",
                             "\n" if wrap_mode in (
@@ -364,14 +353,14 @@ class SortImports(object):
                                 settings.WrapModes.VERTICAL_GRID_GROUPED,
                             ) else "")
                         lines = output.split('\n')
-                        if self.config['comment_prefix'] in lines[-1] and lines[-1].endswith(')'):
-                            line, comment = lines[-1].split(self.config['comment_prefix'], 1)
-                            lines[-1] = line + ')' + self.config['comment_prefix'] + comment[:-1]
+                        if '  #' in lines[-1] and lines[-1].endswith(')'):
+                            line, comment = lines[-1].split('  #', 1)
+                            lines[-1] = line + ')  #' + comment[:-1]
                         return '\n'.join(lines)
-                    return "{0}{1}\\\n{2}".format(line, splitter, cont_line)
+                    return "{0}{1} \\\n{2}".format(line, splitter, cont_line)
         elif len(line) > self.config['line_length'] and wrap_mode == settings.WrapModes.NOQA:
             if "# NOQA" not in line:
-                return "{0}{1} NOQA".format(line, self.config['comment_prefix'])
+                return "{0}  # NOQA".format(line)
 
         return line
 
@@ -402,43 +391,37 @@ class SortImports(object):
                 from_imports = [line for line in from_imports if not "{0}.{1}".format(module, line) in
                                 self.remove_imports]
 
-            sub_modules = ['{0}.{1}'.format(module, from_import) for from_import in from_imports]
-            as_imports = dict((from_import, "{0} as {1}".format(from_import, self.as_map[sub_module])) for
-                              from_import, sub_module in zip(from_imports, sub_modules) if sub_module in self.as_map)
-            if self.config['combine_as_imports'] and not ("*" in from_imports and self.config['combine_star']):
-                for from_import in copy.copy(from_imports):
-                    if from_import in as_imports:
-                        from_imports[from_imports.index(from_import)] = as_imports.pop(from_import)
-
-            while from_imports:
+            for from_import in copy.copy(from_imports):
+                submodule = module + "." + from_import
+                import_as = self.as_map.get(submodule, False)
+                if import_as:
+                    import_definition = "{0} as {1}".format(from_import, import_as)
+                    if self.config['combine_as_imports'] and not ("*" in from_imports and
+                                                                    self.config['combine_star']):
+                        from_imports[from_imports.index(from_import)] = import_definition
+                    else:
+                        import_statement = import_start + import_definition
+                        force_grid_wrap = self.config['force_grid_wrap']
+                        comments = self.comments['straight'].get(submodule)
+                        import_statement = self._add_comments(comments, self._wrap(import_statement))
+                        from_imports.remove(from_import)
+                        section_output.append(import_statement)
+
+            if from_imports:
                 comments = self.comments['from'].pop(module, ())
                 if "*" in from_imports and self.config['combine_star']:
                     import_statement = self._wrap(self._add_comments(comments, "{0}*".format(import_start)))
-                    from_imports = None
                 elif self.config['force_single_line']:
                     import_statements = []
-                    while from_imports:
-                        from_import = from_imports.pop(0)
-                        if from_import in as_imports:
-                            from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import))
-                            import_statements.append(self._add_comments(from_comments,
-                                                     self._wrap(import_start + as_imports[from_import])))
-                            continue
+                    for from_import in from_imports:
                         single_import_line = self._add_comments(comments, import_start + from_import)
                         comment = self.comments['nested'].get(module, {}).pop(from_import, None)
                         if comment:
-                            single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'],
-                                                                   comment)
+                            single_import_line += "{0} {1}".format(comments and ";" or "  #", comment)
                         import_statements.append(self._wrap(single_import_line))
                         comments = None
                     import_statement = "\n".join(import_statements)
                 else:
-                    while from_imports and from_imports[0] in as_imports:
-                        from_import = from_imports.pop(0)
-                        from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import))
-                        section_output.append(self._add_comments(from_comments,
-                                                                 self._wrap(import_start + as_imports[from_import])))
-
                     star_import = False
                     if "*" in from_imports:
                         section_output.append(self._add_comments(comments, "{0}*".format(import_start)))
@@ -447,13 +430,10 @@ class SortImports(object):
                         comments = None
 
                     for from_import in copy.copy(from_imports):
-                        if from_import in as_imports:
-                            continue
                         comment = self.comments['nested'].get(module, {}).pop(from_import, None)
                         if comment:
                             single_import_line = self._add_comments(comments, import_start + from_import)
-                            single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'],
-                                                                   comment)
+                            single_import_line += "{0} {1}".format(comments and ";" or "  #", comment)
                             above_comments = self.comments['above']['from'].pop(module, None)
                             if above_comments:
                                 section_output.extend(above_comments)
@@ -461,32 +441,29 @@ class SortImports(object):
                             from_imports.remove(from_import)
                             comments = None
 
-                    from_import_section = []
-                    while from_imports and from_imports[0] not in as_imports:
-                        from_import_section.append(from_imports.pop(0))
                     if star_import:
-                        import_statement = import_start + (", ").join(from_import_section)
+                        import_statement = import_start + (", ").join(from_imports)
                     else:
-                        import_statement = self._add_comments(comments, import_start + (", ").join(from_import_section))
-                    if not from_import_section:
+                        import_statement = self._add_comments(comments, import_start + (", ").join(from_imports))
+                    if not from_imports:
                         import_statement = ""
 
                     do_multiline_reformat = False
 
                     force_grid_wrap = self.config['force_grid_wrap']
-                    if force_grid_wrap and len(from_import_section) >= force_grid_wrap:
+                    if force_grid_wrap and len(from_imports) >= force_grid_wrap:
                         do_multiline_reformat = True
 
-                    if len(import_statement) > self.config['line_length'] and len(from_import_section) > 1:
+                    if len(import_statement) > self.config['line_length'] and len(from_imports) > 1:
                         do_multiline_reformat = True
 
                     # If line too long AND have imports AND we are NOT using GRID or VERTICAL wrap modes
-                    if (len(import_statement) > self.config['line_length'] and len(from_import_section) > 0 and
+                    if (len(import_statement) > self.config['line_length'] and len(from_imports) > 0 and
                             self.config['multi_line_output'] not in (1, 0)):
                         do_multiline_reformat = True
 
                     if do_multiline_reformat:
-                        import_statement = self._multi_line_reformat(import_start, from_import_section, comments)
+                        import_statement = self._multi_line_reformat(import_start, from_imports, comments)
                     if not do_multiline_reformat and len(import_statement) > self.config['line_length']:
                         import_statement = self._wrap(import_statement)
 
@@ -508,7 +485,7 @@ class SortImports(object):
             lines = import_statement.split("\n")
             line_count = len(lines)
             if len(lines) > 1:
-                minimum_length = min(len(line) for line in lines[:-1])
+                minimum_length = min([len(line) for line in lines[:-1]])
             else:
                 minimum_length = 0
             new_import_statement = import_statement
@@ -519,8 +496,6 @@ class SortImports(object):
                 new_import_statement = formatter(import_start, copy.copy(from_imports),
                                                 dynamic_indent, indent, line_length, comments)
                 lines = new_import_statement.split("\n")
-        if import_statement.count('\n') == 0:
-            return self._wrap(import_statement)
         return import_statement
 
     def _add_formatted_imports(self):
@@ -703,18 +678,18 @@ class SortImports(object):
         retval = '{0}{1}'.format(statement, ', '.join(imports))
         comment_str = ' '.join(comments)
         if comments:
-            if len(retval) + len(self.config['comment_prefix']) + 1 + len(comment_str) <= line_length:
-                return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str)
+            if len(retval) + 4 + len(comment_str) <= line_length:
+                return '{0}  # {1}'.format(retval, comment_str)
         else:
             if len(retval) <= line_length:
                 return retval
         if comments:
             if "NOQA" in comments:
-                return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str)
+                return '{0}  # {1}'.format(retval, comment_str)
             else:
-                return '{0}{1} NOQA {2}'.format(retval, self.config['comment_prefix'], comment_str)
+                return '{0}  # NOQA {1}'.format(retval, comment_str)
         else:
-            return '{0}{1} NOQA'.format(retval, self.config['comment_prefix'])
+            return '{0}  # NOQA'.format(retval)
 
     @staticmethod
     def _strip_comments(line, comments=None):
@@ -889,8 +864,6 @@ class SortImports(object):
                 if import_type == "from":
                     import_from = imports.pop(0)
                     placed_module = self.place_module(import_from)
-                    if self.config['verbose']:
-                        print("from-type place_module for %s returned %s" % (import_from, placed_module))
                     if placed_module == '':
                         print(
                             "WARNING: could not place module {0} of line {1} --"
@@ -941,8 +914,6 @@ class SortImports(object):
                             if self.index - 1 == self.import_index:
                                 self.import_index -= len(self.comments['above']['straight'].get(module, []))
                         placed_module = self.place_module(module)
-                        if self.config['verbose']:
-                            print("else-type place_module for %s returned %s" % (module, placed_module))
                         if placed_module == '':
                             print(
                                 "WARNING: could not place module {0} of line {1} --"
@@ -969,6 +940,18 @@ def coding_check(fname, default='utf-8'):
     return coding
 
 
+def get_stdlib_path():
+    """Returns the path to the standard lib for the current path installation.
+
+    This function can be dropped and "sysconfig.get_paths()" used directly once Python 2.6 support is dropped.
+    """
+    if sys.version_info >= (2, 7):
+        import sysconfig
+        return sysconfig.get_paths()['stdlib']
+    else:
+        return os.path.join(sys.prefix, 'lib')
+
+
 def exists_case_sensitive(path):
     """
     Returns if the given path exists and also matches the case on Windows.
diff --git a/isort/main.py b/isort/main.py
index d950f77b..f17e6c20 100755
--- a/isort/main.py
+++ b/isort/main.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
 '''  Tool for sorting imports alphabetically, and automatically separated into sections.
 
 Copyright (C) 2013  Timothy Edmund Crosley
@@ -211,8 +211,6 @@ def create_parser():
                         help="Combines as imports on the same line.")
     parser.add_argument('-tc', '--trailing-comma', dest='include_trailing_comma', action='store_true',
                         help='Includes a trailing comma on multi line imports that include parentheses.')
-    parser.add_argument('-vn', '--version-number', action='version', version=__version__,
-                        help='Returns just the current version number without the logo')
     parser.add_argument('-v', '--version', action='store_true', dest='show_version')
     parser.add_argument('-vb', '--verbose', action='store_true', dest="verbose",
                         help='Shows verbose output, such as when files are skipped or when a check is successful.')
@@ -235,11 +233,10 @@ def create_parser():
     parser.add_argument('-fss', '--force-sort-within-sections', action='store_true', dest="force_sort_within_sections",
                         help='Force imports to be sorted by module, independent of import_type')
     parser.add_argument('-lbt', '--lines-between-types', dest='lines_between_types', type=int)
-    parser.add_argument('-lai', '--lines-after-imports', dest='lines_after_imports', type=int)
     parser.add_argument('-up', '--use-parentheses', dest='use_parentheses', action='store_true',
-                        help='Use parenthesis for line continuation on length limit instead of slashes.')
+                        help='Use parenthesis for line continuation on lenght limit instead of slashes.')
 
-    arguments = {key: value for key, value in itemsview(vars(parser.parse_args())) if value}
+    arguments = dict((key, value) for (key, value) in itemsview(vars(parser.parse_args())) if value)
     if 'dont_order_by_type' in arguments:
         arguments['order_by_type'] = False
     return arguments
diff --git a/isort/natural.py b/isort/natural.py
index c02b42c3..aac8c4a3 100644
--- a/isort/natural.py
+++ b/isort/natural.py
@@ -8,7 +8,7 @@ usage:
 Copyright (C) 2013  Timothy Edmund Crosley
 
 Implementation originally from @HappyLeapSecond stack overflow user in response to:
-   https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
+   http://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 documentation files (the "Software"), to deal in the Software without restriction, including without limitation
diff --git a/isort/pie_slice.py b/isort/pie_slice.py
index 178cf33e..0122a484 100644
--- a/isort/pie_slice.py
+++ b/isort/pie_slice.py
@@ -21,6 +21,7 @@ OTHER DEALINGS IN THE SOFTWARE.
 """
 from __future__ import absolute_import
 
+import abc
 import collections
 import sys
 
@@ -44,7 +45,7 @@ native_object = object
 
 common = ['native_dict', 'native_round', 'native_filter', 'native_map', 'native_range', 'native_str', 'native_chr',
           'native_input', 'PY2', 'PY3', 'u', 'itemsview', 'valuesview', 'keysview', 'execute', 'integer_types',
-          'native_next', 'native_object', 'with_metaclass', 'lru_cache']
+          'native_next', 'native_object', 'with_metaclass', 'OrderedDict', 'lru_cache']
 
 
 def with_metaclass(meta, *bases):
@@ -82,12 +83,32 @@ def unmodified_isinstance(*bases):
 
     """
     class UnmodifiedIsInstance(type):
-        @classmethod
-        def __instancecheck__(cls, instance):
-            if cls.__name__ in (str(base.__name__) for base in bases):
-                return isinstance(instance, bases)
+        if sys.version_info[0] == 2 and sys.version_info[1] <= 6:
+
+            @classmethod
+            def __instancecheck__(cls, instance):
+                if cls.__name__ in (str(base.__name__) for base in bases):
+                    return isinstance(instance, bases)
+
+                subclass = getattr(instance, '__class__', None)
+                subtype = type(instance)
+                instance_type = getattr(abc, '_InstanceType', None)
+                if not instance_type:
+                    class test_object:
+                        pass
+                    instance_type = type(test_object)
+                if subtype is instance_type:
+                    subtype = subclass
+                if subtype is subclass or subclass is None:
+                    return cls.__subclasscheck__(subtype)
+                return (cls.__subclasscheck__(subclass) or cls.__subclasscheck__(subtype))
+        else:
+            @classmethod
+            def __instancecheck__(cls, instance):
+                if cls.__name__ in (str(base.__name__) for base in bases):
+                    return isinstance(instance, bases)
 
-            return type.__instancecheck__(cls, instance)
+                return type.__instancecheck__(cls, instance)
 
     return with_metaclass(UnmodifiedIsInstance, *bases)
 
@@ -127,7 +148,7 @@ if PY3:
 else:
     from itertools import ifilter as filter  # noqa: F401
     from itertools import imap as map  # noqa: F401
-    from itertools import izip as zip  # noqa: F401
+    from itertools import izip as zip
     from decimal import Decimal, ROUND_HALF_EVEN
 
     str = unicode
@@ -257,10 +278,28 @@ else:
             dct['__str__'] = lambda self: self.__unicode__().encode('utf-8')
             return type.__new__(cls, name, bases, dct)
 
-        def __instancecheck__(cls, instance):
-            if cls.__name__ == "object":
-                return isinstance(instance, native_object)
-            return type.__instancecheck__(cls, instance)
+        if sys.version_info[1] <= 6:
+            def __instancecheck__(cls, instance):
+                if cls.__name__ == "object":
+                    return isinstance(instance, native_object)
+
+                subclass = getattr(instance, '__class__', None)
+                subtype = type(instance)
+                instance_type = getattr(abc, '_InstanceType', None)
+                if not instance_type:
+                    class test_object:
+                        pass
+                    instance_type = type(test_object)
+                if subtype is instance_type:
+                    subtype = subclass
+                if subtype is subclass or subclass is None:
+                    return cls.__subclasscheck__(subtype)
+                return (cls.__subclasscheck__(subclass) or cls.__subclasscheck__(subtype))
+        else:
+            def __instancecheck__(cls, instance):
+                if cls.__name__ == "object":
+                    return isinstance(instance, native_object)
+                return type.__instancecheck__(cls, instance)
 
     class object(with_metaclass(FixStr, object)):
         pass
@@ -268,6 +307,138 @@ else:
     __all__ = common + ['round', 'dict', 'apply', 'cmp', 'coerce', 'execfile', 'raw_input', 'unpacks', 'str', 'chr',
                         'input', 'range', 'filter', 'map', 'zip', 'object']
 
+if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+    # OrderedDict
+    # Copyright (c) 2009 Raymond Hettinger
+    #
+    # Permission is hereby granted, free of charge, to any person
+    # obtaining a copy of this software and associated documentation files
+    # (the "Software"), to deal in the Software without restriction,
+    # including without limitation the rights to use, copy, modify, merge,
+    # publish, distribute, sublicense, and/or sell copies of the Software,
+    # and to permit persons to whom the Software is furnished to do so,
+    # subject to the following conditions:
+    #
+    #     The above copyright notice and this permission notice shall be
+    #     included in all copies or substantial portions of the Software.
+    #
+    #     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    #     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+    #     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    #     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    #     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    #     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    #     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+    #     OTHER DEALINGS IN THE SOFTWARE.
+
+    from UserDict import DictMixin
+
+    class OrderedDict(dict, DictMixin):
+
+        def __init__(self, *args, **kwds):
+            if len(args) > 1:
+                raise TypeError('expected at most 1 arguments, got %d' % len(args))
+            try:
+                self.__end
+            except AttributeError:
+                self.clear()
+            self.update(*args, **kwds)
+
+        def clear(self):
+            self.__end = end = []
+            end += [None, end, end]         # sentinel node for doubly linked list
+            self.__map = {}                 # key --> [key, prev, next]
+            dict.clear(self)
+
+        def __setitem__(self, key, value):
+            if key not in self:
+                end = self.__end
+                curr = end[1]
+                curr[2] = end[1] = self.__map[key] = [key, curr, end]
+            dict.__setitem__(self, key, value)
+
+        def __delitem__(self, key):
+            dict.__delitem__(self, key)
+            key, prev, next = self.__map.pop(key)
+            prev[2] = next
+            next[1] = prev
+
+        def __iter__(self):
+            end = self.__end
+            curr = end[2]
+            while curr is not end:
+                yield curr[0]
+                curr = curr[2]
+
+        def __reversed__(self):
+            end = self.__end
+            curr = end[1]
+            while curr is not end:
+                yield curr[0]
+                curr = curr[1]
+
+        def popitem(self, last=True):
+            if not self:
+                raise KeyError('dictionary is empty')
+            if last:
+                key = reversed(self).next()
+            else:
+                key = iter(self).next()
+            value = self.pop(key)
+            return key, value
+
+        def __reduce__(self):
+            items = [[k, self[k]] for k in self]
+            tmp = self.__map, self.__end
+            del self.__map, self.__end
+            inst_dict = vars(self).copy()
+            self.__map, self.__end = tmp
+            if inst_dict:
+                return (self.__class__, (items,), inst_dict)
+            return self.__class__, (items,)
+
+        def keys(self):
+            return list(self)
+
+        setdefault = DictMixin.setdefault
+        update = DictMixin.update
+        pop = DictMixin.pop
+        values = DictMixin.values
+        items = DictMixin.items
+        iterkeys = DictMixin.iterkeys
+        itervalues = DictMixin.itervalues
+        iteritems = DictMixin.iteritems
+
+        def __repr__(self):
+            if not self:
+                return '%s()' % (self.__class__.__name__,)
+            return '%s(%r)' % (self.__class__.__name__, self.items())
+
+        def copy(self):
+            return self.__class__(self)
+
+        @classmethod
+        def fromkeys(cls, iterable, value=None):
+            d = cls()
+            for key in iterable:
+                d[key] = value
+            return d
+
+        def __eq__(self, other):
+            if isinstance(other, OrderedDict):
+                if len(self) != len(other):
+                    return False
+                for p, q in zip(self.items(), other.items()):
+                    if p != q:
+                        return False
+                return True
+            return dict.__eq__(self, other)
+
+        def __ne__(self, other):
+            return not self == other
+else:
+    from collections import OrderedDict
+
 
 if sys.version_info < (3, 2):
     try:
@@ -289,7 +460,7 @@ if sys.version_info < (3, 2):
         View the cache statistics named tuple (hits, misses, maxsize, currsize) with
         f.cache_info().  Clear the cache and statistics with f.cache_clear().
         Access the underlying function with f.__wrapped__.
-        See: https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
+        See:  http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
 
         """
         def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
@@ -298,7 +469,7 @@ if sys.version_info < (3, 2):
             lock = Lock()
 
             if maxsize is None:
-                CACHE = {}
+                CACHE = dict()
 
                 @wraps(user_function)
                 def wrapper(*args, **kwds):
@@ -316,7 +487,7 @@ if sys.version_info < (3, 2):
                     misses[0] += 1
                     return result
             else:
-                CACHE = collections.OrderedDict()
+                CACHE = OrderedDict()
 
                 @wraps(user_function)
                 def wrapper(*args, **kwds):
diff --git a/isort/settings.py b/isort/settings.py
index 66155e08..a7dc2c7b 100644
--- a/isort/settings.py
+++ b/isort/settings.py
@@ -25,7 +25,6 @@ OTHER DEALINGS IN THE SOFTWARE.
 from __future__ import absolute_import, division, print_function, unicode_literals
 
 import fnmatch
-import io
 import os
 import posixpath
 from collections import namedtuple
@@ -52,57 +51,44 @@ default = {'force_to_top': [],
            'sections': DEFAULT_SECTIONS,
            'no_sections': False,
            'known_future_library': ['__future__'],
-           'known_standard_library': ['AL', 'BaseHTTPServer', 'Bastion', 'CGIHTTPServer', 'Carbon', 'ColorPicker',
-                                      'ConfigParser', 'Cookie', 'DEVICE', 'DocXMLRPCServer', 'EasyDialogs', 'FL',
-                                      'FrameWork', 'GL', 'HTMLParser', 'MacOS', 'MimeWriter', 'MiniAEFrame', 'Nav',
-                                      'PixMapWrapper', 'Queue', 'SUNAUDIODEV', 'ScrolledText', 'SimpleHTTPServer',
-                                      'SimpleXMLRPCServer', 'SocketServer', 'StringIO', 'Tix', 'Tkinter', 'UserDict',
-                                      'UserList', 'UserString', 'W', '__builtin__', 'abc', 'aepack', 'aetools',
-                                      'aetypes', 'aifc', 'al', 'anydbm', 'applesingle', 'argparse', 'array', 'ast',
-                                      'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'autoGIL', 'base64',
-                                      'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'buildtools', 'builtins',
-                                      'bz2', 'cPickle', 'cProfile', 'cStringIO', 'calendar', 'cd', 'cfmfile', 'cgi',
-                                      'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections',
-                                      'colorsys', 'commands', 'compileall', 'compiler', 'concurrent', 'configparser',
-                                      'contextlib', 'cookielib', 'copy', 'copy_reg', 'copyreg', 'crypt', 'csv',
-                                      'ctypes', 'curses', 'datetime', 'dbhash', 'dbm', 'decimal', 'difflib',
-                                      'dircache', 'dis', 'distutils', 'dl', 'doctest', 'dumbdbm', 'dummy_thread',
-                                      'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno',
-                                      'exceptions', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'findertools',
-                                      'fl', 'flp', 'fm', 'fnmatch', 'formatter', 'fpectl', 'fpformat', 'fractions',
-                                      'ftplib', 'functools', 'future_builtins', 'gc', 'gdbm', 'gensuitemodule',
-                                      'getopt', 'getpass', 'gettext', 'gl', 'glob', 'grp', 'gzip', 'hashlib',
-                                      'heapq', 'hmac', 'hotshot', 'html', 'htmlentitydefs', 'htmllib', 'http',
-                                      'httplib', 'ic', 'icopen', 'imageop', 'imaplib', 'imgfile', 'imghdr', 'imp',
-                                      'importlib', 'imputil', 'inspect', 'io', 'ipaddress', 'itertools', 'jpeg',
-                                      'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma',
-                                      'macerrors', 'macostools', 'macpath', 'macresource', 'mailbox', 'mailcap',
-                                      'marshal', 'math', 'md5', 'mhlib', 'mimetools', 'mimetypes', 'mimify', 'mmap',
-                                      'modulefinder', 'msilib', 'msvcrt', 'multifile', 'multiprocessing', 'mutex',
-                                      'netrc', 'new', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os',
-                                      'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes',
-                                      'pkgutil', 'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile',
-                                      'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc',
-                                      'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rexec',
-                                      'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets', 'select',
-                                      'selectors', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'signal',
-                                      'site', 'sitecustomize', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver',
-                                      'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'statvfs', 'string', 'stringprep',
-                                      'struct', 'subprocess', 'sunau', 'sunaudiodev', 'symbol', 'symtable', 'sys',
-                                      'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios',
-                                      'test', 'textwrap', 'this', 'thread', 'threading', 'time', 'timeit', 'tkinter',
-                                      'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'ttk', 'tty', 'turtle',
-                                      'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2',
-                                      'urlparse', 'user', 'usercustomize', 'uu', 'uuid', 'venv', 'videoreader',
-                                      'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb', 'winreg', 'winsound',
-                                      'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile',
-                                      'zipimport', 'zlib'],
+           'known_standard_library': ['BaseHTTPServer', 'ConfigParser', 'Cookie', 'EasyDialogs', 'Queue',
+                                      'SimpleXMLRPCServer', 'SocketServer', 'StringIO', '__builtin__', 'abc',
+                                      'aifc', 'anydbm', 'argparse', 'array', 'ast', 'asynchat', 'asyncio',
+                                      'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex',
+                                      'bisect', 'builtins', 'bz2', 'cPickle', 'cProfile', 'cStringIO', 'calendar',
+                                      'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop',
+                                      'collections', 'colorsys', 'commands', 'compileall', 'concurrent', 'configparser',
+                                      'contextlib', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'datetime',
+                                      'dbhash', 'dbm', 'decimal', 'difflib', 'dircache', 'dis', 'distutils', 'doctest',
+                                      'dumbdbm', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno',
+                                      'exceptions', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch',
+                                      'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'gdbm', 'getopt',
+                                      'getpass', 'gettext', 'glob', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html',
+                                      'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress',
+                                      'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging',
+                                      'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mhlib', 'mimetypes',
+                                      'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis',
+                                      'nntplib', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser',
+                                      'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform',
+                                      'plistlib', 'poplib', 'posix', 'pprint', 'profile', 'pstats', 'pty', 'pwd',
+                                      'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline',
+                                      'reprlib', 'resource', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets',
+                                      'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site',
+                                      'sitecustomize', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver',
+                                      'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'string', 'stringprep',
+                                      'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig',
+                                      'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test',
+                                      'textwrap', 'this', 'thread', 'threading', 'time', 'timeit', 'tkinter', 'token',
+                                      'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo',
+                                      'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2', 'urlparse',
+                                      'usercustomize', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref',
+                                      'webbrowser', 'whichdb', 'winreg', 'winsound', 'wsgiref', 'xdrlib',
+                                      'xml', 'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile', 'zipimport', 'zlib'],
            'known_third_party': ['google.appengine.api'],
            'known_first_party': [],
            'multi_line_output': WrapModes.GRID,
            'forced_separate': [],
            'indent': ' ' * 4,
-           'comment_prefix': '  #',
            'length_sort': False,
            'add_imports': [],
            'remove_imports': [],
@@ -216,7 +202,7 @@ def _as_list(value):
 
 @lru_cache()
 def _get_config_data(file_path, sections):
-    with io.open(file_path, 'r') as config_file:
+    with open(file_path, 'rU') as config_file:
         if file_path.endswith('.editorconfig'):
             line = '\n'
             last_position = config_file.tell()
@@ -229,7 +215,7 @@ def _get_config_data(file_path, sections):
 
         config = configparser.SafeConfigParser()
         config.readfp(config_file)
-        settings = {}
+        settings = dict()
         for section in sections:
             if config.has_section(section):
                 settings.update(dict(config.items(section)))
diff --git a/setup.py b/setup.py
index eb266a34..cdf65c9a 100755
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@ with open('README.rst', 'r') as f:
     readme = f.read()
 
 setup(name='isort',
-      version='4.2.14',
+      version='4.2.13',
       description='A Python utility / library to sort Python imports.',
       long_description=readme,
       author='Timothy Crosley',
@@ -63,6 +63,7 @@ setup(name='isort',
                    'License :: OSI Approved :: MIT License',
                    'Programming Language :: Python',
                    'Programming Language :: Python :: 2',
+                   'Programming Language :: Python :: 2.6',
                    'Programming Language :: Python :: 2.7',
                    'Programming Language :: Python :: 3',
                    'Programming Language :: Python :: 3.3',
diff --git a/tox.ini b/tox.ini
index 7c70f99e..b5996f69 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,15 +1,26 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
 [tox]
 envlist =
     isort-check,
-    py27, py33, py34, py35, py36, pypy,
+    py26, py27, py33, py34, py35, py36, pypy,
     lint
 
 [testenv]
-deps = pytest
-commands = py.test test_isort.py {posargs}
+commands =
+    py.test test_isort.py {posargs}
+deps =
+    six
+    pytest
 
 [testenv:isort-check]
-commands = python setup.py isort
+commands =
+    python setup.py isort
+deps =
+    six
 
 [testenv:lint]
 basepython = python2.7


Test patch:
diff --git a/test_isort.py b/test_isort.py
index 2b792abd..cfa6202c 100644
--- a/test_isort.py
+++ b/test_isort.py
@@ -506,15 +506,6 @@ def test_skip_with_file_name():
     assert skipped
 
 
-def test_skip_within_file():
-    """Ensure skipping a whole file works."""
-    test_input = ("# isort:skip_file\n"
-                  "import django\n"
-                  "import myproject\n")
-
-    assert SortImports(file_contents=test_input, known_third_party=['django']).skipped
-
-
 def test_force_to_top():
     """Ensure forcing a single import to the top of its category works as expected."""
     test_input = ("import lib6\n"
@@ -754,16 +745,12 @@ def test_default_section():
 
 def test_first_party_overrides_standard_section():
     """Test to ensure changing the default section works as expected."""
-    test_input = ("from HTMLParser import HTMLParseError, HTMLParser\n"
-                  "import sys\n"
+    test_input = ("import sys\n"
                   "import os\n"
-                  "import this\n"
                   "import profile.test\n")
     test_output = SortImports(file_contents=test_input, known_first_party=['profile']).output
     assert test_output == ("import os\n"
                            "import sys\n"
-                           "import this\n"
-                           "from HTMLParser import HTMLParseError, HTMLParser\n"
                            "\n"
                            "import profile.test\n")
 
@@ -1017,7 +1004,7 @@ def test_settings_combine_instead_of_overwrite():
            set(SortImports().config['known_standard_library'] + ['not_std_library'])
 
     assert set(SortImports(not_known_standard_library=['thread']).config['known_standard_library']) == \
-           {item for item in SortImports().config['known_standard_library'] if item != 'thread'}
+           set(item for item in SortImports().config['known_standard_library'] if item != 'thread')
 
 
 def test_combined_from_and_as_imports():
@@ -1034,8 +1021,8 @@ def test_as_imports_with_line_length():
                   "from translate.storage.placeables import general, parse as rich_parse\n")
     assert SortImports(file_contents=test_input, combine_as_imports=False, line_length=40).output == \
                   ("from translate.storage import \\\n    base as storage_base\n"
-                   "from translate.storage.placeables import \\\n    general\n"
-                   "from translate.storage.placeables import \\\n    parse as rich_parse\n")
+                   "from translate.storage.placeables import \\\n    parse as rich_parse\n"
+                   "from translate.storage.placeables import \\\n    general\n")
 
 
 def test_keep_comments():
@@ -2158,44 +2145,3 @@ def test_no_extra_lines_issue_557():
                        'from scrapy.core.downloader.handlers.http import HttpDownloadHandler, HTTPDownloadHandler\n')
     assert SortImports(file_contents=test_input, force_alphabetical_sort=True,
                        force_sort_within_sections=True).output == expected_output
-
-
-def test_long_import_wrap_support_with_mode_2():
-    """Test to ensure mode 2 still allows wrapped imports with slash"""
-    test_input = ('from foobar.foobar.foobar.foobar import \\\n'
-                  '    an_even_longer_function_name_over_80_characters\n')
-    assert SortImports(file_contents=test_input, multi_line_output=2, line_length=80).output == test_input
-
-
-def test_pylint_comments_incorrectly_wrapped_issue_571():
-    """Test to ensure pylint comments don't get wrapped"""
-    test_input = ('from PyQt5.QtCore import QRegExp  # @UnresolvedImport pylint: disable=import-error,'
-                  'useless-suppression\n')
-    expected_output = ('from PyQt5.QtCore import \\\n'
-                       '    QRegExp  # @UnresolvedImport pylint: disable=import-error,useless-suppression\n')
-    assert SortImports(file_contents=test_input, line_length=60).output == expected_output
-
-
-def test_ensure_async_methods_work_issue_537():
-    """Test to ensure async methods are correctly identified"""
-    test_input = ('from myapp import myfunction\n'
-                  '\n'
-                  '\n'
-                  'async def test_myfunction(test_client, app):\n'
-                  '    a = await myfunction(test_client, app)\n')
-    assert SortImports(file_contents=test_input).output == test_input
-
-
-def test_ensure_as_imports_sort_correctly_within_from_imports_issue_590():
-    """Test to ensure combination from and as import statements are sorted correct"""
-    test_input = ('from os import defpath\n'
-                  'from os import pathsep as separator\n')
-    assert SortImports(file_contents=test_input, force_sort_within_sections=True).output == test_input
-
-    test_input = ('from os import defpath\n'
-                  'from os import pathsep as separator\n')
-    assert SortImports(file_contents=test_input).output == test_input
-
-    test_input = ('from os import defpath\n'
-                  'from os import pathsep as separator\n')
-    assert SortImports(file_contents=test_input, force_single_line=True).output == test_input