diff --git a/.gitignore b/.gitignore index 85f78d3a1..a43998267 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,14 @@ atlassian-ide-plugin.xml *.swp *.kate-swp .ropeproject/ + +# pip +pip-selfcheck.json + +# Python3 Venv Files +bin/ +include/ +lib/ +lib64 +pyvenv.cfg +share/ diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index 086cb22c5..257882111 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -58,6 +58,7 @@ Code Contributors - David Stensland (@terite) - Ankur Dedania (@AbsoluteMSTR) - Lee Packham (@leepa) +- Jesse Mullan (@jmullan) Documenters =================== diff --git a/CHANGELOG.md b/CHANGELOG.md index f2655165f..1ef29082d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,3 +40,8 @@ Changelog ### 4.2.1 - Hot fix release to fix code error when skipping globs + +### 4.2.2 +- Give an error message when isort is unable to determine where to place a module +- Allow imports to be sorted by module, independant of import_type, when `force_sort_within_sections` option is set +- Fixed an issue that caused Python files with 2 top comments not to be sorted diff --git a/isort/__init__.py b/isort/__init__.py index 251bb78cd..0f09c4a43 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -25,4 +25,4 @@ from . import settings from .isort import SortImports -__version__ = "4.2.1" +__version__ = "4.2.2" diff --git a/isort/isort.py b/isort/isort.py index 9c25350e3..a29e98684 100644 --- a/isort/isort.py +++ b/isort/isort.py @@ -460,8 +460,8 @@ def _add_formatted_imports(self): new_straight_output.append(element) - sorted_from = sorted(new_from_output, key=lambda s: s.lower()) - sorted_straight = sorted(new_straight_output, key=lambda s: s.lower()) + sorted_from = sorted(new_from_output, key=lambda import_string: import_string.lower()) + sorted_straight = sorted(new_straight_output, key=lambda import_string: import_string.lower()) output = (sorted_from + [''] + sorted_straight) if (sorted_from and sorted_straight) else \ (sorted_from or sorted_straight) else: @@ -480,6 +480,15 @@ def _add_formatted_imports(self): self._add_straight_imports(straight_modules, section, section_output) self._add_from_imports(from_modules, section, section_output) + if self.config.get('force_sort_within_sections', False): + def by_module(line): + line = re.sub('^from ', '', line) + line = re.sub('^import ', '', line) + if not self.config['order_by_type']: + line = line.lower() + return line + section_output = nsorted(section_output, key=by_module) + if section_output: section_name = section if section_name in self.place_imports: @@ -653,11 +662,13 @@ def _skip_line(self, line): skip_line = self._in_quote if self.index == 1 and line.startswith("#"): self._in_top_comment = True + return True elif self._in_top_comment: if not line.startswith("#"): - self._in_top_comment = False - self._first_comment_index_end = self.index - elif '"' in line or "'" in line: + self._in_top_comment = False + self._first_comment_index_end = self.index + + if '"' in line or "'" in line: index = 0 if self._first_comment_index_start == -1: self._first_comment_index_start = self.index @@ -779,7 +790,13 @@ def _parse(self): del imports[index:index + 2] if import_type == "from": import_from = imports.pop(0) - root = self.imports[self.place_module(import_from)][import_type] + placed_module = self.place_module(import_from) + if placed_module == '': + print( + "WARNING: could not place module {0} of line {1} --" + " Do you need to define a default section?".format(import_from, line) + ) + root = self.imports[placed_module][import_type] for import_name in imports: associated_commment = nested_comments.get(import_name) if associated_commment: @@ -821,7 +838,13 @@ def _parse(self): last = "" if self.index - 1 == self.import_index: self.import_index -= len(self.comments['above']['straight'].get(module, [])) - self.imports[self.place_module(module)][import_type].add(module) + placed_module = self.place_module(module) + if placed_module == '': + print( + "WARNING: could not place module {0} of line {1} --" + " Do you need to define a default section?".format(import_from, line) + ) + self.imports[placed_module][import_type].add(module) def coding_check(fname, default='utf-8'): diff --git a/isort/main.py b/isort/main.py index 70874db8d..d09ca914e 100755 --- a/isort/main.py +++ b/isort/main.py @@ -218,6 +218,9 @@ def create_parser(): help='Force from imports to be grid wrapped regardless of line length') parser.add_argument('-fas', '--force-alphabetical-sort', action='store_true', dest="force_alphabetical_sort", help='Force all imports to be sorted as a single section') + parser.add_argument('-fss', '--force-sort-within-sections', action='store_true', dest="force_sort_within_sections", + help='Force imports to be sorted by module, independant of import_type') + arguments = dict((key, value) for (key, value) in itemsview(vars(parser.parse_args())) if value) return arguments diff --git a/setup.py b/setup.py index 77c3dc0cb..e3704d00c 100755 --- a/setup.py +++ b/setup.py @@ -39,13 +39,13 @@ def run(self): readme = f.read() setup(name='isort', - version='4.2.1', + version='4.2.2', description='A Python utility / library to sort Python imports.', long_description=readme, author='Timothy Crosley', author_email='timothy.crosley@gmail.com', url='https://github.com/timothycrosley/isort', - download_url='https://github.com/timothycrosley/isort/archive/4.2.1.tar.gz', + download_url='https://github.com/timothycrosley/isort/archive/4.2.2.tar.gz', license="MIT", entry_points={ 'console_scripts': [ diff --git a/test_isort.py b/test_isort.py index 540ef9886..ee9a49ce5 100644 --- a/test_isort.py +++ b/test_isort.py @@ -1605,3 +1605,36 @@ def test_alphabetic_sorting_no_newlines(): 'print(1)\n') test_output = SortImports(file_contents=test_input,force_alphabetical_sort=True, lines_after_imports=2).output assert test_input == test_output + + +def test_sort_within_section(): + '''Test to ensure its possible to force isort to sort within sections''' + test_input = ('from Foob import ar\n' + 'import foo\n' + 'from foo import bar\n' + 'from foo.bar import Quux, baz\n') + test_output = SortImports(file_contents=test_input,force_sort_within_sections=True).output + assert test_output == test_input + + test_input = ('import foo\n' + 'from foo import bar\n' + 'from foo.bar import baz\n' + 'from foo.bar import Quux\n' + 'from Foob import ar\n') + test_output = SortImports(file_contents=test_input,force_sort_within_sections=True, order_by_type=False, + force_single_line=True).output + assert test_output == test_input + + +def test_sorting_with_two_top_comments(): + '''Test to ensure isort will sort files that contain 2 top comments''' + test_input = ('#! comment1\n' + "''' comment2\n" + "'''\n" + 'import b\n' + 'import a\n') + assert SortImports(file_contents=test_input).output == ('#! comment1\n' + "''' comment2\n" + "'''\n" + 'import a\n' + 'import b\n')