Skip to content

Commit

Permalink
Merge branch 'release/4.2.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
timothycrosley committed Mar 29, 2016
2 parents 2282150 + 51516fc commit ee034d0
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 12 deletions.
1 change: 1 addition & 0 deletions ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Notable Bug Reporters
- Chris Adams (@acdha)
- @OddBloke
- Martin Geisler (@mgeisler)
- Tim Heap (@timheap)

Code Contributors
===================
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ Changelog

### 4.2.3
- Fixed a large number of priority bugs - bug fix only release

### 4.2.4
- Fixed an issue that caused module that contained functions before doc strings, to incorrectly place imports
- Fixed regression in how `force_alphabetical_sort` was being interpretted (issue #409)
- Fixed stray print statement printing skipped files (issue #411)
- Added option for forcing imports into a single bucket: `no_sections`
- Added option for new lines between import types (from, straight): `lines_between_sections`
2 changes: 1 addition & 1 deletion isort/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
from . import settings
from .isort import SortImports

__version__ = "4.2.3"
__version__ = "4.2.4"
25 changes: 22 additions & 3 deletions isort/isort.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def __init__(self, file_path=None, file_contents=None, write_to_stdout=False, ch
else:
self.config[key] = value

if self.config.get('force_alphabetical_sort', False):
self.config.update({'force_alphabetical_sort_within_sections': True,
'no_sections': True,
'lines_between_types': 1,
'from_first': True})

indent = str(self.config['indent'])
if indent.isdigit():
indent = " " * int(indent)
Expand Down Expand Up @@ -451,10 +457,19 @@ def _add_formatted_imports(self):
(at the index of the first import) sorted alphabetically and split between groups
"""
sort_ignore_case = self.config.get('force_alphabetical_sort', False)
sort_ignore_case = self.config.get('force_alphabetical_sort_within_sections', False)
sections = itertools.chain(self.sections, self.config['forced_separate'])

sections = itertools.chain(self.sections, self.config['forced_separate'])
if self.config.get('no_sections', False):
self.imports['no_sections'] = {'straight': [], 'from': {}}
for section in sections:
self.imports['no_sections']['straight'].extend(self.imports[section].get('straight', []))
self.imports['no_sections']['from'].update(self.imports[section].get('from', {}))
sections = ('no_sections', )

output = []
for section in itertools.chain(self.sections, self.config['forced_separate']):
for section in sections:
straight_modules = list(self.imports[section]['straight'])
straight_modules = nsorted(straight_modules, key=lambda key: self._module_key(key, self.config))
from_modules = sorted(list(self.imports[section]['from'].keys()))
Expand All @@ -463,9 +478,13 @@ def _add_formatted_imports(self):
section_output = []
if self.config.get('from_first', False):
self._add_from_imports(from_modules, section, section_output, sort_ignore_case)
if self.config.get('lines_between_types', 0) and from_modules and straight_modules:
section_output.extend([''] * self.config['lines_between_types'])
self._add_straight_imports(straight_modules, section, section_output)
else:
self._add_straight_imports(straight_modules, section, section_output)
if self.config.get('lines_between_types', 0) and from_modules and straight_modules:
section_output.extend([''] * self.config['lines_between_types'])
self._add_from_imports(from_modules, section, section_output, sort_ignore_case)

if self.config.get('force_sort_within_sections', False):
Expand Down Expand Up @@ -658,7 +677,7 @@ def _skip_line(self, line):

if '"' in line or "'" in line:
index = 0
if self._first_comment_index_start == -1:
if self._first_comment_index_start == -1 and (line.startswith('"') or line.startswith("'")):
self._first_comment_index_start = self.index
while index < len(line):
if line[index] == "\\":
Expand Down
7 changes: 6 additions & 1 deletion isort/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ def create_parser():
help='Forces all from imports to appear on their own line')
parser.add_argument('--force_single_line_imports', dest='force_single_line', action='store_true',
help=argparse.SUPPRESS)
parser.add_argument('-ds', '--no-sections', help='Put all imports into the same section bucket', dest='no_sections',
action='store_true')
parser.add_argument('-sd', '--section-default', dest='default_section',
help='Sets the default section for imports (by default FIRSTPARTY) options: ' +
str(DEFAULT_SECTIONS))
Expand Down Expand Up @@ -220,11 +222,14 @@ def create_parser():
help="Specifies how long lines that are wrapped should be, if not set line_length is used.")
parser.add_argument('-fgw', '--force-grid-wrap', action='store_true', dest="force_grid_wrap",
help='Force from imports to be grid wrapped regardless of line length')
parser.add_argument('-fass', '--force-alphabetical-sort-within-sections', action='store_true',
dest="force_alphabetical_sort", help='Force all imports to be sorted alphabetically within a '
'section')
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, independent of import_type')

parser.add_argument('-lbt', '--lines-between-types', dest='lines_between_types', type=int)

arguments = dict((key, value) for (key, value) in itemsview(vars(parser.parse_args())) if value)
if 'dont_order_by_type' in arguments:
Expand Down
4 changes: 3 additions & 1 deletion isort/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
'line_length': 79,
'wrap_length': 0,
'sections': DEFAULT_SECTIONS,
'no_sections': False,
'known_future_library': ['__future__'],
'known_standard_library': ["abc", "anydbm", "argparse", "array", "asynchat", "asyncore", "atexit", "base64",
"BaseHTTPServer", "bisect", "bz2", "calendar", "cgitb", "cmd", "codecs",
Expand Down Expand Up @@ -90,13 +91,15 @@
'atomic': False,
'lines_after_imports': -1,
'lines_between_sections': 1,
'lines_between_types': 0,
'combine_as_imports': False,
'combine_star': False,
'include_trailing_comma': False,
'from_first': False,
'verbose': False,
'quiet': False,
'force_adds': False,
'force_alphabetical_sort_within_sections': False,
'force_alphabetical_sort': False,
'force_grid_wrap': False,
'force_sort_within_sections': False,
Expand Down Expand Up @@ -203,7 +206,6 @@ def should_skip(filename, config, path='/'):
"""Returns True if the file should be skipped based on the passed in settings."""
for skip_path in config['skip']:
if os.path.join(path, filename).endswith('/' + skip_path.lstrip('/')):
print(skip_path)
return True

position = os.path.split(filename)
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ def run(self):
readme = f.read()

setup(name='isort',
version='4.2.3',
version='4.2.4',
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.3.tar.gz',
download_url='https://github.com/timothycrosley/isort/archive/4.2.4.tar.gz',
license="MIT",
entry_points={
'console_scripts': [
Expand Down
39 changes: 35 additions & 4 deletions test_isort.py
Original file line number Diff line number Diff line change
Expand Up @@ -1509,7 +1509,7 @@ def test_alphabetic_sorting():
"from Products.CMFPlone import utils\n"
)
options = {'force_single_line': True,
'force_alphabetical_sort': True, }
'force_alphabetical_sort_within_sections': True, }

output = SortImports(file_contents=test_input, **options).output
assert output == test_input
Expand All @@ -1523,7 +1523,7 @@ def test_alphabetic_sorting_multi_line():
"""Test to ensure isort correctly handles multiline import see: issue 364"""
test_input = ("from a import (CONSTANT_A, cONSTANT_B, CONSTANT_C, CONSTANT_D, CONSTANT_E,\n"
" CONSTANT_F, CONSTANT_G, CONSTANT_H, CONSTANT_I, CONSTANT_J)\n")
options = {'force_alphabetical_sort': True, }
options = {'force_alphabetical_sort_within_sections': True, }
assert SortImports(file_contents=test_input, **options).output == test_input


Expand Down Expand Up @@ -1608,7 +1608,7 @@ def test_sections_parsed_correct():
def test_alphabetic_sorting_no_newlines():
'''Test to ensure that alphabetical sort does not erroneously introduce new lines (issue #328)'''
test_input = "import os\n"
test_output = SortImports(file_contents=test_input,force_alphabetical_sort=True).output
test_output = SortImports(file_contents=test_input,force_alphabetical_sort_within_sections=True).output
assert test_input == test_output

test_input = ('import os\n'
Expand All @@ -1618,7 +1618,7 @@ def test_alphabetic_sorting_no_newlines():
'\n'
'\n'
'print(1)\n')
test_output = SortImports(file_contents=test_input,force_alphabetical_sort=True, lines_after_imports=2).output
test_output = SortImports(file_contents=test_input,force_alphabetical_sort_within_sections=True, lines_after_imports=2).output
assert test_input == test_output


Expand Down Expand Up @@ -1751,3 +1751,34 @@ def test_import_by_paren_issue_375():
' Bar,\n'
')\n')
assert SortImports(file_contents=test_input).output == 'from .models import Bar, Foo\n'


def test_function_with_docstring():
"""Test to ensure isort can correctly sort imports when the first found content is a function with a docstring"""
add_imports = ['from __future__ import unicode_literals']
test_input = ('def foo():\n'
' """ Single line triple quoted doctring """\n'
' pass\n')
expected_output = ('from __future__ import unicode_literals\n'
'\n'
'\n'
'def foo():\n'
' """ Single line triple quoted doctring """\n'
' pass\n')
assert SortImports(file_contents=test_input, add_imports=add_imports).output == expected_output


def test_plone_style():
"""Test to ensure isort correctly plone style imports"""
test_input = ("from django.contrib.gis.geos import GEOSException\n"
"from plone.app.testing import getRoles\n"
"from plone.app.testing import ManageRoles\n"
"from plone.app.testing import setRoles\n"
"from Products.CMFPlone import utils\n"
"\n"
"import ABC\n"
"import unittest\n"
"import Zope\n")
options = {'force_single_line': True,
'force_alphabetical_sort': True}
assert SortImports(file_contents=test_input, **options).output == test_input

0 comments on commit ee034d0

Please sign in to comment.