summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Crosley <timothy.crosley@gmail.com>2015-07-12 22:59:10 -0700
committerTimothy Crosley <timothy.crosley@gmail.com>2015-07-12 22:59:10 -0700
commit8385818445e9a28d1cc6479b37dbaeddbb6b2de7 (patch)
tree6441d20e700393f2cdde2d3306ac54eb6c007011
parente6ab66575f18364841ebbcfd3ca2190d8c7cf6d7 (diff)
parent2432ca510130001cc4f5b2997724954a68112f02 (diff)
downloadisort-custom-sections.tar.gz
Merge in latest changes from developcustom-sections
-rw-r--r--isort/isort.py23
-rwxr-xr-xisort/main.py2
-rw-r--r--isort/natural.py46
-rw-r--r--isort/pie_slice.py528
-rw-r--r--isort/settings.py6
-rw-r--r--runtests.py2
-rwxr-xr-xsetup.py3
-rw-r--r--test_isort.py27
8 files changed, 620 insertions, 17 deletions
diff --git a/isort/isort.py b/isort/isort.py
index c2b3f8d0..8f8ddaa0 100644
--- a/isort/isort.py
+++ b/isort/isort.py
@@ -30,14 +30,15 @@ import codecs
import copy
import itertools
import os
+import re
from collections import namedtuple
from datetime import datetime
from difflib import unified_diff
from sys import path as PYTHONPATH
from sys import stderr, stdout
-from natsort import natsorted
-from pies.overrides import *
+from .natural import nsorted
+from .pie_slice import *
from . import settings
@@ -292,16 +293,20 @@ class SortImports(object):
"""
if len(line) > self.config['line_length']:
for splitter in ("import", "."):
- if splitter in line and not line.strip().startswith(splitter):
- line_parts = line.split(splitter)
+ exp = r"\b" + re.escape(splitter) + r"\b"
+ 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())
line = splitter.join(line_parts)
if not line:
line = next_line.pop()
- return "{0}{1} \\\n{2}".format(line, splitter,
- self._wrap(self.config['indent'] + splitter.join(next_line).lstrip()))
+
+ cont_line = self._wrap(self.config['indent'] + splitter.join(next_line).lstrip())
+ if self.config['use_parentheses']:
+ return "{0}{1} (\n{2})".format(line, splitter, cont_line)
+ return "{0}{1} \\\n{2}".format(line, splitter, cont_line)
return line
@@ -327,7 +332,7 @@ class SortImports(object):
import_start = "from {0} import ".format(module)
from_imports = list(self.imports[section]['from'][module])
- from_imports = natsorted(from_imports, key=lambda key: self._module_key(key, self.config, True))
+ from_imports = nsorted(from_imports, key=lambda key: self._module_key(key, self.config, True))
if self.remove_imports:
from_imports = [line for line in from_imports if not "{0}.{1}".format(module, line) in
self.remove_imports]
@@ -432,9 +437,9 @@ class SortImports(object):
output = []
for section in itertools.chain(self.sections, self.config['forced_separate']):
straight_modules = list(self.imports[section]['straight'])
- straight_modules = natsorted(straight_modules, key=lambda key: self._module_key(key, self.config))
+ straight_modules = nsorted(straight_modules, key=lambda key: self._module_key(key, self.config))
from_modules = sorted(list(self.imports[section]['from'].keys()))
- from_modules = natsorted(from_modules, key=lambda key: self._module_key(key, self.config, ))
+ from_modules = nsorted(from_modules, key=lambda key: self._module_key(key, self.config, ))
section_output = []
if self.config.get('from_first', False):
diff --git a/isort/main.py b/isort/main.py
index ff26882e..86270675 100755
--- a/isort/main.py
+++ b/isort/main.py
@@ -26,7 +26,7 @@ import os
import sys
import setuptools
-from pies.overrides import *
+from .pie_slice import *
from isort import SortImports, __version__
from isort.settings import DEFAULT_SECTIONS, default, from_path
diff --git a/isort/natural.py b/isort/natural.py
new file mode 100644
index 00000000..a20ba1d4
--- /dev/null
+++ b/isort/natural.py
@@ -0,0 +1,46 @@
+"""isort/natural.py.
+
+Enables sorting strings that contain numbers naturally
+
+usage:
+ natural.nsorted(list)
+
+Copyright (C) 2013 Timothy Edmund Crosley
+
+Implementation originally from @HappyLeapSecond stack overflow user in response to:
+ 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
+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.
+
+"""
+import re
+
+
+def _atoi(text):
+ return int(text) if text.isdigit() else text
+
+
+def _natural_keys(text):
+ return [_atoi(c) for c in re.split('(\d+)', text)]
+
+
+def nsorted(to_sort, key=None):
+ """Returns a naturally sorted list"""
+ if not key:
+ key_callback = _natural_keys
+ else:
+ key_callback = lambda item: _natural_keys(key(item))
+
+ return sorted(to_sort, key=key_callback)
diff --git a/isort/pie_slice.py b/isort/pie_slice.py
new file mode 100644
index 00000000..5cef39b1
--- /dev/null
+++ b/isort/pie_slice.py
@@ -0,0 +1,528 @@
+"""pie_slice/overrides.py.
+
+Overrides Python syntax to conform to the Python3 version as much as possible using a '*' import
+
+Copyright (C) 2013 Timothy Edmund Crosley
+
+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 copie_slice 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 copie_slice 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 __future__ import absolute_import
+
+import abc
+import functools
+import sys
+from numbers import Integral
+
+__version__ = "1.1.0"
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+VERSION = sys.version_info
+
+native_dict = dict
+native_round = round
+native_filter = filter
+native_map = map
+native_zip = zip
+native_range = range
+native_str = str
+native_chr = chr
+native_input = input
+native_next = next
+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', 'OrderedDict', 'lru_cache']
+
+
+def with_metaclass(meta, *bases):
+ """Enables use of meta classes across Python Versions. taken from jinja2/_compat.py.
+
+ Use it like this::
+
+ class BaseForm(object):
+ pass
+
+ class FormType(type):
+ pass
+
+ class Form(with_metaclass(FormType, BaseForm)):
+ pass
+
+ """
+ class metaclass(meta):
+ __call__ = type.__call__
+ __init__ = type.__init__
+ def __new__(cls, name, this_bases, d):
+ if this_bases is None:
+ return type.__new__(cls, name, (), d)
+ return meta(name, bases, d)
+ return metaclass('temporary_class', None, {})
+
+
+def unmodified_isinstance(*bases):
+ """When called in the form
+
+ MyOverrideClass(unmodified_isinstance(BuiltInClass))
+
+ it allows calls against passed in built in instances to pass even if there not a subclass
+
+ """
+ class UnmodifiedIsInstance(type):
+ 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 with_metaclass(UnmodifiedIsInstance, *bases)
+
+
+if PY3:
+ import urllib
+ import builtins
+ from urllib import parse
+
+ integer_types = (int, )
+
+ def u(string):
+ return string
+
+ def itemsview(collection):
+ return collection.items()
+
+ def valuesview(collection):
+ return collection.values()
+
+ def keysview(collection):
+ return collection.keys()
+
+ urllib.quote = parse.quote
+ urllib.quote_plus = parse.quote_plus
+ urllib.unquote = parse.unquote
+ urllib.unquote_plus = parse.unquote_plus
+ urllib.urlencode = parse.urlencode
+ execute = getattr(builtins, 'exec')
+ if VERSION[1] < 2:
+ def callable(entity):
+ return hasattr(entity, '__call__')
+ common.append('callable')
+
+ __all__ = common + ['urllib']
+else:
+ from itertools import ifilter as filter
+ from itertools import imap as map
+ from itertools import izip as zip
+ from decimal import Decimal, ROUND_HALF_EVEN
+
+ import codecs
+ str = unicode
+ chr = unichr
+ input = raw_input
+ range = xrange
+ integer_types = (int, long)
+
+ import sys
+ stdout = sys.stdout
+ stderr = sys.stderr
+ reload(sys)
+ sys.stdout = stdout
+ sys.stderr = stderr
+ sys.setdefaultencoding('utf-8')
+
+ def _create_not_allowed(name):
+ def _not_allow(*args, **kwargs):
+ raise NameError("name '{0}' is not defined".format(name))
+ _not_allow.__name__ = name
+ return _not_allow
+
+ for removed in ('apply', 'cmp', 'coerce', 'execfile', 'raw_input', 'unpacks'):
+ globals()[removed] = _create_not_allowed(removed)
+
+ def u(s):
+ if isinstance(s, unicode):
+ return s
+ else:
+ return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
+
+ def execute(_code_, _globs_=None, _locs_=None):
+ """Execute code in a namespace."""
+ if _globs_ is None:
+ frame = sys._getframe(1)
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
+ del frame
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
+
+ class _dict_view_base(object):
+ __slots__ = ('_dictionary', )
+
+ def __init__(self, dictionary):
+ self._dictionary = dictionary
+
+ def __repr__(self):
+ return "{0}({1})".format(self.__class__.__name__, str(list(self.__iter__())))
+
+ def __unicode__(self):
+ return str(self.__repr__())
+
+ def __str__(self):
+ return str(self.__unicode__())
+
+ class dict_keys(_dict_view_base):
+ __slots__ = ()
+
+ def __iter__(self):
+ return self._dictionary.iterkeys()
+
+ class dict_values(_dict_view_base):
+ __slots__ = ()
+
+ def __iter__(self):
+ return self._dictionary.itervalues()
+
+ class dict_items(_dict_view_base):
+ __slots__ = ()
+
+ def __iter__(self):
+ return self._dictionary.iteritems()
+
+ def itemsview(collection):
+ return dict_items(collection)
+
+ def valuesview(collection):
+ return dict_values(collection)
+
+ def keysview(collection):
+ return dict_keys(collection)
+
+ class dict(unmodified_isinstance(native_dict)):
+ def has_key(self, *args, **kwargs):
+ return AttributeError("'dict' object has no attribute 'has_key'")
+
+ def items(self):
+ return dict_items(self)
+
+ def keys(self):
+ return dict_keys(self)
+
+ def values(self):
+ return dict_values(self)
+
+ def round(number, ndigits=None):
+ return_int = False
+ if ndigits is None:
+ return_int = True
+ ndigits = 0
+ if hasattr(number, '__round__'):
+ return number.__round__(ndigits)
+
+ if ndigits < 0:
+ raise NotImplementedError('negative ndigits not supported yet')
+ exponent = Decimal('10') ** (-ndigits)
+ d = Decimal.from_float(number).quantize(exponent,
+ rounding=ROUND_HALF_EVEN)
+ if return_int:
+ return int(d)
+ else:
+ return float(d)
+
+ def next(iterator):
+ try:
+ iterator.__next__()
+ except Exception:
+ native_next(iterator)
+
+ class FixStr(type):
+ def __new__(cls, name, bases, dct):
+ if '__str__' in dct:
+ dct['__unicode__'] = dct['__str__']
+ dct['__str__'] = lambda self: self.__unicode__().encode('utf-8')
+ return type.__new__(cls, name, bases, dct)
+
+ 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
+
+ __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:
+ from threading import Lock
+ except ImportError:
+ from dummy_threading import Lock
+
+ from functools import wraps
+
+ def lru_cache(maxsize=100):
+ """Least-recently-used cache decorator.
+ Taking from: https://github.com/MiCHiLU/python-functools32/blob/master/functools32/functools32.py
+ with slight modifications.
+ If *maxsize* is set to None, the LRU features are disabled and the cache
+ can grow without bound.
+ Arguments to the cached function must be hashable.
+ 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: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
+
+ """
+ def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
+ hits, misses = [0], [0]
+ kwd_mark = (object(),) # separates positional and keyword args
+ lock = Lock()
+
+ if maxsize is None:
+ CACHE = dict()
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ key = args
+ if kwds:
+ key += kwd_mark + tuple(sorted(kwds.items()))
+ try:
+ result = CACHE[key]
+ hits[0] += 1
+ return result
+ except KeyError:
+ pass
+ result = user_function(*args, **kwds)
+ CACHE[key] = result
+ misses[0] += 1
+ return result
+ else:
+ CACHE = OrderedDict()
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ key = args
+ if kwds:
+ key += kwd_mark + tuple(sorted(kwds.items()))
+ with lock:
+ cached = CACHE.get(key, None)
+ if cached:
+ del CACHE[key]
+ CACHE[key] = cached
+ hits[0] += 1
+ return cached
+ result = user_function(*args, **kwds)
+ with lock:
+ CACHE[key] = result # record recent use of this key
+ misses[0] += 1
+ while len(CACHE) > maxsize:
+ CACHE.popitem(last=False)
+ return result
+
+ def cache_info():
+ """Report CACHE statistics."""
+ with lock:
+ return _CacheInfo(hits[0], misses[0], maxsize, len(CACHE))
+
+ def cache_clear():
+ """Clear the CACHE and CACHE statistics."""
+ with lock:
+ CACHE.clear()
+ hits[0] = misses[0] = 0
+
+ wrapper.cache_info = cache_info
+ wrapper.cache_clear = cache_clear
+ return wrapper
+
+ return decorating_function
+
+else:
+ from functools import lru_cache
diff --git a/isort/settings.py b/isort/settings.py
index 57067f61..a14fa115 100644
--- a/isort/settings.py
+++ b/isort/settings.py
@@ -27,8 +27,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
import os
from collections import namedtuple
-from pies.functools import lru_cache
-from pies.overrides import *
+from .pie_slice import *
try:
import configparser
@@ -55,7 +54,7 @@ default = {'force_to_top': [],
"decimal", "difflib", "dircache", "dis", "doctest", "dumbdbm", "EasyDialogs",
"errno", "exceptions", "filecmp", "fileinput", "fnmatch", "fractions",
"functools", "gc", "gdbm", "getopt", "getpass", "gettext", "glob", "grp", "gzip",
- "hashlib", "heapq", "hmac", "imaplib", "imp", "inspect", "itertools", "json",
+ "hashlib", "heapq", "hmac", "imaplib", "imp", "inspect", "io", "itertools", "json",
"linecache", "locale", "logging", "mailbox", "math", "mhlib", "mmap",
"multiprocessing", "operator", "optparse", "os", "pdb", "pickle", "pipes",
"pkgutil", "platform", "plistlib", "pprint", "profile", "pstats", "pwd", "pyclbr",
@@ -84,6 +83,7 @@ default = {'force_to_top': [],
'import_heading_firstparty': '',
'import_heading_localfolder': '',
'balanced_wrapping': False,
+ 'use_parentheses': False,
'order_by_type': True,
'atomic': False,
'lines_after_imports': -1,
diff --git a/runtests.py b/runtests.py
index ad902463..a024d3d5 100644
--- a/runtests.py
+++ b/runtests.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
sources = """
eNrcvWmXG1l2INYzY1tjeGYk2ePtg32iQVGIKCKDSXZpyylUqVRFqqmuYvFwUVPOSoGRQGRmNJER
diff --git a/setup.py b/setup.py
index f51220f7..6daad883 100755
--- a/setup.py
+++ b/setup.py
@@ -57,8 +57,6 @@ setup(name='isort',
'distutils.commands': ['isort = isort.main:ISortCommand'],
},
packages=['isort'],
- requires=['pies', 'natsort'],
- install_requires=['pies>=2.6.2', 'natsort>=3.0.0'],
cmdclass={'test': PyTest},
keywords='Refactor, Python, Python2, Python3, Refactoring, Imports, Sort, Clean',
classifiers=['Development Status :: 6 - Mature',
@@ -76,6 +74,7 @@ setup(name='isort',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
'Topic :: Software Development :: Libraries',
'Topic :: Utilities'],
**PyTest.extra_kwargs)
diff --git a/test_isort.py b/test_isort.py
index 41458991..574f201e 100644
--- a/test_isort.py
+++ b/test_isort.py
@@ -22,7 +22,7 @@ OTHER DEALINGS IN THE SOFTWARE.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
-from pies.overrides import *
+from isort.pie_slice import *
from isort.isort import SortImports
from isort.settings import WrapModes
@@ -413,6 +413,18 @@ def test_custom_indent():
" lib20, lib21, lib22\n")
+def test_use_parentheses():
+ test_input = (
+ "from fooooooooooooooooooooooooo.baaaaaaaaaaaaaaaaaaarrrrrrr import \\"
+ " my_custom_function as my_special_function"
+ )
+ test_output = SortImports(
+ file_contents=test_input, known_third_party=['django'],
+ line_length=79, use_parentheses=True,
+ ).output
+ assert '(' in test_output
+
+
def test_skip():
"""Ensure skipping a single import will work as expected."""
test_input = ("import myproject\n"
@@ -1397,3 +1409,16 @@ def test_fcntl():
"import os\n"
"import sys\n")
assert SortImports(file_contents=test_input).output == test_input
+
+
+def test_import_split_is_word_boundary_aware():
+ """Test to ensure that isort splits words in a boundry aware mannor"""
+ test_input = ("from mycompany.model.size_value_array_import_func import ("
+ " get_size_value_array_import_func_jobs,"
+ ")")
+ test_output = SortImports(file_contents=test_input,
+ multi_line_output=WrapModes.VERTICAL_HANGING_INDENT,
+ line_length=79).output
+
+ assert test_output == ("from mycompany.model.size_value_array_import_func import \\\n"
+ " get_size_value_array_import_func_jobs\n")