From 2ca5460c307bc0cced18a7881926fe43157ba423 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 15 Jan 2018 15:03:28 +0100 Subject: [PATCH] Move FalconSocial pre-commit-python-sorter to offical pre-commit repo, thanks @Dinoshauer @teeberg @amureki @jeffshek @Tenzer @asottile --- .pre-commit-hooks.yaml | 7 +++ README.md | 1 + hooks.yaml | 6 +++ pre_commit_hooks/sort_python_imports.py | 69 +++++++++++++++++++++++++ setup.py | 2 + tests/sort_python_imports_test.py | 45 ++++++++++++++++ 6 files changed, 130 insertions(+) create mode 100644 pre_commit_hooks/sort_python_imports.py create mode 100644 tests/sort_python_imports_test.py diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index f1a901e..0bfaf0b 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -247,6 +247,13 @@ entry: requirements-txt-fixer language: python files: requirements.*\.txt$ +- id: python-import-sorter + name: Sort python imports + description: This hook sorts python imports. + entry: python-import-sorter + language: python + files: \.py$ + minimum_pre_commit_version: 0.15.0 - id: sort-simple-yaml name: Sort simple YAML files language: python diff --git a/README.md b/README.md index cd69edc..e3acea1 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Add this to your `.pre-commit-config.yaml` - `no-commit-to-branch` - Protect specific branches from direct checkins. - Use `args: -b ` to set the branch. `master` is the default if no argument is set. - `pyflakes` - Run pyflakes on your python files. +- `python-import-sorter` - Sorts Python imports with isort. - `pretty-format-json` - Checks that all your JSON files are pretty. "Pretty" here means that keys are sorted and indented. You can configure this with the following commandline options: diff --git a/hooks.yaml b/hooks.yaml index 4552fa7..6719a9d 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -172,6 +172,12 @@ entry: upgrade-your-pre-commit-version files: '' minimum_pre_commit_version: 0.15.0 +- id: python-import-sorter + language: system + name: upgrade-your-pre-commit-version + entry: upgrade-your-pre-commit-version + files: '' + minimum_pre_commit_version: 0.15.0 - id: sort-simple-yaml language: system name: upgrade-your-pre-commit-version diff --git a/pre_commit_hooks/sort_python_imports.py b/pre_commit_hooks/sort_python_imports.py new file mode 100644 index 0000000..fa68c13 --- /dev/null +++ b/pre_commit_hooks/sort_python_imports.py @@ -0,0 +1,69 @@ +""" +Migrated from https://github.com/FalconSocial/pre-commit-python-sorter/ +by @benjaoming + +Thanks to FalconSocial devs for starting this. +Original author: Kasper Jacobsen, @Dinoshauer + +The MIT License (MIT) + +Copyright (c) 2014 Falcon Social + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +import argparse +import os + +from isort import isort + + +def imports_incorrect(filename, show_diff=False): + return isort.SortImports(filename, check=True, show_diff=show_diff).incorrectly_sorted + + +def main(argv=None): + + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='*', help='Filenames to run') + parser.add_argument('--silent-overwrite', action='store_true', dest='silent', default=False) + parser.add_argument('--check-only', action='store_true', dest='check_only', default=False) + parser.add_argument('--diff', action='store_true', dest='show_diff', default=False) + args = parser.parse_args(argv) + + return_value = 0 + + for filename in args.filenames: + if imports_incorrect(filename, show_diff=args.show_diff): + if args.check_only: + return_value = 1 + elif args.silent: + isort.SortImports(filename) + else: + return_value = 1 + isort.SortImports(filename) + print('FIXED: {}'.format(os.path.abspath(filename))) + return return_value + + +if __name__ == '__main__': + exit(main()) diff --git a/setup.py b/setup.py index 662ec62..d62a310 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ setup( install_requires=[ # quickfix to prevent pycodestyle conflicts 'flake8!=2.5.3', + 'isort>=4.1.1,<4.3', 'autopep8>=1.3', 'pyyaml', 'six', @@ -58,6 +59,7 @@ setup( 'name-tests-test = pre_commit_hooks.tests_should_end_in_test:validate_files', 'no-commit-to-branch = pre_commit_hooks.no_commit_to_branch:main', 'pretty-format-json = pre_commit_hooks.pretty_format_json:pretty_format_json', + 'python-import-sorter = pre_commit_hooks.sort_python_imports:main', 'requirements-txt-fixer = pre_commit_hooks.requirements_txt_fixer:fix_requirements_txt', 'sort-simple-yaml = pre_commit_hooks.sort_simple_yaml:main', 'trailing-whitespace-fixer = pre_commit_hooks.trailing_whitespace_fixer:fix_trailing_whitespace', diff --git a/tests/sort_python_imports_test.py b/tests/sort_python_imports_test.py new file mode 100644 index 0000000..fdf96b7 --- /dev/null +++ b/tests/sort_python_imports_test.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + +from sys import stdout + +import pytest + +from pre_commit_hooks.sort_python_imports import main + + +def write_file(filename, contents): + with open(filename, 'w') as file_obj: + file_obj.write(contents) + + +@pytest.fixture +def tmpfiles(tmpdir): + write_file(tmpdir.join('correct_1.py').strpath, 'import json\nimport sys\n') + write_file(tmpdir.join('incorrect_1.py').strpath, 'import sys\n\n\nimport json\n') + write_file(tmpdir.join('incorrect_2.py').strpath, 'import sys\n\n\nimport json\n') + return tmpdir + + +def test_sort(tmpfiles): + assert main([tmpfiles.join('correct_1.py').strpath]) == 0 + assert main([tmpfiles.join('incorrect_1.py').strpath]) == 1 + assert main([tmpfiles.join('incorrect_2.py').strpath, '--check-only']) == 1 + assert main([tmpfiles.join('incorrect_2.py').strpath, '--silent-overwrite']) == 0 + + +def test_sort_with_diff(tmpfiles): + filename = tmpfiles.join('incorrect_1.py').strpath + main(['--diff', '--check-only', filename]) + + stdout.seek(0) + lines = stdout.read().decode("utf-8").splitlines() + # Skip diff header + lines = "\n".join(lines[4:]) + assert lines == ( + '+import json\n' + ' import sys\n' + '-\n' + '-\n' + '-import json' + )