Skip to content

Commit

Permalink
Let isort decide which config to use
Browse files Browse the repository at this point in the history
isort will look for a configuration in pyproject.toml
when the TOML library is available.

flake8-isort has its own mechanism to look up an appropriate
configuration file and will pass its path on to isort.
Right now, flake8-isort doesn't consider pyproject.toml files as
configuration file candidates and will raise an error if there's
no other configuration file than the pyproject.toml.

Luckily, there's the parameter `--no-isort-config` to make flake8-isort
not stall when it cannot find a config file.
In this case, isort will not be instructed to look for a specific
configuration file by flake8-isort but instead look for it itself
(beginning from either the currently linted file path
or the current working directory).

This is great because it means that we can rely on isort
to figure out which config to use
and linting behavior between both libraries will be exactly the same.
The search for a configuration file can be removed from flake8-isort
and we get Pyproject.toml support for free.
The `--no-isort-config` option can be removed as well
because from now on it will always be the burden of isort
to figure out which config to use.

Related to gforcada#71
Closes gforcada#78
  • Loading branch information
jnns committed Feb 29, 2020
1 parent 313963e commit 77bda77
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 127 deletions.
114 changes: 16 additions & 98 deletions flake8_isort.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
# -*- coding: utf-8 -*-
from difflib import Differ
from isort import SortImports
from os.path import expanduser
from testfixtures import OutputCapture

import os


try:
from configparser import ConfigParser as SafeConfigParser
except ImportError:
from ConfigParser import SafeConfigParser


class Flake8Isort(object):
name = 'flake8_isort'
Expand All @@ -32,7 +23,6 @@ class Flake8Isort(object):
'I005 isort found an unexpected missing import'
)

config_file = None
show_traceback = False
stdin_display_name = None

Expand All @@ -43,12 +33,6 @@ def __init__(self, tree, filename, lines, search_current=True):

@classmethod
def add_options(cls, parser):
parser.add_option(
'--no-isort-config',
action='store_true',
parse_from_config=True,
help='Do not require explicit configuration to be found'
)

parser.add_option(
'--isort-show-traceback',
Expand All @@ -59,93 +43,27 @@ def add_options(cls, parser):

@classmethod
def parse_options(cls, options):
if options.no_isort_config is None:
cls.config_file = True
else:
cls.config_file = False

cls.stdin_display_name = options.stdin_display_name
cls.show_traceback = options.isort_show_traceback

def run(self):
settings_file = self.search_isort_config()
if self.config_file and not settings_file:
yield 0, 0, self.no_config_msg, type(self)
if self.filename is not self.stdin_display_name:
file_path = self.filename
else:
if self.filename is not self.stdin_display_name:
file_path = self.filename
else:
file_path = None
with OutputCapture() as buffer:
sort_result = SortImports(
file_path=file_path,
file_contents=''.join(self.lines),
check=True,
settings_path=settings_file,
show_diff=True,
)
traceback = self._format_isort_output(buffer)

for line_num, message in self.sortimports_linenum_msg(sort_result):
if self.show_traceback:
message += traceback
yield line_num, 0, message, type(self)

def search_isort_config(self):
# type: () -> Optional[str] # noqa: F821
"""Search for isort configuration all the way up to the root folder
Looks for ``.isort.cfg``, ``.editorconfig`` or ``[isort]`` section in
``setup.cfg``, ``tox.ini``, or ``.flake8`` config files.
"""
full_path = os.path.abspath(self.filename)
split_path = (os.path.dirname(full_path), True)
while split_path[1]:
config_on_file = self._search_config_on_path(split_path[0])
if config_on_file:
return config_on_file
split_path = os.path.split(split_path[0])

if self.search_current:
return self.search_isort_config_at_current()

# last attempt, check home folder
home = expanduser('~')
config_on_home = self._search_config_on_path(home)
if config_on_home:
return config_on_home

return None

def search_isort_config_at_current(self):
# type: () -> Optional[str] # noqa: F821
"""Search for isort configuration at current directory"""
return self._search_config_on_path(os.path.realpath('.'))

def _search_config_on_path(self, path):
# type: (str) -> Optional[str] # noqa: F821
"""Search for isort configuration files at the specifed path.
Args:
path: The path to search for config files on.
Return:
str: the isort config if found otherwise, None
"""
for config_file in ('.isort.cfg', '.editorconfig'):
config_file_path = os.path.join(path, config_file)
if os.path.isfile(config_file_path):
return config_file_path

# Check for '[isort]' section in other configuration files.
for config_file in ('tox.ini', 'setup.cfg', '.flake8'):
config_file_path = os.path.join(path, config_file)
config = SafeConfigParser()
config.read(config_file_path)
if 'isort' in config.sections():
return config_file_path

return None
file_path = None
with OutputCapture() as buffer:
sort_result = SortImports(
file_path=file_path,
file_contents=''.join(self.lines),
check=True,
show_diff=True,
)
traceback = self._format_isort_output(buffer)

for line_num, message in self.sortimports_linenum_msg(sort_result):
if self.show_traceback:
message += traceback
yield line_num, 0, message, type(self)

def sortimports_linenum_msg(self, sort_result):
"""Parses isort.SortImports for line number changes and message
Expand Down
32 changes: 3 additions & 29 deletions run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,7 @@ def test_isortcfg_found(self):

def test_isortcfg_not_found(self):
(file_path, lines) = self._given_a_file_in_test_dir(
'from sys import pid\n'
'import threading',
'from sys import pid, path',
isort_config='force_single_line=True'
)
# remove the .isort.cfg file
Expand All @@ -239,33 +238,9 @@ def test_isortcfg_not_found(self):
checker.config_file = True
ret = list(checker.run())
self.assertEqual(len(ret), 1)
self.assertEqual(ret[0][0], 0)
self.assertEqual(ret[0][0], 1)
self.assertEqual(ret[0][1], 0)
self.assertTrue(ret[0][2].startswith('I002 '))

def test_default_option(self):
"""By default a config file (.isort.cfg) is expected"""
(file_path, lines) = self._given_a_file_in_test_dir(
'from sys import pid\n'
'import threading\n',
isort_config='force_single_line=True'
)
with OutputCapture():
app = application.Application()
app.run([file_path, ])
self.assertTrue(Flake8Isort.config_file)

def test_config_file(self):
"""Check that one can force to not look for a config file"""
(file_path, lines) = self._given_a_file_in_test_dir(
'from sys import pid\n'
'import threading\n',
isort_config='force_single_line=True'
)
with OutputCapture():
app = application.Application()
app.run(['--no-isort-config', file_path, ])
self.assertFalse(Flake8Isort.config_file)
self.assertTrue(ret[0][2].startswith('I001 '))

def test_isort_formatted_output(self):
options = collections.namedtuple(
Expand Down Expand Up @@ -294,7 +269,6 @@ def test_isort_formatted_output(self):
self.assertEqual(ret[0][1], 0)
self.assertIn(diff, ret[0][2])

@unittest.expectedFailure
def test_isort_uses_pyproject_toml_if_available(self):
(file_path, lines) = self._given_a_file_in_test_dir(
'import os\n'
Expand Down

0 comments on commit 77bda77

Please sign in to comment.