summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--CHANGELOG.md9
-rw-r--r--README.rst2
-rw-r--r--appveyor.yml2
-rw-r--r--isort/__init__.py2
-rw-r--r--isort/isort.py24
-rwxr-xr-xisort/main.py2
-rw-r--r--isort/pie_slice.py193
-rw-r--r--isort/settings.py3
-rwxr-xr-xsetup.py3
-rw-r--r--test_isort.py18
-rw-r--r--tox.ini2
12 files changed, 235 insertions, 27 deletions
diff --git a/.travis.yml b/.travis.yml
index cc8354b6..6b23c0db 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,8 @@ language: python
matrix:
include:
- env: TOXENV=isort-check
+ - python: 2.6
+ env: TOXENV=py26
- python: 2.7
env: TOXENV=py27
- python: 3.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c55055d9..b16ae902 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,14 @@
Changelog
=========
-### 4.2.9 - June 1, 2017 - hotfix release
+### 4.2.12 - June 1, 2017 - hotfix release
IMPORTANT NOTE: This will be the last release with Python 2.6 support, subsequent releases will be 2.7+ only
+- Fixed wheel distribution bug
+
+### 4.2.11 - June 1, 2017 - hotfix release
+- Fixed #546: Can't select y/n/c after latest update
+- Fixed #545: Incorrectly moves __future__ imports above encoding comments
+
+### 4.2.9 - June 1, 2017 - hotfix release
- Fixed #428: Check only modifies sorting
- Fixed #540: Not correctly identifying stdlib modules
diff --git a/README.rst b/README.rst
index 68b03850..f185178d 100644
--- a/README.rst
+++ b/README.rst
@@ -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 d906c27d..a6b3a0ab 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
from .isort import SortImports
-__version__ = "4.2.9"
+__version__ = "4.2.12"
diff --git a/isort/isort.py b/isort/isort.py
index b5896df9..d9b4f31b 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, itemsview
+from .pie_slice import OrderedDict, OrderedSet, input, itemsview
KNOWN_SECTION_MAPPING = {
'STDLIB': 'STANDARD_LIBRARY',
@@ -264,7 +263,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(".", "/")))
@@ -489,7 +488,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
@@ -903,7 +902,8 @@ class SortImports(object):
self.comments['straight'][module] = comments
comments = None
- if len(self.out_lines) > max(self.import_index, self._first_comment_index_end, 1) - 1:
+ if len(self.out_lines) > max(self.import_index, self._first_comment_index_end + 1, 1) - 1:
+
last = self.out_lines and self.out_lines[-1].rstrip() or ""
while (last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and
not 'isort:imports-' in last):
@@ -942,6 +942,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 fd192338..eae7afa5 100755
--- a/isort/main.py
+++ b/isort/main.py
@@ -236,7 +236,7 @@ def create_parser():
parser.add_argument('-up', '--use-parentheses', dest='use_parentheses', action='store_true',
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/pie_slice.py b/isort/pie_slice.py
index 359ff25c..bfb341a7 100644
--- a/isort/pie_slice.py
+++ b/isort/pie_slice.py
@@ -47,7 +47,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):
@@ -85,12 +85,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)
@@ -100,6 +120,7 @@ if PY3:
import builtins
from urllib import parse
+ input = input
integer_types = (int, )
def u(string):
@@ -260,10 +281,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
@@ -271,6 +310,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:
@@ -317,7 +488,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 e9efe4ea..c60bd786 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
@@ -203,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()
diff --git a/setup.py b/setup.py
index c6be3306..a750f763 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.9',
+ version='4.2.12',
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/test_isort.py b/test_isort.py
index 492d0452..67eccc03 100644
--- a/test_isort.py
+++ b/test_isort.py
@@ -1002,7 +1002,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():
@@ -2116,3 +2116,19 @@ def test_correct_number_of_new_lines_with_comment_issue_435():
'def baz():\n'
' pass\n')
assert SortImports(file_contents=test_input).output == test_input
+
+
+def test_future_below_encoding_issue_545():
+ """Test to ensure future is always below comment"""
+ test_input = ('#!/usr/bin/env python\n'
+ 'from __future__ import print_function\n'
+ 'import logging\n'
+ '\n'
+ 'print("hello")\n')
+ expected_output = ('#!/usr/bin/env python\n'
+ 'from __future__ import print_function\n'
+ '\n'
+ 'import logging\n'
+ '\n'
+ 'print("hello")\n')
+ assert SortImports(file_contents=test_input).output == expected_output
diff --git a/tox.ini b/tox.ini
index 7610eefe..1ee185be 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,7 +6,7 @@
[tox]
envlist =
isort-check,
- py27, py33, py34, py35, py36, pypy
+ py26, py27, py33, py34, py35, py36, pypy
[testenv]
commands =