From 1ba36cb21f431b2c0a4b6d8fad384e46cf78767f Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Tue, 26 Feb 2019 02:20:09 +0300 Subject: extract config preparation in separate function --- isort/isort.py | 39 +++++++-------------------------------- isort/settings.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/isort/isort.py b/isort/isort.py index ca28d56d..0fd9ae82 100644 --- a/isort/isort.py +++ b/isort/isort.py @@ -34,6 +34,8 @@ from datetime import datetime from difflib import unified_diff from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple +from isort import utils + from . import settings from .finders import FindersManager from .natural import nsorted @@ -46,7 +48,6 @@ if TYPE_CHECKING: 'straight': Dict[str, Any], 'from': Dict[str, Any] }) - CommentsDict = TypedDict('CommentsDict', { 'from': Dict[str, Any], 'straight': Dict[str, Any], @@ -74,41 +75,14 @@ class SortImports(object): settings_path = os.path.dirname(os.path.abspath(file_path)) settings_path = settings_path or os.getcwd() - self.config = settings.from_path(settings_path).copy() - for key, value in setting_overrides.items(): - access_key = key.replace('not_', '').lower() - # The sections config needs to retain order and can't be converted to a set. - if access_key != 'sections' and type(self.config.get(access_key)) in (list, tuple): - if key.startswith('not_'): - self.config[access_key] = list(set(self.config[access_key]).difference(value)) - else: - self.config[access_key] = list(set(self.config[access_key]).union(value)) - else: - self.config[key] = value - - if self.config['force_alphabetical_sort']: - 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) - else: - indent = indent.strip("'").strip('"') - if indent.lower() == "tab": - indent = "\t" - self.config['indent'] = indent - - self.config['comment_prefix'] = self.config['comment_prefix'].strip("'").strip('"') + self.config = settings.prepare_config(settings_path, **setting_overrides) self.place_imports = {} # type: Dict[str, List[str]] self.import_placements = {} # type: Dict[str, str] self.remove_imports = [self._format_simplified(removal) for removal in self.config['remove_imports']] self.add_imports = [self._format_natural(addition) for addition in self.config['add_imports']] - self._section_comments = ["# " + value for key, value in self.config.items() if - key.startswith('import_heading') and value] + self._section_comments = ["# " + value for key, value in self.config.items() + if key.startswith('import_heading') and value] self.file_encoding = 'utf-8' file_name = file_path @@ -143,6 +117,7 @@ class SortImports(object): self.line_separator = '\r' else: self.line_separator = '\n' + self.in_lines = file_contents.split(self.line_separator) self.original_length = len(self.in_lines) if (self.original_length > 1 or self.in_lines[:1] not in ([], [""])) or self.config['force_adds']: @@ -535,7 +510,7 @@ class SortImports(object): import_statement = new_import_statement line_length -= 1 new_import_statement = formatter(import_start, copy.copy(from_imports), - dynamic_indent, indent, line_length, comments) + dynamic_indent, indent, line_length, comments) lines = new_import_statement.split(self.line_separator) if import_statement.count(self.line_separator) == 0: return self._wrap(import_statement) diff --git a/isort/settings.py b/isort/settings.py index c1d17802..9c577556 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -184,6 +184,38 @@ def from_path(path: str) -> Dict[str, Any]: return computed_settings +def prepare_config(settings_path: str, **setting_overrides: Any) -> Dict[str, Any]: + config = from_path(settings_path).copy() + for key, value in setting_overrides.items(): + access_key = key.replace('not_', '').lower() + # The sections config needs to retain order and can't be converted to a set. + if access_key != 'sections' and type(config.get(access_key)) in (list, tuple): + if key.startswith('not_'): + config[access_key] = list(set(config[access_key]).difference(value)) + else: + config[access_key] = list(set(config[access_key]).union(value)) + else: + config[key] = value + + if config['force_alphabetical_sort']: + config.update({'force_alphabetical_sort_within_sections': True, + 'no_sections': True, + 'lines_between_types': 1, + 'from_first': True}) + + indent = str(config['indent']) + if indent.isdigit(): + indent = " " * int(indent) + else: + indent = indent.strip("'").strip('"') + if indent.lower() == "tab": + indent = "\t" + config['indent'] = indent + + config['comment_prefix'] = config['comment_prefix'].strip("'").strip('"') + return config + + def _update_settings_with_config( path: str, name: str, -- cgit v1.2.1 From 3825fabc9ba74f793a43257b244fe299500e0eb0 Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Tue, 26 Feb 2019 02:21:14 +0300 Subject: rename should_skip -> file_should_be_skipped --- isort/isort.py | 2 +- isort/main.py | 8 ++++---- isort/settings.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/isort/isort.py b/isort/isort.py index 0fd9ae82..5635133b 100644 --- a/isort/isort.py +++ b/isort/isort.py @@ -89,7 +89,7 @@ class SortImports(object): self.file_path = file_path or "" if file_path: file_path = os.path.abspath(file_path) - if settings.should_skip(file_path, self.config): + if settings.file_should_be_skipped(file_path, self.config): self.skipped = True if self.config['verbose']: print("WARNING: {0} was skipped as it's listed in 'skip' setting" diff --git a/isort/main.py b/isort/main.py index a2d4b935..094b752c 100644 --- a/isort/main.py +++ b/isort/main.py @@ -28,7 +28,7 @@ from typing import Any, Dict, Iterable, Iterator, List, MutableMapping, Optional import setuptools from isort import SortImports, __version__ -from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, should_skip +from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, file_should_be_skipped INTRO = r""" /#######################################################################\ @@ -96,7 +96,7 @@ def iter_source_code(paths: Iterable[str], config: MutableMapping[str, Any], ski for path in paths: if os.path.isdir(path): - if should_skip(path, config, os.getcwd()): + if file_should_be_skipped(path, config, os.getcwd()): skipped.append(path) continue @@ -104,13 +104,13 @@ def iter_source_code(paths: Iterable[str], config: MutableMapping[str, Any], ski path, topdown=True, followlinks=True ): for dirname in list(dirnames): - if should_skip(dirname, config, dirpath): + if file_should_be_skipped(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): + if file_should_be_skipped(filename, config, dirpath): skipped.append(filename) else: yield filepath diff --git a/isort/settings.py b/isort/settings.py index 9c577556..29d36be5 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -363,7 +363,7 @@ def _get_config_data(file_path: str, sections: Iterable[str]) -> Dict[str, Any]: return settings -def should_skip( +def file_should_be_skipped( filename: str, config: Mapping[str, Any], path: str = '/' -- cgit v1.2.1 From 9338202167e7b3fa6c2ead9079aba1058066e768 Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Tue, 26 Feb 2019 02:22:09 +0300 Subject: extract infer_line_separator method --- isort/isort.py | 7 +------ isort/utils.py | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/isort/isort.py b/isort/isort.py index 5635133b..2b5a4a13 100644 --- a/isort/isort.py +++ b/isort/isort.py @@ -111,12 +111,7 @@ class SortImports(object): if self.config['line_ending']: self.line_separator = self.config['line_ending'] else: - if '\r\n' in file_contents: - self.line_separator = '\r\n' - elif '\r' in file_contents: - self.line_separator = '\r' - else: - self.line_separator = '\n' + self.line_separator = utils.infer_line_separator(file_contents) self.in_lines = file_contents.split(self.line_separator) self.original_length = len(self.in_lines) diff --git a/isort/utils.py b/isort/utils.py index 1f1b5143..0f6f57bf 100644 --- a/isort/utils.py +++ b/isort/utils.py @@ -52,3 +52,12 @@ def difference(a: Iterable[Any], b: Container[Any]) -> List[Any]: if item not in b: d.append(item) return d + + +def infer_line_separator(file_contents: str) -> str: + if '\r\n' in file_contents: + return '\r\n' + elif '\r' in file_contents: + return '\r' + else: + return '\n' -- cgit v1.2.1 From 115c1a8a9949b1c605bfdf33e5f9a60c5d492cfc Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Tue, 26 Feb 2019 23:30:07 +0300 Subject: rename original_length -> original_num_of_lines --- isort/isort.py | 8 ++++---- isort/main.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/isort.py b/isort/isort.py index 2b5a4a13..e7e8c2a2 100644 --- a/isort/isort.py +++ b/isort/isort.py @@ -114,8 +114,8 @@ class SortImports(object): self.line_separator = utils.infer_line_separator(file_contents) self.in_lines = file_contents.split(self.line_separator) - self.original_length = len(self.in_lines) - if (self.original_length > 1 or self.in_lines[:1] not in ([], [""])) or self.config['force_adds']: + self.original_num_of_lines = len(self.in_lines) + if (self.original_num_of_lines > 1 or self.in_lines[:1] not in ([], [""])) or self.config['force_adds']: for add_import in self.add_imports: self.in_lines.append(add_import) self.number_of_lines = len(self.in_lines) @@ -139,7 +139,7 @@ class SortImports(object): self._parse() if self.import_index != -1: self._add_formatted_imports() - self.length_change = len(self.out_lines) - self.original_length + self.length_change = len(self.out_lines) - self.original_num_of_lines while self.out_lines and self.out_lines[-1].strip() == "": self.out_lines.pop(-1) self.out_lines.append("") @@ -583,7 +583,7 @@ class SortImports(object): output.pop(0) output_at = 0 - if self.import_index < self.original_length: + if self.import_index < self.original_num_of_lines: output_at = self.import_index elif self._first_comment_index_end != -1 and self._first_comment_index_start <= 2: output_at = self._first_comment_index_end diff --git a/isort/main.py b/isort/main.py index 094b752c..2fef41bf 100644 --- a/isort/main.py +++ b/isort/main.py @@ -28,7 +28,7 @@ from typing import Any, Dict, Iterable, Iterator, List, MutableMapping, Optional import setuptools from isort import SortImports, __version__ -from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, file_should_be_skipped +from isort.settings import DEFAULT_SECTIONS, WrapModes, default, file_should_be_skipped, from_path INTRO = r""" /#######################################################################\ -- cgit v1.2.1 From 6dfabb9901280e3aff2cb51d4c06acba575f2832 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 3 Mar 2019 12:45:42 -0800 Subject: Pull request to merge in latest hotfix master changes into develop branch --- .travis.yml | 9 ++++++++- CHANGELOG.md | 13 +++++++++++-- isort/__init__.py | 2 +- isort/settings.py | 2 +- scripts/before_install.sh | 28 ++++++++++++++++++++++++++++ setup.py | 2 +- 6 files changed, 50 insertions(+), 6 deletions(-) create mode 100755 scripts/before_install.sh diff --git a/.travis.yml b/.travis.yml index 0cd3f4c2..5c96e81e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,16 @@ matrix: - python: 3.7 env: TOXENV=py37-coverage - python: pypy3.5-6.0 - env: TOXENV=pypy3 + - os: osx + language: generic + env: TOXENV=py36 + +before_install: + - ./scripts/before_install.sh + install: - pip install tox + script: - tox after_success: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6635a5af..a19bee52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,19 @@ Internal: Planned: - profile support for common project types (black, django, google, etc) -### 4.3.9 - Feburary 25, 2019 - hot fix release +### 4.3.10 - March 2, 2019 - hot fix release +- Fixed Windows incompatibilities (Issue #835) +- Fixed relative import sorting bug (Issue #417) +- Fixed "no_lines_before" to also be respected from previous empty sections. +- Fixed slow-down introduced by finders mechanism by adding a LRU cache (issue #848) +- Fixed issue #842 default encoding not-set in Python2 +- Restored Windows automated testing +- Added Mac automated testing + +### 4.3.9 - February 25, 2019 - hot fix release - Fixed a bug that led to an incompatibility with black: #831 -### 4.3.8 - Feburary 25, 2019 - hot fix release +### 4.3.8 - February 25, 2019 - hot fix release - Fixed a bug that led to the recursive option not always been available from the command line. ### 4.3.7 - February 25, 2019 - hot fix release diff --git a/isort/__init__.py b/isort/__init__.py index 63034de6..e3af789b 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -22,4 +22,4 @@ OTHER DEALINGS IN THE SOFTWARE. from . import settings # noqa: F401 from .isort import SortImports # noqa: F401 -__version__ = "4.3.9" +__version__ = "4.3.10" diff --git a/isort/settings.py b/isort/settings.py index 06d1e70d..048b0bb7 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -315,7 +315,7 @@ def _get_config_data(file_path: str, sections: Iterable[str]) -> Dict[str, Any]: settings.update(config_section) else: warnings.warn( - "Found %s but toml package is not installed. To configure" + "Found %s but toml package is not installed. To configure " "isort with %s, install with 'isort[pyproject]'." % (file_path, file_path) ) else: diff --git a/scripts/before_install.sh b/scripts/before_install.sh new file mode 100755 index 00000000..fa86a770 --- /dev/null +++ b/scripts/before_install.sh @@ -0,0 +1,28 @@ +#! /bin/bash + +echo $TRAVIS_OS_NAME + + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + + # Travis has an old version of pyenv by default, upgrade it + brew update > /dev/null 2>&1 + brew outdated pyenv || brew upgrade pyenv + + pyenv --version + + # Find the latest requested version of python + case "$TOXENV" in + py34) + python_minor=4;; + py35) + python_minor=5;; + py36) + python_minor=6;; + py37) + python_minor=7;; + esac + latest_version=`pyenv install --list | grep -e "^[ ]*3\.$python_minor" | tail -1` + + pyenv install $latest_version + pyenv local $latest_version +fi diff --git a/setup.py b/setup.py index 6858ad4e..3a4360b7 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open('README.rst') as f: readme = f.read() setup(name='isort', - version='4.3.9', + version='4.3.10', description='A Python utility / library to sort Python imports.', long_description=readme, author='Timothy Crosley', -- cgit v1.2.1 From d1a4681c418c61356289e29b9235aa23acfecaff Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 3 Mar 2019 13:00:06 -0800 Subject: Re-add missing environment varieable --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5c96e81e..4b6ded30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ matrix: - python: 3.7 env: TOXENV=py37-coverage - python: pypy3.5-6.0 + env: TOXENV=pypy3 - os: osx language: generic env: TOXENV=py36 -- cgit v1.2.1 From 638746f219dbf9f12fdeb783f062c8d8d09c4152 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 3 Mar 2019 20:00:36 -0800 Subject: Add downloads link --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 90e4f9a5..f87c4504 100644 --- a/README.rst +++ b/README.rst @@ -23,6 +23,9 @@ :alt: Join the chat at https://gitter.im/timothycrosley/isort :target: https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +.. image:: https://pepy.tech/badge/isort + :alt: Downloads + :target: https://pepy.tech/project/isort isort your python imports for you so you don't have to. -- cgit v1.2.1