Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: PyCQA/isort
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4.3.12
Choose a base ref
...
head repository: PyCQA/isort
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4.3.13
Choose a head ref
  • 20 commits
  • 8 files changed
  • 3 contributors

Commits on Mar 6, 2019

  1. Copy the full SHA
    a278be9 View commit details

Commits on Mar 7, 2019

  1. Merge in fix for quiet mode

    emilmelnikov authored and timothycrosley committed Mar 7, 2019
    Copy the full SHA
    be50253 View commit details
  2. Copy the full SHA
    2ebab7c View commit details
  3. Copy the full SHA
    35765c5 View commit details
  4. Merge pull request #886 from timothycrosley/feature/anaconda-support

    Feature/anaconda support
    timothycrosley authored Mar 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d6f200b View commit details
  5. Copy the full SHA
    3a856aa View commit details

Commits on Mar 8, 2019

  1. Copy the full SHA
    4f4064b View commit details
  2. Copy the full SHA
    f981631 View commit details
  3. Copy the full SHA
    476ebe4 View commit details
  4. Fix issue #890

    timothycrosley committed Mar 8, 2019
    Copy the full SHA
    12c034a View commit details
  5. Copy the full SHA
    fa3421e View commit details
  6. Copy the full SHA
    855806f View commit details
  7. Copy the full SHA
    4f2ed4c View commit details
  8. Copy the full SHA
    8120d69 View commit details
  9. Update isort.py

    timothycrosley authored Mar 8, 2019
    Copy the full SHA
    2403ccf View commit details
  10. Merge pull request #893 from timothycrosley/feature/fix-issue-889

    Fix issue #889: Isort should not add blank lines to beginning of file
    timothycrosley authored Mar 8, 2019
    Copy the full SHA
    28e1f7d View commit details
  11. Update isort.py

    timothycrosley authored Mar 8, 2019
    Copy the full SHA
    9377ab2 View commit details
  12. Update changelog

    timothycrosley committed Mar 8, 2019
    Copy the full SHA
    f4c29ca View commit details
  13. Merge pull request #883 from bcroq/file-encoding

    work on file encoding detection
    timothycrosley authored Mar 8, 2019
    Copy the full SHA
    6655b28 View commit details
  14. Bump release

    timothycrosley committed Mar 8, 2019
    Copy the full SHA
    8b73414 View commit details
Showing with 169 additions and 43 deletions.
  1. +8 −0 CHANGELOG.md
  2. +1 −1 isort/__init__.py
  3. +21 −7 isort/finders.py
  4. +52 −25 isort/isort.py
  5. +16 −6 isort/main.py
  6. +3 −2 isort/settings.py
  7. +1 −1 setup.py
  8. +67 −1 test_isort.py
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
Changelog
=========
### 4.3.13 - March 8, 2019 - hot fix release
- Fixed the inability to accurately determine import section when a mix of conda and virtual environments are used.
- Fixed some output being printed even when --quiet mode is enabled.
- Fixed issue #890 interoperability with PyCharm by allowing case sensitive non type grouped sorting.
- Fixed issue #889 under some circumstances isort will incorrectly add a new line at the beginning of a file.
- Fixed issue #885 many files not being skipped according to set skip settings.
- Fixed issue #842 streaming encoding improvements.

### 4.3.12 - March 6, 2019 - hot fix release
- Fix error caused when virtual environment not detected

2 changes: 1 addition & 1 deletion isort/__init__.py
Original file line number Diff line number Diff line change
@@ -25,4 +25,4 @@
from . import settings # noqa: F401
from .isort import SortImports # noqa: F401

__version__ = "4.3.12"
__version__ = "4.3.13"
28 changes: 21 additions & 7 deletions isort/finders.py
Original file line number Diff line number Diff line change
@@ -130,13 +130,8 @@ class PathFinder(BaseFinder):
def __init__(self, config, sections):
super(PathFinder, self).__init__(config, sections)

# Use a copy of sys.path to avoid any unintended modifications
# to it - e.g. `+=` used below will change paths in place and
# if not copied, consequently sys.path, which will grow unbounded
# with duplicates on every call to this method.
self.paths = list(sys.path)
# restore the original import path (i.e. not the path to bin/isort)
self.paths[0] = os.getcwd()
self.paths = [os.getcwd()]

# virtual env
self.virtual_env = self.config.get('virtual_env') or os.environ.get('VIRTUAL_ENV')
@@ -155,6 +150,17 @@ def __init__(self, config, sections):
if os.path.isdir(path):
self.paths.append(path)

# conda
self.conda_env = self.config.get('conda_env') or os.environ.get('CONDA_PREFIX')
if self.conda_env:
self.conda_env = os.path.realpath(self.conda_env)
for path in glob('{0}/lib/python*/site-packages'.format(self.conda_env)):
if path not in self.paths:
self.paths.append(path)
for path in glob('{0}/lib/python*/*/site-packages'.format(self.conda_env)):
if path not in self.paths:
self.paths.append(path)

