diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 93c3b53..369ce93 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,48 +1,91 @@ -name: Tests - +name: Testing on: push: branches: [master] pull_request: branches: [master] - paths: - - "flake8_isort.py" - - "test_flake8_isort.py" - - "setup.py" - - ".github/workflows/tests.yml" - +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - tests: + test: + name: py-${{ matrix.python-version }}/isort-${{ matrix.isort }}/flake8-${{ matrix.flake8 }} runs-on: ubuntu-latest - name: Python ${{ matrix.python-version }} x isort ${{ matrix.isort }} x flake8 ${{ matrix.flake8 }} - strategy: matrix: - python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy3'] - isort: [4.3.21, 5.10.1] - flake8: [3.9.2, 4.0.1, 5.0.4] + python-version: ["3.10", 3.9, 3.8, 3.7, pypy-3.9] + isort: [5.10.1] + flake8: [5.0.4] + include: + - python-version: 3.9 + isort: 5.10.1 + flake8: 4.0.1 + qa: 'true' + - python-version: 3.9 + isort: 5.10.1 + flake8: 3.9.2 + - python-version: 3.9 + isort: 4.3.21 + flake8: 5.0.4 + - python-version: 3.9 + isort: 4.3.21 + flake8: 4.0.1 + - python-version: 3.9 + isort: 4.3.21 + flake8: 3.9.2 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - architecture: x64 - - name: Install matrix dependencies - run: pip install 'isort==${{ matrix.isort }}' 'flake8==${{ matrix.flake8 }}' + - name: Cache packages + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.python-version }}- + - name: pip version + run: pip --version + - name: Install dependencies + run: | + python -m pip install -r requirements.txt + pip install 'isort==${{ matrix.isort }}' 'flake8==${{ matrix.flake8 }}' # isort 4.x requires `toml` to be able to read pyproject.toml, so install it... - name: Install toml if required run: pip install toml if: matrix.isort == '4.3.21' - - name: Install dependencies - run: pip install .[test] - - name: flake8 - run: flake8 *.py - - name: pytest - run: pytest -v --cov flake8_isort --cov-report term-missing - - name: Upload coverage + # formatters + - name: Run pyupgrade + if: matrix.qa == 'true' + run: pyupgrade --py37-plus *.py + - name: Run isort + if: matrix.qa == 'true' + run: isort --check-only *.py + - name: Run black + if: matrix.qa == 'true' + run: black --check --skip-string-normalization *.py + # linters + - name: Lint with bandit + if: matrix.qa == 'true' + run: bandit --skip B101 *.py # B101 is assert statements + - name: Lint with codespell + if: matrix.qa == 'true' + run: codespell *.rst *.py + - name: Lint with flake8 + if: matrix.qa == 'true' + run: flake8 *.py --count --max-complexity=18 --max-line-length=88 --show-source --statistics + - name: Lint with mypy + if: matrix.qa == 'true' run: | - python -m pip install coveralls - coveralls --service=github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + mkdir --parents --verbose .mypy_cache + mypy --ignore-missing-imports --install-types --non-interactive *.py || true + - name: Lint with safety + if: matrix.qa == 'true' + run: safety check || true + # tests and coverage + - name: Test + run: pytest run_tests.py --cov --cov-report term-missing + - name: Coverage + run: coveralls --service=github diff --git a/.gitignore b/.gitignore index 73d713b..f85267e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ *.egg-info *.pyc +.cache .coverage .installed.cfg +.hypothesis bin develop-eggs include diff --git a/CHANGES.rst b/CHANGES.rst index a8d59ea..cf06ca2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,8 +6,13 @@ Changelog 4.2.1 (unreleased) ------------------ -- Nothing changed yet. +- Update dependencies. [gforcada] + +- Revamp GitHub actions. [gforcada] + +- Drop python 3.6, and add python 3.10. [gforcada] +- Use linters and formatters to keep code sane and beautiful. [gforcada] 4.2.0 (2022-08-04) ------------------ diff --git a/LICENSE.rst b/LICENSE.rst deleted file mode 100644 index 334d8e0..0000000 --- a/LICENSE.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. -*- coding: utf-8 -*- - -flake8-isort Copyright 2015, Gil Forcada - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, -MA 02111-1307 USA. diff --git a/MANIFEST.in b/MANIFEST.in index 98fd89b..c64aae7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,11 @@ -include MANIFEST.in LICENSE *.rst *.py *.cfg *.ini -exclude .installed.cfg .coveragerc *.pyc +include MANIFEST.in +include LICENSE +include setup.cfg +include *.rst +include *.py +include *requirements.txt +exclude .installed.cfg +exclude .coveragerc +exclude *.pyc +exclude *.in diff --git a/README.rst b/README.rst index 396cb8f..5ffd4dc 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,9 @@ .. -*- coding: utf-8 -*- -.. image:: https://github.com/gforcada/flake8-isort/actions/workflows/tests.yml/badge.svg?branch=master - :target: https://github.com/gforcada/flake8-isort/actions/workflows/tests.yml +.. image:: https://github.com/gforcada/flake8-isort/actions/workflows/testing.yml/badge.svg?branch=master + :target: https://github.com/gforcada/flake8-isort/actions/workflows/testing.yml -.. image:: https://coveralls.io/repos/gforcada/flake8-isort/badge.svg?branch=master&service=github +.. image:: https://coveralls.io/repos/gforcada/flake8-isort/badge.svg?branch=master :target: https://coveralls.io/github/gforcada/flake8-isort?branch=master Flake8 meet isort @@ -47,7 +47,7 @@ Error codes Requirements ------------ -- Python 2.7, 3.5, 3.6, pypy or pypy3 +- Python 3.7, 3.8, 3.9, 3.10 and pypy3 - flake8 - isort diff --git a/flake8_isort.py b/flake8_isort.py index 88de2ed..ab96cce 100644 --- a/flake8_isort.py +++ b/flake8_isort.py @@ -1,36 +1,20 @@ -# -*- coding: utf-8 -*- - +import warnings from contextlib import redirect_stdout -from difflib import Differ -from difflib import unified_diff +from difflib import Differ, unified_diff from io import StringIO from pathlib import Path import isort -import warnings -__version__ = '4.2.1.dev0' - - -class Flake8IsortBase(object): +class Flake8IsortBase: name = 'flake8_isort' - version = __version__ - isort_unsorted = ( - 'I001 isort found an import in the wrong position' - ) - no_config_msg = ( - 'I002 no configuration found (.isort.cfg or [isort] in configs)' - ) - isort_blank_req = ( - 'I003 isort expected 1 blank line in imports, found 0' - ) - isort_blank_unexp = ( - 'I004 isort found an unexpected blank line in imports' - ) - isort_add_unexp = ( - 'I005 isort found an unexpected missing import' - ) + version = '4.2.1' + isort_unsorted = 'I001 isort found an import in the wrong position' + no_config_msg = 'I002 no configuration found (.isort.cfg or [isort] in configs)' + isort_blank_req = 'I003 isort expected 1 blank line in imports, found 0' + isort_blank_unexp = 'I004 isort found an unexpected blank line in imports' + isort_add_unexp = 'I005 isort found an unexpected missing import' show_traceback = False stdin_display_name = None @@ -46,7 +30,7 @@ def add_options(cls, parser): '--isort-show-traceback', action='store_true', parse_from_config=True, - help='Show full traceback with diff from isort' + help='Show full traceback with diff from isort', ) @classmethod @@ -101,9 +85,7 @@ def sortimports_linenum_msg(self, sort_result): diff = differ.compare(sort_result.in_lines, sort_result.out_lines) line_num = 0 - additions = { - '+ {}'.format(add_import) for add_import in sort_result.add_imports - } + additions = {f'+ {add_import}' for add_import in sort_result.add_imports} for line in diff: if line.startswith(' ', 0, 2): line_num += 1 # Ignore unchanged lines but increment line_num. @@ -176,7 +158,8 @@ def _fixup_sortimports_wrapped(sort_imports): for idx, line in enumerate(sort_imports.out_lines): if '\n' in line: for new_idx, new_line in enumerate( - sort_imports.out_lines.pop(idx).splitlines()): + sort_imports.out_lines.pop(idx).splitlines() + ): sort_imports.out_lines.insert(idx + new_idx, new_line) @@ -186,12 +169,10 @@ class Flake8Isort5(Flake8IsortBase): def run(self): if self.filename is not self.stdin_display_name: file_path = Path(self.filename) - isort_config = isort.settings.Config( - settings_path=file_path.parent) + isort_config = isort.settings.Config(settings_path=file_path.parent) else: file_path = None - isort_config = isort.settings.Config( - settings_path=Path.cwd()) + isort_config = isort.settings.Config(settings_path=Path.cwd()) input_string = ''.join(self.lines) traceback = '' isort_changed = False @@ -204,19 +185,23 @@ def run(self): input_stream=input_stream, output_stream=output_stream, config=isort_config, - file_path=file_path) + file_path=file_path, + ) except isort.exceptions.FileSkipped: pass except isort.exceptions.ISortError as e: warnings.warn(e) if isort_changed: outlines = output_stream.getvalue() - diff_delta = "".join(unified_diff( - input_string.splitlines(keepends=True), - outlines.splitlines(keepends=True), - fromfile="{}:before".format(self.filename), - tofile="{}:after".format(self.filename))) - traceback = (isort_stdout.getvalue() + "\n" + diff_delta) + diff_delta = ''.join( + unified_diff( + input_string.splitlines(keepends=True), + outlines.splitlines(keepends=True), + fromfile=f'{self.filename}:before', + tofile=f'{self.filename}:after', + ) + ) + traceback = f'{isort_stdout.getvalue()}\n{diff_delta}' for line_num, message in self.isort_linenum_msg(diff_delta): if self.show_traceback: message += traceback diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..dc2b48b --- /dev/null +++ b/requirements.in @@ -0,0 +1,23 @@ +bandit +black +codespell +coveralls +flake8-blind-except +flake8-bugbear +flake8-comprehensions +flake8-debugger +flake8-deprecated +flake8-isort +flake8-pep3101 +flake8-print +flake8-quotes +flake8-todo +importlib-metadata; python_version < '3.8' +isort +mypy +pytest +pytest-cov +pyupgrade +safety +typed-ast; python_version < '3.8' # dependency of black and mypy +zipp; python_version < '3.8' # dependency of importlib-metadata diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..15b1138 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,172 @@ +# +# This file is autogenerated by pip-compile with python 3.7 +# To update, run: +# +# pip-compile requirements.in +# +attrs==22.1.0 + # via + # flake8-bugbear + # pytest +bandit==1.7.4 + # via -r requirements.in +black==22.10.0 + # via -r requirements.in +certifi==2022.9.24 + # via requests +charset-normalizer==2.1.1 + # via requests +click==8.1.3 + # via + # black + # safety +codespell==2.2.1 + # via -r requirements.in +coverage[toml]==6.5.0 + # via + # coveralls + # pytest-cov +coveralls==3.3.1 + # via -r requirements.in +docopt==0.6.2 + # via coveralls +dparse==0.6.2 + # via safety +flake8==5.0.4 + # via + # flake8-bugbear + # flake8-comprehensions + # flake8-debugger + # flake8-deprecated + # flake8-isort + # flake8-pep3101 + # flake8-print + # flake8-quotes +flake8-blind-except==0.2.1 + # via -r requirements.in +flake8-bugbear==22.9.23 + # via -r requirements.in +flake8-comprehensions==3.10.0 + # via -r requirements.in +flake8-debugger==4.1.2 + # via -r requirements.in +flake8-deprecated==1.3 + # via -r requirements.in +flake8-isort==4.2.0 + # via -r requirements.in +flake8-pep3101==2.0.0 + # via -r requirements.in +flake8-print==5.0.0 + # via -r requirements.in +flake8-quotes==3.3.1 + # via -r requirements.in +flake8-todo==0.7 + # via -r requirements.in +gitdb==4.0.9 + # via gitpython +gitpython==3.1.28 + # via bandit +idna==3.4 + # via requests +importlib-metadata==4.2.0 ; python_version < "3.8" + # via + # -r requirements.in + # click + # flake8 + # flake8-comprehensions + # pluggy + # pytest + # stevedore +iniconfig==1.1.1 + # via pytest +isort==5.10.1 + # via + # -r requirements.in + # flake8-isort +mccabe==0.7.0 + # via flake8 +mypy==0.982 + # via -r requirements.in +mypy-extensions==0.4.3 + # via + # black + # mypy +packaging==21.3 + # via + # dparse + # pytest + # safety +pathspec==0.10.1 + # via black +pbr==5.10.0 + # via stevedore +platformdirs==2.5.2 + # via black +pluggy==1.0.0 + # via pytest +py==1.11.0 + # via pytest +pycodestyle==2.9.1 + # via + # flake8 + # flake8-debugger + # flake8-print + # flake8-todo +pyflakes==2.5.0 + # via flake8 +pyparsing==3.0.9 + # via packaging +pytest==7.1.3 + # via + # -r requirements.in + # pytest-cov +pytest-cov==4.0.0 + # via -r requirements.in +pyupgrade==3.0.0 + # via -r requirements.in +pyyaml==6.0 + # via bandit +requests==2.28.1 + # via + # coveralls + # safety +ruamel-yaml==0.17.21 + # via safety +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml +safety==2.3.1 + # via -r requirements.in +smmap==5.0.0 + # via gitdb +stevedore==3.5.0 + # via bandit +tokenize-rt==5.0.0 + # via pyupgrade +toml==0.10.2 + # via dparse +tomli==2.0.1 + # via + # black + # coverage + # mypy + # pytest +typed-ast==1.5.4 ; python_version < "3.8" + # via + # -r requirements.in + # black + # mypy +typing-extensions==4.4.0 + # via + # black + # gitpython + # importlib-metadata + # mypy +urllib3==1.26.12 + # via requests +zipp==3.8.1 ; python_version < "3.8" + # via + # -r requirements.in + # importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 0000000..7ecf98e --- /dev/null +++ b/run_tests.py @@ -0,0 +1,251 @@ +"""unit tests for flake8-isort + +the test should pass with both isort 4 and isort 5 +""" + +import collections +import os +import textwrap +import pytest + +from flake8_isort import Flake8Isort + + +def write_python_file(tmpdir, content): + source = textwrap.dedent(content) + file_path = os.path.join(str(tmpdir), 'test.py') + with open(file_path, 'w') as python_file: + python_file.write(source) + return (file_path, source) + + +def write_isort_cfg(tmpdir, content): + write_config_file(tmpdir, '.isort.cfg', 'settings', content) + + +def write_setup_cfg(tmpdir, content): + write_config_file(tmpdir, 'setup.cfg', 'isort', content) + + +def write_tox_ini(tmpdir, content): + write_config_file(tmpdir, 'tox.ini', 'isort', content) + + +def write_pyproject_toml(tmpdir, content): + write_config_file(tmpdir, 'pyproject.toml', 'tool.isort', content) + + +def write_config_file(tmpdir, filename, header, content): + source = f'[{header}]\n{textwrap.dedent(content)}' + file_path = os.path.join(str(tmpdir), filename) + with open(file_path, 'w') as config_file: + config_file.write(source) + + +def check_isort(return_values, references): + """Sort the return by (line, errortype) and compare it to the reference""" + assert len(return_values) == len(references) + for return_value, reference in zip( + sorted(return_values, key=lambda x: (x[0], x[2])), references + ): + assert return_value[:2] == reference[:2] + assert return_value[2].startswith(reference[2]) + + +testcases = [ + { + 'name': 'sorted_correctly_default', + 'code': """ + import os + from sys import path + """, + 'reference': [], + }, + { + 'name': 'sorted_correctly_alpha', + 'config': """ + force_single_line=True + force_alphabetical_sort=True + """, + 'code': """ + from sys import path + + import os + """, + 'reference': [], + }, + { + 'name': 'eof_blank_lines', + 'code': """ + import os + from sys import path + + + + """, + 'reference': [], + }, + { + 'name': 'imports_requires_blank_line', + 'code': """ + from __future__ import division + import threading + from sys import pid + """, + 'reference': [(3, 0, 'I003 ')], + }, + { + 'name': 'isortcfg_skip_file', + 'config': 'skip=test.py', + 'code': 'skipped_file', + 'reference': [], + }, + {'name': 'file_skipped_with_comment', 'code': '# isort:skip_file', 'reference': []}, + { + 'name': 'imports_unexpected_blank_line', + 'code': """ + from __future__ import division + + import threading + + from sys import pid + """, + 'reference': [(5, 0, 'I004 ')], + }, + { + 'name': 'sorted_incorrectly_multiple', + 'code': """ + from __future__ import division + import os + from sys import pid + import threading + + import isort + + + + def func(): ... + """, + 'reference': [(3, 0, 'I003 '), (5, 0, 'I001 '), (10, 0, 'I004 ')], + }, + { + 'name': 'sorted_incorrectly', + 'config': 'force_single_line=True', + 'code': """ + from sys import pid + import threading + """, + 'reference': [(3, 0, 'I001 ')], + }, + {'name': 'empty_file', 'code': '\n\n', 'reference': []}, + { + 'name': 'wrapped_imports', + 'config': 'wrap_length=65', + 'code': """ + from deluge.common import (fdate, fpcnt, fpeer, fsize, fspeed, + ftime, get_path_size, is_infohash, + is_ip, is_magnet, is_url) + """, + 'reference': [], + }, + { + 'name': 'force_single_line_imports', + 'config': """ + force_alphabetical_sort=True + force_single_line=True + """, + 'code': """ + from plone.app.testing import applyProfile + from plone.app.testing import FunctionalTesting + """, + 'reference': [], + }, + { + 'name': 'missing_add_imports', + 'config': 'add_imports=from __future__ import unicode_literals', + 'code': 'import os\n', + 'reference': [(1, 0, 'I003'), (1, 0, 'I005')], + }, +] + + +@pytest.mark.parametrize('mode', ['file', 'code_string']) +@pytest.mark.parametrize('testcase', testcases, ids=[t['name'] for t in testcases]) +def test_flake8_isort(tmpdir, testcase, mode): + """Test the code examples in files and directly from string""" + with tmpdir.as_cwd(): + if 'config' in testcase: + write_isort_cfg(tmpdir, testcase['config']) + if mode == 'file': + (file_path, lines) = write_python_file(tmpdir, testcase['code']) + checker = Flake8Isort(None, file_path, lines) + elif mode == 'code_string': + source = textwrap.dedent(testcase['code']) + checker = Flake8Isort(None, None, source) + return_values = list(checker.run()) + check_isort(return_values, testcase['reference']) + + +def test_isortcfg_found(tmpdir): + source = """ + from sys import pid + import threading + """ + (file_path, lines) = write_python_file(tmpdir, source) + write_isort_cfg(tmpdir, 'force_single_line=True') + checker = Flake8Isort(None, file_path, lines) + checker.config_file = True + ret = list(checker.run()) + check_isort(ret, [(3, 0, 'I001 ')]) + + +def test_isortcfg_not_found(tmpdir): + (file_path, lines) = write_python_file(tmpdir, 'from sys import pid, path') + checker = Flake8Isort(None, file_path, lines) + checker.search_current = False + checker.config_file = True + ret = list(checker.run()) + check_isort(ret, [(1, 0, 'I001 ')]) + + +def test_isort_formatted_output(tmpdir): + source = """ + from __future__ import division + import os + from sys import pid + """ + options = collections.namedtuple( + 'Options', ['no_isort_config', 'isort_show_traceback', 'stdin_display_name'] + ) + + (file_path, lines) = write_python_file(tmpdir, source) + + diff = ' from __future__ import division\n+\n import os' + + checker = Flake8Isort(None, file_path, lines) + checker.parse_options(options(None, True, 'stdin')) + ret = list(checker.run()) + assert len(ret) == 1 + assert ret[0][0] == 3 + assert ret[0][1] == 0 + assert diff in ret[0][2] + + +@pytest.mark.parametrize( + 'method_to_write_config', + [write_isort_cfg, write_setup_cfg, write_tox_ini, write_pyproject_toml], +) +def test_if_config_file_is_used(tmpdir, method_to_write_config): + source = """ + import os + from sys import path + """ + (file_path, lines) = write_python_file( + tmpdir, + source, + ) + method_to_write_config(tmpdir, 'lines_between_types=1') + + checker = Flake8Isort(None, file_path, lines) + ret = list(checker.run()) + check_isort(ret, [(3, 0, 'I003 ')]) diff --git a/setup.cfg b/setup.cfg index 50f9c8a..2caaac2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,12 +1,9 @@ -[bdist_wheel] -universal = 0 - [zest.releaser] create-wheel = yes -python-file-with-version = flake8_isort.py + +[check-manifest] +ignore = + .installed.cfg [isort] -force_alphabetical_sort = True -force_single_line = True -lines_after_imports = 2 -line_length = 200 +profile = black diff --git a/setup.py b/setup.py index b78d237..7b66c14 100644 --- a/setup.py +++ b/setup.py @@ -1,31 +1,26 @@ -# -*- coding: utf-8 -*- from setuptools import setup -import re +short_description = 'flake8 plugin that integrates isort .' -def get_version(file="flake8_isort.py"): - with open(file) as f: - for line in f: - m = re.match(r"^__version__ = '(?P.*?)'$", line) - if m: - return m.group('version') +def read_file(filename): + with open(filename) as file_obj: + file_contents = file_obj.read() + return file_contents -short_description = 'flake8 plugin that integrates isort .' - -long_description = '{0}\n{1}'.format( - open('README.rst').read(), - open('CHANGES.rst').read(), -) +long_description = f""" +{read_file('README.rst')} +{read_file('CHANGES.rst')} +""" setup( name='flake8-isort', - version=get_version(), + version='4.2.1.dev0', description=short_description, long_description=long_description, - # Get more from http://pypi.python.org/pypi?%3Aaction=list_classifiers + # Get more from https://pypi.org/classifiers/ classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -36,7 +31,6 @@ def get_version(file="flake8_isort.py"): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', @@ -46,22 +40,30 @@ def get_version(file="flake8_isort.py"): 'Topic :: Software Development', 'Topic :: Software Development :: Quality Assurance', ], - keywords='pep8 flake8 isort imports', + python_requires='>=3.7', + keywords='pep8 flake8 python isort imports', author='Gil Forcada', author_email='gil.gnome@gmail.com', url='https://github.com/gforcada/flake8-isort', license='GPL version 2', - py_modules=['flake8_isort', ], + py_modules=[ + 'flake8_isort', + ], include_package_data=True, + test_suite='run_tests', zip_safe=False, install_requires=[ - 'flake8 >= 3.2.1, <6', + 'flake8', 'isort >= 4.3.5, <6', ], extras_require={ - 'test': ['pytest-cov'], + 'test': [ + 'pytest', + ], }, entry_points={ - 'flake8.extension': ['I00 = flake8_isort:Flake8Isort', ], + 'flake8.extension': [ + 'I00 = flake8_isort:Flake8Isort', + ], }, ) diff --git a/test_flake8_isort.py b/test_flake8_isort.py deleted file mode 100644 index 4c66411..0000000 --- a/test_flake8_isort.py +++ /dev/null @@ -1,216 +0,0 @@ -"""unit tests for flake8-isort - -the test should pass with both isort 4 and isort 5 -""" - -import collections -import os -import pytest - -from flake8_isort import Flake8Isort - - -def write_python_file(tmpdir, content): - file_path = os.path.join(str(tmpdir), 'test.py') - with open(file_path, 'w') as python_file: - python_file.write(content) - return (file_path, content) - - -def write_isort_cfg(tmpdir, content): - content = '[settings]\n' + content - write_config_file(tmpdir, '.isort.cfg', content) - - -def write_setup_cfg(tmpdir, content): - content = '[isort]\n' + content - write_config_file(tmpdir, 'setup.cfg', content) - - -def write_tox_ini(tmpdir, content): - content = '[isort]\n' + content - write_config_file(tmpdir, 'tox.ini', content) - - -def write_pyproject_toml(tmpdir, content): - content = '[tool.isort]\n' + content - write_config_file(tmpdir, 'pyproject.toml', content) - - -def write_config_file(tmpdir, filename, content): - file_path = os.path.join(str(tmpdir), filename) - with open(file_path, 'w') as config_file: - config_file.write(content) - - -def check_isort_ret(ret, ref): - """Sort the return by (line, errortype) and compare it to the reference""" - assert len(ret) == len(ref) - for ret_i, ref_i in zip(sorted(ret, key=lambda x: (x[0], x[2])), ref): - assert ret_i[:2] == ref_i[:2] - assert ret_i[2].startswith(ref_i[2]) - - -testcases = [ - {'name': 'sorted_correctly_default', - 'code': ('import os\n' - 'from sys import path\n'), - 'ref': []}, - {'name': 'sorted_correctly_alpha', - 'config': 'force_single_line=True\n' - 'force_alphabetical_sort=True\n', - 'code': 'from sys import path\n' - '\n' - 'import os\n', - 'ref': []}, - {'name': 'eof_blank_lines', - 'code': 'import os\n' - 'from sys import path\n' - '\n' - '\n' - ' \n', - 'ref': []}, - {'name': 'imports_requires_blank_line', - 'code': 'from __future__ import division\n' - 'import threading\n' - 'from sys import pid\n', - 'ref': [(2, 0, 'I003 ')]}, - {'name': 'isortcfg_skip_file', - 'config': 'skip=test.py', - 'code': 'skipped_file', - 'ref': []}, - {'name': 'file_skipped_with_comment', - 'code': '# isort:skip_file', - 'ref': []}, - {'name': 'imports_unexpected_blank_line', - 'code': 'from __future__ import division\n' - '\n' - 'import threading\n' - '\n' - 'from sys import pid\n', - 'ref': [(4, 0, 'I004 ')]}, - {'name': 'sorted_incorrectly_multiple', - 'code': 'from __future__ import division\n' - 'import os\n' - 'from sys import pid\n' - 'import threading\n' - '\n' - 'import isort\n' - '\n\n\n' - 'def func()\n', - 'ref': [(2, 0, 'I003 '), - (4, 0, 'I001 '), - (9, 0, 'I004 ')]}, - {'name': 'sorted_incorrectly', - 'config': 'force_single_line=True', - 'code': 'from sys import pid\n' - 'import threading', - 'ref': [(2, 0, 'I001 ')]}, - {'name': 'empty_file', - 'code': '\n\n', - 'ref': []}, - {'name': 'wrapped_imports', - 'config': 'wrap_length=65', - 'code': 'from deluge.common import (fdate, fpcnt, fpeer, fsize, fspeed,\n' - ' ftime, get_path_size, is_infohash,\n' - ' is_ip, is_magnet, is_url)\n', - 'ref': []}, - {'name': 'force_single_line_imports', - 'config': 'force_alphabetical_sort=True\n' - 'force_single_line=True', - 'code': 'from plone.app.testing import applyProfile\n' - 'from plone.app.testing import FunctionalTesting\n', - 'ref': []}, - {'name': 'missing_add_imports', - 'config': 'add_imports=from __future__ import unicode_literals', - 'code': 'import os\n', - 'ref': [(1, 0, 'I003'), - (1, 0, 'I005')]}, -] - - -@pytest.mark.parametrize('mode', ["file", "code_string"]) -@pytest.mark.parametrize('testcase', testcases, - ids=[t['name'] for t in testcases]) -def test_flake8_isort(tmpdir, testcase, mode): - """Test the code examples in files and directly from string""" - with tmpdir.as_cwd(): - if 'config' in testcase: - write_isort_cfg(tmpdir, testcase['config']) - if mode == "file": - (file_path, lines) = write_python_file(tmpdir, testcase['code']) - checker = Flake8Isort(None, file_path, lines) - elif mode == "code_string": - checker = Flake8Isort(None, None, testcase['code']) - else: - raise RuntimeError("invalid mode") - ret = list(checker.run()) - check_isort_ret(ret, testcase['ref']) - - -def test_isortcfg_found(tmpdir): - (file_path, lines) = write_python_file( - tmpdir, - 'from sys import pid\n' - 'import threading', - ) - write_isort_cfg(tmpdir, 'force_single_line=True') - checker = Flake8Isort(None, file_path, lines) - checker.config_file = True - ret = list(checker.run()) - check_isort_ret(ret, [(2, 0, 'I001 ')]) - - -def test_isortcfg_not_found(tmpdir): - (file_path, lines) = write_python_file( - tmpdir, - 'from sys import pid, path' - ) - checker = Flake8Isort(None, file_path, lines) - checker.search_current = False - checker.config_file = True - ret = list(checker.run()) - check_isort_ret(ret, [(1, 0, 'I001 ')]) - - -def test_isort_formatted_output(tmpdir): - options = collections.namedtuple( - 'Options', [ - 'no_isort_config', - 'isort_show_traceback', - 'stdin_display_name' - ] - ) - - (file_path, lines) = write_python_file( - tmpdir, - 'from __future__ import division\n' - 'import os\n' - 'from sys import pid\n', - ) - - diff = ' from __future__ import division\n+\n import os' - - checker = Flake8Isort(None, file_path, lines) - checker.parse_options(options(None, True, 'stdin')) - ret = list(checker.run()) - assert len(ret) == 1 - assert ret[0][0] == 2 - assert ret[0][1] == 0 - assert diff in ret[0][2] - - -@pytest.mark.parametrize( - 'method_to_write_config', - [write_isort_cfg, write_setup_cfg, write_tox_ini, write_pyproject_toml]) -def test_if_config_file_is_used(tmpdir, method_to_write_config): - (file_path, lines) = write_python_file( - tmpdir, - 'import os\n' - 'from sys import path\n', - ) - method_to_write_config(tmpdir, 'lines_between_types=1') - - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - check_isort_ret(ret, [(2, 0, 'I003 ')])