diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml deleted file mode 100644 index 2375c94..0000000 --- a/.github/workflows/lint_python.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: lint_python -on: [pull_request, push] -jobs: - lint_python: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - run: pip install --upgrade pip wheel - - run: pip install bandit black codespell flake8 flake8-bugbear isort - mypy pytest pyupgrade safety # flake8-comprehensions - - run: bandit --recursive --skip B101 . # B101 is assert statements - - run: black --check --skip-string-normalization . - - run: codespell - - run: flake8 . --count --max-complexity=18 --max-line-length=88 --show-source --statistics - - run: isort --check-only --profile black . - - run: pip install -r requirements.txt - - run: mkdir --parents --verbose .mypy_cache - - run: mypy --ignore-missing-imports --install-types --non-interactive . || true - - run: pytest run_tests.py --cov flake8_builtins --cov-report term-missing - - run: coveralls || true - - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true - - run: safety check || true diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..097074d --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,61 @@ +name: Testing +on: [pull_request, push] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +jobs: + test: + name: Testing on + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9, 3.8, 3.7, pypy-3.9] + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - 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 + # formatters + - name: Run pyupgrade + if: matrix.python-version == '3.9' + run: pyupgrade --py37-plus *.py + - name: Run isort + if: matrix.python-version == '3.9' + run: isort --check-only *.py + - name: Run black + if: matrix.python-version == '3.9' + run: black --check --skip-string-normalization *.py + # linters + - name: Lint with bandit + if: matrix.python-version == '3.9' + run: bandit --skip B101 *.py # B101 is assert statements + - name: Lint with codespell + if: matrix.python-version == '3.9' + run: codespell *.rst *.py + - name: Lint with flake8 + if: matrix.python-version == '3.9' + run: flake8 *.py --count --max-complexity=18 --max-line-length=88 --show-source --statistics + - name: Lint with mypy + if: matrix.python-version == '3.9' + run: | + mkdir --parents --verbose .mypy_cache + mypy --ignore-missing-imports --install-types --non-interactive *.py || true + - name: Lint with safety + if: matrix.python-version == '3.9' + 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 510fd1f..f85267e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ .cache .coverage .installed.cfg -.tox +.hypothesis bin develop-eggs include diff --git a/CHANGES.rst b/CHANGES.rst index a68cd0b..d08ca5e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,10 @@ Changelog - Make black and isort mandatory. [cclauss] +- Drop python 2.7 and 3.6. [gforcada] + +- Overhaul GitHub actions to test on actual supported python versions. [gforcada] + 1.5.3 (2020-05-14) ------------------ diff --git a/README.rst b/README.rst index d7e32f7..59d5c3f 100644 --- a/README.rst +++ b/README.rst @@ -75,7 +75,7 @@ Install with pip:: Requirements ------------ -- Python 2.7, 3.6, 3.7, 3.8, 3.9 +- Python 3.7, 3.8, 3.9 - flake8 License diff --git a/flake8_builtins.py b/flake8_builtins.py index b76709e..90b5cbc 100644 --- a/flake8_builtins.py +++ b/flake8_builtins.py @@ -1,14 +1,9 @@ -# -*- coding: utf-8 -*- import ast +import builtins import inspect import sys - -try: - from flake8.engine import pep8 as stdin_utils -except ImportError: - from flake8 import utils as stdin_utils - +from flake8 import utils as stdin_utils WHITE_LIST = { '__name__', @@ -18,21 +13,7 @@ } -if sys.version_info >= (3, 0): - import builtins - - BUILTINS = [a[0] for a in inspect.getmembers(builtins) if a[0] not in WHITE_LIST] - PY3 = True -else: - import __builtin__ - - BUILTINS = [a[0] for a in inspect.getmembers(__builtin__) if a[0] not in WHITE_LIST] - PY3 = False - -if sys.version_info >= (3, 6): - AnnAssign = ast.AnnAssign -else: # There was no `AnnAssign` before python3.6 - AnnAssign = type('AnnAssign', (ast.AST,), {}) +BUILTINS = [a[0] for a in inspect.getmembers(builtins) if a[0] not in WHITE_LIST] if sys.version_info >= (3, 8): NamedExpr = ast.NamedExpr @@ -40,7 +21,7 @@ NamedExpr = type('NamedExpr', (ast.AST,), {}) -class BuiltinsChecker(object): +class BuiltinsChecker: name = 'flake8_builtins' version = '1.5.2' assign_msg = 'A001 variable "{0}" is shadowing a python builtin' @@ -86,7 +67,7 @@ def run(self): value = None for statement in ast.walk(tree): - if isinstance(statement, (ast.Assign, AnnAssign, NamedExpr)): + if isinstance(statement, (ast.Assign, ast.AnnAssign, NamedExpr)): value = self.check_assignment(statement) elif isinstance(statement, function_nodes): @@ -111,8 +92,7 @@ def run(self): value = self.check_class(statement) if value: - for line, offset, msg, rtype in value: - yield line, offset, msg, rtype + yield from value def check_assignment(self, statement): msg = self.assign_msg @@ -130,7 +110,7 @@ def check_assignment(self, statement): stack.extend(list(item.elts)) elif isinstance(item, ast.Name) and item.id in BUILTINS: yield self.error(item, message=msg, variable=item.id) - elif PY3 and isinstance(item, ast.Starred): + elif isinstance(item, ast.Starred): if hasattr(item.value, 'id') and item.value.id in BUILTINS: yield self.error( statement, @@ -148,23 +128,18 @@ def check_function_definition(self, statement): yield self.error(statement, message=msg, variable=statement.name) - if PY3: - all_arguments = [] - all_arguments.extend(statement.args.args) - all_arguments.extend(getattr(statement.args, 'kwonlyargs', [])) - all_arguments.extend(getattr(statement.args, 'posonlyargs', [])) + all_arguments = [] + all_arguments.extend(statement.args.args) + all_arguments.extend(getattr(statement.args, 'kwonlyargs', [])) + all_arguments.extend(getattr(statement.args, 'posonlyargs', [])) - for arg in all_arguments: - if isinstance(arg, ast.arg) and arg.arg in BUILTINS: - yield self.error( - arg, - message=self.argument_msg, - variable=arg.arg, - ) - else: - for arg in statement.args.args: - if isinstance(arg, ast.Name) and arg.id in BUILTINS: - yield self.error(arg, message=self.argument_msg) + for arg in all_arguments: + if isinstance(arg, ast.arg) and arg.arg in BUILTINS: + yield self.error( + arg, + message=self.argument_msg, + variable=arg.arg, + ) def check_for_loop(self, statement): stack = [statement.target] @@ -174,7 +149,7 @@ def check_for_loop(self, statement): stack.extend(list(item.elts)) elif isinstance(item, ast.Name) and item.id in BUILTINS: yield self.error(statement, variable=item.id) - elif PY3 and isinstance(item, ast.Starred): + elif isinstance(item, ast.Starred): if hasattr(item.value, 'id') and item.value.id in BUILTINS: yield self.error( statement, @@ -184,44 +159,31 @@ def check_for_loop(self, statement): stack.extend(list(item.value.elts)) def check_with(self, statement): - if not PY3: - var = statement.optional_vars + for item in statement.items: + var = item.optional_vars if isinstance(var, (ast.Tuple, ast.List)): for element in var.elts: if isinstance(element, ast.Name) and element.id in BUILTINS: yield self.error(statement, variable=element.id) + elif ( + isinstance(element, ast.Starred) + and element.value.id in BUILTINS + ): + yield self.error( + element, + variable=element.value.id, + ) elif isinstance(var, ast.Name) and var.id in BUILTINS: yield self.error(statement, variable=var.id) - else: - for item in statement.items: - var = item.optional_vars - if isinstance(var, (ast.Tuple, ast.List)): - for element in var.elts: - if isinstance(element, ast.Name) and element.id in BUILTINS: - yield self.error(statement, variable=element.id) - elif ( - isinstance(element, ast.Starred) - and element.value.id in BUILTINS - ): - yield self.error( - element, - variable=element.value.id, - ) - - elif isinstance(var, ast.Name) and var.id in BUILTINS: - yield self.error(statement, variable=var.id) def check_exception(self, statement): exception_name = statement.name - value = '' - if isinstance(exception_name, ast.Name): - value = exception_name.id - elif isinstance(exception_name, str): # Python +3.x - value = exception_name - - if value in BUILTINS: - yield self.error(statement, variable=value) + if exception_name is None: + return + + if exception_name in BUILTINS: + yield self.error(statement, variable=exception_name) def check_comprehension(self, statement): for generator in statement.generators: diff --git a/requirements.in b/requirements.in index 1a7cd7a..dc2b48b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,22 +1,23 @@ +bandit +black +codespell coveralls flake8-blind-except -flake8-coding -flake8-commas +flake8-bugbear +flake8-comprehensions flake8-debugger flake8-deprecated flake8-isort flake8-pep3101 -flake8-polyfill flake8-print flake8-quotes -flake8-string-format flake8-todo -futures; python_version < '3.0' -hypothesis; python_version >= '3.6' -hypothesmith; python_version >= '3.6' -mock ; python_version < '3.0' -pytest<5; python_version < '3.0' -pytest>5; python_version >= '3.0' +importlib-metadata; python_version < '3.8' +isort +mypy +pytest pytest-cov -more-itertools==5.0.0 -zipp ; python_version >= '3.0' +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 index 54555ec..fb4bb03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,56 +1,172 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.7 # To update, run: # -# pip-compile +# pip-compile requirements.in # -attrs==19.3.0 # via hypothesis, pytest -certifi==2019.11.28 # via requests -chardet==3.0.4 # via requests -coverage==5.0.3 # via coveralls, pytest-cov -coveralls==1.11.1 # via -r requirements.in -docopt==0.6.2 # via coveralls -entrypoints==0.3 # via flake8 -flake8-blind-except==0.1.1 # via -r requirements.in -flake8-coding==1.3.2 # via -r requirements.in -flake8-commas==2.0.0 # via -r requirements.in -flake8-debugger==3.2.1 # via -r requirements.in -flake8-deprecated==1.3 # via -r requirements.in -flake8-isort==2.8.0 # via -r requirements.in -flake8-pep3101==1.3.0 # via -r requirements.in -flake8-polyfill==1.0.2 # via -r requirements.in -flake8-print==3.1.4 # via -r requirements.in -flake8-quotes==2.1.1 # via -r requirements.in -flake8-string-format==0.3.0 # via -r requirements.in -flake8-todo==0.7 # via -r requirements.in -flake8==3.7.9 # via flake8-coding, flake8-commas, flake8-debugger, flake8-deprecated, flake8-isort, flake8-pep3101, flake8-polyfill, flake8-print, flake8-quotes, flake8-string-format -hypothesis==5.6.0 ; python_version >= "3.6" # via -r requirements.in, hypothesmith -hypothesmith==0.0.5 ; python_version >= "3.6" # via -r requirements.in -idna==2.9 # via requests -importlib-metadata==1.5.0 # via pluggy, pytest -isort[pyproject]==4.3.21 # via flake8-isort -lark-parser==0.8.2 # via hypothesmith -mccabe==0.6.1 # via flake8 -more-itertools==5.0.0 # via -r requirements.in, pytest -packaging==20.3 # via pytest -pluggy==0.13.1 # via pytest -py==1.8.1 # via pytest -pycodestyle==2.5.0 # via flake8, flake8-debugger, flake8-print, flake8-todo -pyflakes==2.1.1 # via flake8 -pyparsing==2.4.6 # via packaging -pytest-cov==2.8.1 # via -r requirements.in -pytest==5.4.1 ; python_version >= "3.0" # via -r requirements.in, pytest-cov -requests==2.23.0 # via coveralls -six==1.14.0 # via flake8-print, more-itertools, packaging -sortedcontainers==2.1.0 # via hypothesis -testfixtures==6.14.0 # via flake8-isort -toml==0.10.0 # via isort -urllib3==1.25.8 # via requests -wcwidth==0.1.8 # via pytest -zipp==3.1.0 ; python_version >= "3.0" # via -r requirements.in, importlib-metadata +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==1.3.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 - -# added manually -mock==3.0.5 ; python_version < '3.0' diff --git a/run_tests.py b/run_tests.py index 3878fb9..3dccc5a 100644 --- a/run_tests.py +++ b/run_tests.py @@ -1,555 +1,383 @@ -# -*- coding: utf-8 -*- -from flake8_builtins import BuiltinsChecker - import ast -import pytest import sys +import textwrap import unittest +from unittest import mock +import pytest -try: - from unittest import mock -except ImportError: - import mock +from flake8_builtins import BuiltinsChecker -if sys.version_info >= (3, 6): - from hypothesis import given - from hypothesis import reject +class TestBuiltins(unittest.TestCase): + def check_code(self, source, expected_codes=None): + """Check if the given source code generates the given flake8 errors - import hypothesmith + If `expected_codes` is a string is converted to a list, + if it is not given, then it is expected to **not** generate any error. + """ + if isinstance(expected_codes, str): + expected_codes = [expected_codes] + elif expected_codes is None: + expected_codes = [] + tree = ast.parse(textwrap.dedent(source)) + checker = BuiltinsChecker(tree, '/home/script.py') + return_statements = list(checker.run()) + self.assertEqual( + len(return_statements), len(expected_codes), f'Got {return_statements}' + ) -class TestBuiltins(unittest.TestCase): - def assert_codes(self, ret, codes): - self.assertEqual(len(ret), len(codes)) - for item, code in zip(ret, codes): - self.assertTrue(item[2].startswith(code + ' ')) + for item, code in zip(return_statements, expected_codes): + self.assertTrue( + item[2].startswith(f'{code} '), + f'Actually got {item[2]} rather than {code}', + ) def test_builtin_top_level(self): - tree = ast.parse('max = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = 'max = 4' + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 6), - reason='AnnAssign appeared in 3.6', - ) def test_ann_assign(self): - tree = ast.parse('list: int = 1') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = 'list: int = 1' + self.check_code(source, 'A001') @pytest.mark.skipif( sys.version_info < (3, 8), reason='NamedExpr appeared in 3.8', ) def test_walrus_operator(self): - tree = ast.parse('(dict := 1)') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = '(dict := 1)' + self.check_code(source, 'A001') def test_nested(self): - tree = ast.parse( - 'def bla():\n' ' filter = 4', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = """ + def bla(): + filter = 4 + """ + self.check_code(source, 'A001') def test_more_nested(self): - tree = ast.parse( - 'class Bla(object):\n' ' def method(self):\n' ' int = 4', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = """ + class Bla(object): + def method(self): + int = 4 + """ + self.check_code(source, 'A001') def test_line_number(self): - tree = ast.parse( - 'a = 2\n' 'open = 4', - ) + source = """ + a = 2 + open = 4 + """ + tree = ast.parse(textwrap.dedent(source)) checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(ret[0][0], 2) + ret = list(checker.run()) + self.assertEqual(ret[0][0], 3) def test_offset(self): - tree = ast.parse( - 'def bla():\n' ' zip = 4', - ) + source = """ + def bla(): + zip = 4 + """ + tree = ast.parse(textwrap.dedent(source)) checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] + ret = list(checker.run()) self.assertEqual(ret[0][1], 4) def test_assign_message(self): - tree = ast.parse('def bla():\n object = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = """ + def bla(): + object = 4 + """ + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 0), - reason='This syntax is only valid in Python 3.x', - ) def test_assignment_starred(self): - tree = ast.parse('bla, *int = range(4)') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = 'bla, *int = range(4)' + self.check_code(source, 'A001') def test_assignment_list(self): - tree = ast.parse('[bla, int] = range(4)') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001']) + source = '[bla, int] = range(4)' + self.check_code(source, 'A001') def test_class_attribute_message(self): - tree = ast.parse('class TestClass():\n object = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A003']) + source = """ + class TestClass(): + object = 4 + """ + self.check_code(source, 'A003') def test_argument_message(self): - tree = ast.parse('def bla(list):\n a = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A002']) + source = """ + def bla(list): + a = 4""" + self.check_code(source, 'A002') def test_keyword_argument_message(self): - tree = ast.parse('def bla(dict=3):\n b = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A002']) + source = """ + def bla(dict=3): + b = 4""" + self.check_code(source, 'A002') - @pytest.mark.skipif( - sys.version_info < (3, 6), - reason='This syntax is only valid in Python 3.6+', - ) def test_kwonly_argument_message(self): - tree = ast.parse('def bla(*, list):\n a = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A002']) + source = """ + def bla(*, list): + a = 4 + """ + self.check_code(source, 'A002') @pytest.mark.skipif( sys.version_info < (3, 8), reason='This syntax is only valid in Python 3.8+', ) def test_posonly_argument_message(self): - tree = ast.parse('def bla(list, /):\n a = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A002']) + source = """ + def bla(list, /): + a = 4 + """ + self.check_code(source, 'A002') def test_no_error(self): - tree = ast.parse('def bla(first):\n b = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """def bla(first):\n b = 4""" + self.check_code(source) def test_method_without_arguments(self): - tree = ast.parse('def bla():\n b = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + def bla(): + b = 4 + """ + self.check_code(source) def test_method_only_normal_keyword_arguments(self): - tree = ast.parse('def bla(k=4):\n b = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + def bla(k=4): + b = 4 + """ + self.check_code(source) def test_report_all_arguments(self): - tree = ast.parse('def bla(zip, object=4):\n b = 4') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A002', 'A002']) + source = """ + def bla(zip, object=4): + b = 4 + """ + self.check_code(source, ['A002', 'A002']) def test_report_all_variables_within_a_line(self): - tree = ast.parse('def bla():\n object = 4; zip = 3') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assert_codes(ret, ['A001', 'A001']) + source = """ + def bla(): + object = 4; zip = 3 + """ + self.check_code(source, ['A001', 'A001']) def test_ignore_whitelisted_names(self): - tree = ast.parse( - 'class MyClass(object):\n' ' __name__ = 4\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + class MyClass(object): + __name__ = 4 + """ + self.check_code(source) def test_for_loop_variable(self): - tree = ast.parse( - 'for format in (1, 2, 3):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + for format in (1, 2, 3): + continue + """ + self.check_code(source, 'A001') def test_for_loop_multiple_variables(self): - tree = ast.parse( - 'for (index, format) in enumerate([1,2,3,]):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + for (index, format) in enumerate([1,2,3,]): + continue + """ + self.check_code(source, 'A001') def test_for_loop_list(self): - tree = ast.parse( - 'for [index, format] in enumerate([1,2,3,]):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + for [index, format] in enumerate([1,2,3,]): + continue + """ + self.check_code(source, 'A001') def test_for_loop_nested_tuple(self): - tree = ast.parse( - 'for index, (format, list) in enumerate([(1, "a"), (2, "b")]):\n' - ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 2) + source = """ + for index, (format, list) in enumerate([(1, "a"), (2, "b")]): + continue + """ + self.check_code(source, ['A001', 'A001']) - @pytest.mark.skipif( - sys.version_info < (3, 0), - reason='This syntax is only valid in Python 3.x', - ) def test_for_loop_starred(self): - tree = ast.parse( - 'for index, *int in enumerate([(1, "a"), (2, "b")]):\n' - ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + for index, *int in enumerate([(1, "a"), (2, "b")]): + continue + """ + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 0), - reason='This syntax is only valid in Python 3.x', - ) def test_for_loop_starred_no_error(self): - tree = ast.parse( - 'for index, *other in enumerate([(1, "a"), (2, "b")]):\n' - ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + for index, *other in enumerate([(1, "a"), (2, "b")]): + continue + """ + self.check_code(source) def test_with_statement(self): - tree = ast.parse( - 'with open("bla.txt") as dir:\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + with open("bla.txt") as dir: + continue + """ + self.check_code(source, 'A001') def test_with_statement_no_error(self): - tree = ast.parse( - 'with open("bla.txt"):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = 'with open("bla.txt"): ...' + self.check_code(source) def test_with_statement_multiple(self): - tree = ast.parse( - 'with open("bla.txt") as dir, open("bla.txt") as int:\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 2) + source = 'with open("bla.txt") as dir, open("bla.txt") as int: ...' + self.check_code(source, ['A001', 'A001']) def test_with_statement_unpack(self): - tree = ast.parse( - 'with open("bla.txt") as (dir, bla):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'with open("bla.txt") as (dir, bla): ...' + self.check_code(source, 'A001') def test_with_statement_unpack_on_list(self): - tree = ast.parse( - 'with open("bla.txt") as [dir, bla]:\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'with open("bla.txt") as [dir, bla]: ...' + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 0), - reason='This syntax is only valid in Python 3.x', - ) def test_with_statement_unpack_star(self): - tree = ast.parse( - 'with open("bla.txt") as (bla, *int):\n' ' continue\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'with open("bla.txt") as (bla, *int): ...' + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 0), - reason='This syntax is only valid in Python 3.x', - ) def test_exception_py3(self): - tree = ast.parse( - 'try:\n' ' a = 2\n' 'except Exception as int:\n' ' print("ooops")\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) - - @pytest.mark.skipif( - sys.version_info > (3, 0), - reason='This syntax is only valid in Python 2.x', - ) - def test_exception_py2(self): - tree = ast.parse( - 'try:\n' ' a = 2\n' 'except Exception, int:\n' ' print("ooops")\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + try: + a = 2 + except Exception as int: ... + """ + self.check_code(source, 'A001') def test_exception_no_error(self): - tree = ast.parse( - 'try:\n' ' a = 2\n' 'except Exception:\n' ' print("ooops")\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + try: + a = 2 + except Exception: ... + """ + self.check_code(source) def test_list_comprehension(self): - tree = ast.parse('a = [int for int in range(3,9)]') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'a = [int for int in range(3,9)]' + self.check_code(source, 'A001') def test_set_comprehension(self): - tree = ast.parse('a = {int for int in range(3,9)}') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'a = {int for int in range(3,9)}' + self.check_code(source, 'A001') def test_dict_comprehension(self): - tree = ast.parse('a = {int:"a" for int in range(3,9)}') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'a = {int:"a" for int in range(3,9)}' + self.check_code(source, 'A001') def test_gen_comprehension(self): - tree = ast.parse('a = (int for int in range(3,9))') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'a = (int for int in range(3,9))' + self.check_code(source, 'A001') def test_list_comprehension_multiple(self): - tree = ast.parse( - 'a = [(int, list) for int, list in enumerate(range(3,9))]\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 2) + source = 'a = [(int, list) for int, list in enumerate(range(3,9))]\n' + self.check_code(source, ['A001', 'A001']) def test_list_comprehension_nested(self): - tree = ast.parse( - 'a = [(int, str) for int in some() for str in other()]\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 2) + source = 'a = [(int, str) for int in some() for str in other()]' + self.check_code(source, ['A001', 'A001']) def test_list_comprehension_multiple_as_list(self): - tree = ast.parse( - 'a = [(int, a) for [int, a] in enumerate(range(3,9))]\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'a = [(int, a) for [int, a] in enumerate(range(3,9))]' + self.check_code(source, 'A001') def test_import_as(self): - tree = ast.parse( - 'import zope.component.getSite as int\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'import zope.component.getSite as int' + self.check_code(source, 'A001') def test_import_from_as(self): - tree = ast.parse( - 'from zope.component import getSite as int\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'from zope.component import getSite as int' + self.check_code(source, 'A001') def test_import_as_nothing(self): - tree = ast.parse( - 'import zope.component.getSite as something_else\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = 'import zope.component.getSite as something_else' + self.check_code(source) def test_import_from_as_nothing(self): - tree = ast.parse( - 'from zope.component import getSite as something_else\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = 'from zope.component import getSite as something_else' + self.check_code(source) def test_class(self): - tree = ast.parse( - 'class int(object): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'class int(object): ...' + self.check_code(source, 'A001') def test_class_nothing(self): - tree = ast.parse( - 'class integer(object): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = 'class integer(object): ...' + self.check_code(source) def test_function(self): - tree = ast.parse( - 'def int(): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'def int(): ...' + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.5', - ) def test_async_function(self): - tree = ast.parse( - 'async def int(): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = 'async def int(): ...' + self.check_code(source, 'A001') def test_method(self): - tree = ast.parse( - 'class bla(object):\n' ' def int(): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + class bla(object): + def int(): ... + """ + self.check_code(source, 'A003') def test_method_error_code(self): - tree = ast.parse( - 'class bla(object):\n' ' def int(): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - error_code = ret[0][2][:4] - self.assertEqual(error_code, 'A003') + source = """ + class bla(object): + def int(): ... + """ + self.check_code(source, 'A003') def test_function_nothing(self): - tree = ast.parse( - 'def integer(): pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = 'def integer(): ...' + self.check_code(source) - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.x', - ) def test_async_for(self): - tree = ast.parse( - 'async def bla():\n' ' async for int in range(4):\n' ' pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + async def bla(): + async for int in range(4): ... + """ + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.x', - ) def test_async_for_nothing(self): - tree = ast.parse( - 'async def bla():\n' ' async for x in range(4):\n' ' pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + async def bla(): + async for x in range(4): ... + """ + self.check_code(source) - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.x', - ) def test_async_with(self): - tree = ast.parse( - 'async def bla():\n' - ' async with open("bla.txt") as int:\n' - ' pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 1) + source = """ + async def bla(): + async with open("bla.txt") as int: ... + """ + self.check_code(source, 'A001') - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.x', - ) def test_async_with_nothing(self): - tree = ast.parse( - 'async def bla():\n' - ' async with open("bla.txt") as x:\n' - ' pass\n', - ) - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) + source = """ + async def bla(): + async with open("bla.txt") as x: ... + """ + self.check_code(source) @mock.patch('flake8.utils.stdin_get_value') def test_stdin(self, stdin_get_value): - code = u'max = 4' - stdin_get_value.return_value = code + source = 'max = 4' + stdin_get_value.return_value = source checker = BuiltinsChecker('', 'stdin') - ret = [c for c in checker.run()] + ret = list(checker.run()) self.assertEqual( len(ret), 1, ) - @pytest.mark.skipif( - sys.version_info < (3, 5), - reason='This syntax is only valid in Python 3.x', - ) def test_tuple_unpacking(self): - tree = ast.parse('a, *(b, c) = 1, 2, 3') - checker = BuiltinsChecker(tree, '/home/script.py') - ret = [c for c in checker.run()] - self.assertEqual(len(ret), 0) - - -if sys.version_info >= (3, 6): - - @given(source_code=hypothesmith.from_grammar()) - def test_builtin_works_on_many_examples(source_code): - try: - source = source_code.encode('utf-8-sig') - except UnicodeEncodeError: - reject() - raise - tree = ast.parse(source) - checker = BuiltinsChecker(tree, '/home/script.py') - for c in checker.run(): - assert isinstance(c, list) + source = 'a, *(b, c) = 1, 2, 3' + self.check_code(source) diff --git a/setup.cfg b/setup.cfg index d3460c7..43fd837 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,8 +9,4 @@ ignore = .installed.cfg [isort] -force_alphabetical_sort = True -force_single_line = True -lines_after_imports = 2 -line_length = 200 -not_skip = __init__.py +profile = black diff --git a/setup.py b/setup.py index 9b94033..e176829 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,18 @@ -# -*- coding: utf-8 -*- from setuptools import setup - short_description = 'Check for python builtins being used as variables or parameters.' -long_description = '{0}\n{1}'.format( - open('README.rst').read(), - open('CHANGES.rst').read(), -) +def read_file(filename): + with open(filename) as file_obj: + file_contents = file_obj.read() + return file_contents + + +long_description = f""" +{read_file('README.rst')} +{read_file('CHANGES.rst')} +""" setup( @@ -16,7 +20,7 @@ version='1.5.4.dev0', description=short_description, long_description=long_description, - # Get more from https://pypi.org/pypi?%3Aaction=list_classifiers + # Get more from https://pypi.org/classifiers/ classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -25,10 +29,8 @@ 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', @@ -51,11 +53,7 @@ ], extras_require={ 'test': [ - 'coverage', - 'coveralls', - 'mock', 'pytest', - 'pytest-cov', ], }, entry_points={