# handle case-insensitive paths on windows
self.stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()['stdlib'])
if self.stdlib_lib_prefix not in self.paths:
@@ -163,12 +169,18 @@ def __init__(self, config, sections):
# handle compiled libraries
self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ".so"

# add system paths
for path in sys.path[1:]:
if path not in self.paths:
self.paths.append(path)

def find(self, module_name):
for prefix in self.paths:
package_path = "/".join((prefix, module_name.split(".")[0]))
is_module = (exists_case_sensitive(package_path + ".py") or
exists_case_sensitive(package_path + ".so") or
exists_case_sensitive(package_path + self.ext_suffix))
exists_case_sensitive(package_path + self.ext_suffix) or
exists_case_sensitive(package_path + "/__init__.py"))
is_package = exists_case_sensitive(package_path) and os.path.isdir(package_path)
if is_module or is_package:
if 'site-packages' in prefix:
@@ -177,6 +189,8 @@ def find(self, module_name):
return self.sections.THIRDPARTY
if self.virtual_env and self.virtual_env_src in prefix:
return self.sections.THIRDPARTY
if self.conda_env and self.conda_env in prefix:
return self.sections.THIRDPARTY
if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix):
return self.sections.STDLIB
return self.config['default_section']
77 changes: 52 additions & 25 deletions isort/isort.py
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
"""
from __future__ import absolute_import, division, print_function, unicode_literals

import codecs
import copy
import io
import itertools
@@ -46,8 +47,9 @@ class SortImports(object):
incorrectly_sorted = False
skipped = False

def __init__(self, file_path=None, file_contents=None, write_to_stdout=False, check=False,
show_diff=False, settings_path=None, ask_to_apply=False, check_skip=True, **setting_overrides):
def __init__(self, file_path=None, file_contents=None, file_=None, write_to_stdout=False, check=False,
show_diff=False, settings_path=None, ask_to_apply=False, run_path='', check_skip=True,
**setting_overrides):
if not settings_path and file_path:
settings_path = os.path.dirname(os.path.abspath(file_path))
settings_path = settings_path or os.getcwd()
@@ -93,14 +95,22 @@ def __init__(self, file_path=None, file_contents=None, write_to_stdout=False, ch
self.file_path = file_path or ""
if file_path:
file_path = os.path.abspath(file_path)
if check_skip and settings.should_skip(file_path, self.config):
self.skipped = True
if self.config['verbose']:
print("WARNING: {0} was skipped as it's listed in 'skip' setting"
" or matches a glob in 'skip_glob' setting".format(file_path))
file_contents = None
elif not file_contents:
file_encoding = coding_check(file_path)
if check_skip:
if run_path and file_path.startswith(run_path):
file_name = file_path.replace(run_path, '', 1)
else:
file_name = file_path
run_path = ''

if settings.should_skip(file_name, self.config, run_path):
self.skipped = True
if self.config['verbose']:
print("WARNING: {0} was skipped as it's listed in 'skip' setting"
" or matches a glob in 'skip_glob' setting".format(file_path))
file_contents = None
if not self.skipped and not file_contents:
with io.open(file_path, 'rb') as f:
file_encoding = coding_check(f)
with io.open(file_path, encoding=file_encoding, newline='') as file_to_import_sort:
try:
file_contents = file_to_import_sort.read()
@@ -125,6 +135,24 @@ def __init__(self, file_path=None, file_contents=None, write_to_stdout=False, ch
"{} encoding or {} fallback encoding".format(file_path,
self.file_encoding,
file_to_import_sort.encoding))
elif file_:
self.file_encoding = coding_check(file_)
file_.seek(0)
reader = codecs.getreader(self.file_encoding)
file_contents = reader(file_).read()

# try to decode file_contents
if file_contents:
try:
basestring
# python 2
need_decode = (str, bytes)
except NameError:
# python 3
need_decode = bytes

if isinstance(file_contents, need_decode):
file_contents = file_contents.decode(coding_check(file_contents.splitlines()))

if file_contents is None or ("isort:" + "skip_file") in file_contents:
self.skipped = True
@@ -219,7 +247,8 @@ def __init__(self, file_path=None, file_contents=None, write_to_stdout=False, ch
if answer in ('quit', 'q'):
sys.exit(1)
with io.open(self.file_path, encoding=self.file_encoding, mode='w', newline='') as output_file:
print("Fixing {0}".format(self.file_path))
if not self.config['quiet']:
print("Fixing {0}".format(self.file_path))
output_file.write(self.output)

@property
@@ -294,7 +323,8 @@ def _module_key(module_name, config, sub_imports=False, ignore_case=False, secti
prefix = "B"
else:
prefix = "C"
module_name = module_name.lower()
if not config['case_sensitive']:
module_name = module_name.lower()
if section_name is None or 'length_sort_' + str(section_name).lower() not in config:
length_sort = config['length_sort']
else:
@@ -983,7 +1013,7 @@ def _parse(self):
'isort:imports-' not in last):
self.comments['above']['straight'].setdefault(module, []).insert(0,
self.out_lines.pop(-1))
if len(self.out_lines) > 0:
if len(self.out_lines) > 0 and len(self.out_lines) != self._first_comment_index_end:
last = self.out_lines[-1].rstrip()
else:
last = ""
@@ -1000,19 +1030,16 @@ def _parse(self):
self.imports[placed_module][import_type][module] = None


def coding_check(fname, default='utf-8'):
def coding_check(lines, default='utf-8'):

# see https://www.python.org/dev/peps/pep-0263/
pattern = re.compile(br'coding[:=]\s*([-\w.]+)')

coding = default
with io.open(fname, 'rb') as f:
for line_number, line in enumerate(f, 1):
groups = re.findall(pattern, line)
if groups:
coding = groups[0].decode('ascii')
break
if line_number > 2:
break

return coding
for line_number, line in enumerate(lines, 1):
groups = re.findall(pattern, line)
if groups:
return groups[0].decode('ascii')
if line_number > 2:
break

return default
22 changes: 16 additions & 6 deletions isort/main.py
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ def __init__(self, incorrectly_sorted, skipped):

def sort_imports(file_name, **arguments):
try:
result = SortImports(file_name, check_skip=False, **arguments)
result = SortImports(file_name, **arguments)
return SortAttempt(result.incorrectly_sorted, result.skipped)
except IOError as e:
print("WARNING: Unable to parse file {0} due to {1}".format(file_name, e))
@@ -99,17 +99,16 @@ def iter_source_code(paths, config, skipped):

for path in paths:
if os.path.isdir(path):
for dirpath, dirnames, filenames in os.walk(
path, topdown=True, followlinks=True
):
for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=True):
for dirname in list(dirnames):
if should_skip(dirname, config, dirpath):
skipped.append(dirname)
dirnames.remove(dirname)
for filename in filenames:
filepath = os.path.join(dirpath, filename)
if is_python_file(filepath):
if should_skip(filename, config, dirpath):
relative_file = os.path.relpath(filepath, path)
if should_skip(relative_file, config, path):
skipped.append(filename)
else:
yield filepath
@@ -278,6 +277,8 @@ def parse_args(argv=None):
help='Shows verbose output, such as when files are skipped or when a check is successful.')
parser.add_argument('--virtual-env', dest='virtual_env',
help='Virtual environment to use for determining whether a package is third-party')
parser.add_argument('--conda-env', dest='conda_env',
help='Conda environment to use for determining whether a package is third-party')
parser.add_argument('-vn', '--version-number', action='version', version=__version__,
help='Returns just the current version number without the logo')
parser.add_argument('-w', '--line-width', help='The max length of an import line (used for wrapping long imports).',
@@ -291,6 +292,8 @@ def parse_args(argv=None):
parser.add_argument('--unsafe', dest='unsafe', action='store_true',
help='Tells isort to look for files in standard library directories, etc. '
'where it may not be safe to operate in')
parser.add_argument('--case-sensitive', dest='case_sensitive', action='store_true',
help='Tells isort to include casing when sorting module names')
parser.add_argument('files', nargs='*', help='One or more Python source files that need their imports sorted.')

arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value}
@@ -312,6 +315,7 @@ def main(argv=None):
'-rc for recursive')
sys.exit(1)

arguments['check_skip'] = False
if 'settings_path' in arguments:
sp = arguments['settings_path']
arguments['settings_path'] = os.path.abspath(sp) if os.path.isdir(sp) else os.path.dirname(os.path.abspath(sp))
@@ -326,7 +330,13 @@ def main(argv=None):

file_names = arguments.pop('files', [])
if file_names == ['-']:
SortImports(file_contents=sys.stdin.read(), write_to_stdout=True, **arguments)
try:
# python 3
file_ = sys.stdin.buffer
except AttributeError:
# python 2
file_ = sys.stdin
SortImports(file_=file_, write_to_stdout=True, **arguments)
else:
if not file_names:
file_names = ['.']
5 changes: 3 additions & 2 deletions isort/settings.py
Original file line number Diff line number Diff line change
@@ -162,7 +162,8 @@
'no_lines_before': [],
'no_inline_sort': False,
'ignore_comments': False,
'safety_excludes': True}
'safety_excludes': True,
'case_sensitive': False}


@lru_cache()
@@ -325,7 +326,7 @@ def should_skip(filename, config, path=''):
if normalized_path[1:2] == ':':
normalized_path = normalized_path[2:]

if config['safety_excludes']:
if path and config['safety_excludes']:
check_exclude = '/' + filename.replace('\\', '/') + '/'
if path and os.path.basename(path) in ('lib', ):
check_exclude = '/' + os.path.basename(path) + check_exclude
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
readme = f.read()

setup(name='isort',
version='4.3.12',
version='4.3.13',
description='A Python utility / library to sort Python imports.',
long_description=readme,
author='Timothy Crosley',
Loading