flake8-comprehensions-3.14.0/0000775000175000017500000000000014452776613015746 5ustar jriverojriveroflake8-comprehensions-3.14.0/tests/0000775000175000017500000000000014452776613017110 5ustar jriverojriveroflake8-comprehensions-3.14.0/tests/__init__.py0000664000175000017500000000000014452776613021207 0ustar jriverojriveroflake8-comprehensions-3.14.0/tests/test_flake8_comprehensions.py0000664000175000017500000006326214452776613025020 0ustar jriverojriverofrom __future__ import annotations import re from importlib.metadata import version from textwrap import dedent import pytest @pytest.fixture def flake8_path(flake8_path): (flake8_path / "setup.cfg").write_text( dedent( """\ [flake8] select = C4 """ ) ) yield flake8_path def test_version(flake8_path): result = flake8_path.run_flake8(["--version"]) version_regex = r"flake8-comprehensions:( )*" + version("flake8-comprehensions") unwrapped = "".join(result.out_lines) assert re.search(version_regex, unwrapped) @pytest.mark.parametrize( "code", [ "foo = [x + 1 for x in range(10)]", ], ) def test_C400_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = list(x + 1 for x in range(10))", [ "./example.py:1:7: C400 Unnecessary generator - rewrite as a list " + "comprehension." ], ), ( """\ foobar = list( str(x) for x in range(10) ) """, [ "./example.py:1:10: C400 Unnecessary generator - rewrite as a list " + "comprehension." ], ), ], ) def test_C400_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = {x + 1 for x in range(10)}", ], ) def test_C401_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = set(x + 1 for x in range(10))", [ "./example.py:1:7: C401 Unnecessary generator - rewrite as a set " + "comprehension." ], ), ( """\ foobar = set( str(x) for x in range(10) ) """, [ "./example.py:1:10: C401 Unnecessary generator - rewrite as a set " + "comprehension." ], ), ], ) def test_C401_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = {x: str(x) for x in range(10)}", """\ foo = ['a=1', 'b=2', 'c=3'] dict(pair.split('=') for pair in foo) """, """\ foo = [('a', 1), ('b', 2), ('c', 3)] dict(pair for pair in foo if pair[1] % 2 == 0) """, # Previously a false positive: "dict(((x, str(x)) for x in range(10)), c=1)", ], ) def test_C402_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = dict((x, str(x)) for x in range(10))", [ "./example.py:1:7: C402 Unnecessary generator - rewrite as a dict " + "comprehension." ], ), ( """\ foobar = dict( (x, str(x)) for x in range(10) ) """, [ "./example.py:1:10: C402 Unnecessary generator - rewrite as a dict " + "comprehension." ], ), ], ) def test_C402_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = {x + 1 for x in range(10)}", ], ) def test_C403_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = set([x + 1 for x in range(10)])", [ "./example.py:1:7: C403 Unnecessary list comprehension - rewrite as a " + "set comprehension." ], ), ], ) def test_C403_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = {x: x for x in range(10)}", # Previously a false positive: "foo = dict([x.split('=') for x in ['a=1', 'b=2']])", # Previously a false positive: "dict([(x, x) for x in range(10)], y=2)", ], ) def test_C404_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = dict([(x, x) for x in range(10)])", [ "./example.py:1:7: C404 Unnecessary list comprehension - rewrite as a " + "dict comprehension." ], ), ], ) def test_C404_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = set(range)", ], ) def test_C405_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = set([])", [ "./example.py:1:7: C405 Unnecessary list literal - rewrite as a set " + "literal." ], ), ( "foo = set([1])", [ "./example.py:1:7: C405 Unnecessary list literal - rewrite as a set " + "literal." ], ), ( "foo = set(())", [ "./example.py:1:7: C405 Unnecessary tuple literal - rewrite as a set " + "literal." ], ), ( "foo = set((1,))", [ "./example.py:1:7: C405 Unnecessary tuple literal - rewrite as a set " + "literal." ], ), ], ) def test_C405_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = dict(range)", "something = (1, 2); dict([something])", "dict([(1,)])", ], ) def test_C406_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = dict([])", [ "./example.py:1:7: C406 Unnecessary list literal - rewrite as a dict " + "literal." ], ), ( "foo = dict([(1, 2)])", [ "./example.py:1:7: C406 Unnecessary list literal - rewrite as a dict " + "literal." ], ), ( "foo = dict(())", [ "./example.py:1:7: C406 Unnecessary tuple literal - rewrite as a dict " + "literal." ], ), ( "foo = dict(((1, 2),))", [ "./example.py:1:7: C406 Unnecessary tuple literal - rewrite as a dict " + "literal." ], ), ], ) def test_C406_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "()", "[]", "{}", "set()", """\ foo = [('foo', 2)] dict(foo) """, """\ foo = {} dict(bar=1, **foo) """, """\ foo = [1, 2] list(foo) """, """\ foo = [1, 2] list(*foo) """, ], ) def test_C408_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "tuple()", ["./example.py:1:1: C408 Unnecessary tuple call - rewrite as a literal."], ), ( "list()", ["./example.py:1:1: C408 Unnecessary list call - rewrite as a literal."], ), ( "dict()", ["./example.py:1:1: C408 Unnecessary dict call - rewrite as a literal."], ), ( "dict(a=1)", ["./example.py:1:1: C408 Unnecessary dict call - rewrite as a literal."], ), ], ) def test_C408_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = tuple(range)", ], ) def test_C409_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = tuple([])", [ "./example.py:1:7: C409 Unnecessary list passed to tuple() - " + "rewrite as a tuple literal." ], ), ( "foo = tuple([1, 2])", [ "./example.py:1:7: C409 Unnecessary list passed to tuple() - " + "rewrite as a tuple literal." ], ), ( "foo = tuple(())", [ "./example.py:1:7: C409 Unnecessary tuple passed to tuple() - remove " + "the outer call to tuple()." ], ), ( "foo = tuple((1, 2))", [ "./example.py:1:7: C409 Unnecessary tuple passed to tuple() - remove " + "the outer call to tuple()." ], ), ], ) def test_C409_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "foo = list(range)", ], ) def test_C410_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "foo = list([])", [ "./example.py:1:7: C410 Unnecessary list passed to list() - remove the " + "outer call to list()." ], ), ( "foo = list([1, 2])", [ "./example.py:1:7: C410 Unnecessary list passed to list() - remove the " + "outer call to list()." ], ), ( "foo = list(())", [ "./example.py:1:7: C410 Unnecessary tuple passed to list() - " + "rewrite as a list literal." ], ), ( "foo = list((1, 2))", [ "./example.py:1:7: C410 Unnecessary tuple passed to list() - " + "rewrite as a list literal." ], ), ], ) def test_C410_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "[x + 1 for x in range(10)]", ], ) def test_C411_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "list([x + 1 for x in range(10)])", [ "./example.py:1:1: C411 Unnecessary list call - remove the outer call " + "to list()." ], ), ], ) def test_C411_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "sorted([2, 3, 1])", "sorted([2, 3, 1], reverse=True)", "sorted([2, 3, 1], reverse=False)", "sorted([2, 3, 1], reverse=0)", "sorted([2, 3, 1], reverse=1)", "reversed([2, 3, 1])", ], ) def test_C413_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "list(sorted([2, 3, 1]))", ["./example.py:1:1: C413 Unnecessary list call around sorted()."], ), ( "reversed(sorted([2, 3, 1]))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - use sorted(..., reverse=True)." ], ), ( "reversed(sorted([2, 3, 1], reverse=False))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - use sorted(..., reverse=True)." ], ), ( "reversed(sorted([2, 3, 1], reverse=True))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - use sorted(..., reverse=False)." ], ), ( "reversed(sorted([2, 3, 1], reverse=0))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - use sorted(..., reverse=True)." ], ), ( "reversed(sorted([2, 3, 1], reverse=1))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - use sorted(..., reverse=False)." ], ), ( "reversed(sorted([2, 3, 1], reverse=bool()))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - toggle reverse argument to sorted()." ], ), ( "reversed(sorted([2, 3, 1], reverse=not True))", [ "./example.py:1:1: C413 Unnecessary reversed call around sorted()" + " - toggle reverse argument to sorted()." ], ), ], ) def test_C413_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "list(set(a))", "tuple(set(a))", "sorted(set(a))", ], ) def test_C414_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "list(list(a))", ["./example.py:1:1: C414 Unnecessary list call within list()."], ), ( "list(tuple(a))", ["./example.py:1:1: C414 Unnecessary tuple call within list()."], ), ( "tuple(list(a))", ["./example.py:1:1: C414 Unnecessary list call within tuple()."], ), ( "tuple(tuple(a))", ["./example.py:1:1: C414 Unnecessary tuple call within tuple()."], ), ("set(set(a))", ["./example.py:1:1: C414 Unnecessary set call within set()."]), ( "set(list(a))", ["./example.py:1:1: C414 Unnecessary list call within set()."], ), ( "set(tuple(a))", ["./example.py:1:1: C414 Unnecessary tuple call within set()."], ), ( "set(sorted(a))", ["./example.py:1:1: C414 Unnecessary sorted call within set()."], ), ( "set(sorted(a, reverse=True))", ["./example.py:1:1: C414 Unnecessary sorted call within set()."], ), ( "set(reversed(a))", ["./example.py:1:1: C414 Unnecessary reversed call within set()."], ), ( "sorted(list(a))", ["./example.py:1:1: C414 Unnecessary list call within sorted()."], ), ( "sorted(tuple(a))", ["./example.py:1:1: C414 Unnecessary tuple call within sorted()."], ), ( "sorted(sorted(a))", ["./example.py:1:1: C414 Unnecessary sorted call within sorted()."], ), ( "sorted(sorted(a), reverse=True)", ["./example.py:1:1: C414 Unnecessary sorted call within sorted()."], ), ( "sorted(sorted(a, reverse=True))", ["./example.py:1:1: C414 Unnecessary sorted call within sorted()."], ), ( "sorted(sorted(a, reverse=True), reverse=True)", ["./example.py:1:1: C414 Unnecessary sorted call within sorted()."], ), ( "sorted(reversed(a))", ["./example.py:1:1: C414 Unnecessary reversed call within sorted()."], ), ( "sorted(reversed(a), reverse=True)", ["./example.py:1:1: C414 Unnecessary reversed call within sorted()."], ), ], ) def test_C414_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "set([2, 3, 1][::1])", "sorted([2, 3, 1][::1])", "reversed([2, 3, 1][::1])", ], ) def test_C415_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "set([2, 3, 1][::-1])", [ "./example.py:1:1: C415 Unnecessary subscript reversal of iterable " + "within set()." ], ), ( "sorted([2, 3, 1][::-1])", [ "./example.py:1:1: C415 Unnecessary subscript reversal of iterable " + "within sorted()." ], ), ( "sorted([2, 3, 1][::-1], reverse=True)", [ "./example.py:1:1: C415 Unnecessary subscript reversal of iterable " + "within sorted()." ], ), ( "reversed([2, 3, 1][::-1])", [ "./example.py:1:1: C415 Unnecessary subscript reversal of iterable " + "within reversed()." ], ), ], ) def test_C415_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "{x, y for x, y, z in zip('abc', '123', 'def')}", "{y: x for x, y in zip('abc', '123')}", "{x: y for x, (y,) in zip('a', ('1',))}", "{x: z for x, (y,), z in zip('a', ('1',), 'b')}", "[str(x) for x in range(5)]", "[x + 1 for x in range(5)]", "[x for x in range(5) if x % 2]", "{str(x) for x in range(5)}", "{x + 1 for x in range(5)}", "{x for x in range(5) if x % 2}", """\ async def foo(): [x async for x in range(5)] """, """\ async def foo(): return {x async for x in range(5)} """, "[(x, y, 1) for x, y in []]", # We can't assume unpacking came from tuples: "[(x, y) for x, y in zip('abc', '123')]", "[(x, y) for (x, y) in zip('abc', '123')]", "{(x, y) for x, y in zip('abc', '123')}", "{(x, y) for (x, y) in zip('abc', '123')}", ], ) def test_C416_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "{x: y for x, y in zip(range(5), range(5))}", [ "./example.py:1:1: C416 Unnecessary dict comprehension - " + "rewrite using dict().", ], ), ( "{x: y for (x, y) in zip(range(5), range(5))}", [ "./example.py:1:1: C416 Unnecessary dict comprehension - " + "rewrite using dict().", ], ), ( "[x for x in range(5)]", [ "./example.py:1:1: C416 Unnecessary " + "list comprehension - rewrite using list()." ], ), ( "{x for x in range(5)}", [ "./example.py:1:1: C416 Unnecessary set comprehension - " + "rewrite using set().", ], ), ], ) def test_C416_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "map()", "map(str, numbers)", "list(map())", "list(map(str, numbers))", "set(map(f, items))", "dict(map(enumerate, values))", "dict(map(lambda v: data[v], values))", ], ) def test_C417_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "map(lambda x: x * 2, iterable)", [ "./example.py:1:1: C417 Unnecessary use of map - " + "use a generator expression instead.", ], ), ( "list(map(lambda x: x * 2, iterable))", [ "./example.py:1:1: C417 Unnecessary use of map - " + "use a list comprehension instead.", ], ), ( "set(map(lambda num: num % 2 == 0, nums))", [ "./example.py:1:1: C417 Unnecessary use of map - " + "use a set comprehension instead.", ], ), ( "dict(map(lambda v: (v, v ** 2), values))", [ "./example.py:1:1: C417 Unnecessary use of map - " "use a dict comprehension instead.", ], ), ], ) def test_C417_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "dict({}, a=1)", "dict({x: 1 for x in range(1)}, a=1)", ], ) def test_C418_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "dict({})", [ "./example.py:1:1: C418 Unnecessary dict passed to dict() - " + "remove the outer call to dict()." ], ), ( "dict({'a': 1})", [ "./example.py:1:1: C418 Unnecessary dict passed to dict() - " + "remove the outer call to dict()." ], ), ( "dict({'x': 1 for x in range(10)})", [ "./example.py:1:1: C418 Unnecessary dict comprehension passed " + "to dict() - remove the outer call to dict()." ], ), ], ) def test_C418_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures @pytest.mark.parametrize( "code", [ "any(num == 3 for num in range(5))", "all(num == 3 for num in range(5))", ], ) def test_C419_pass(code, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == [] @pytest.mark.parametrize( "code,failures", [ ( "any([num == 3 for num in range(5)])", [ "./example.py:1:1: C419 Unnecessary list comprehension passed " + "to any() prevents short-circuiting - rewrite as a generator." ], ), ( "all([num == 3 for num in range(5)])", [ "./example.py:1:1: C419 Unnecessary list comprehension passed " + "to all() prevents short-circuiting - rewrite as a generator." ], ), ], ) def test_C419_fail(code, failures, flake8_path): (flake8_path / "example.py").write_text(dedent(code)) result = flake8_path.run_flake8() assert result.out_lines == failures flake8-comprehensions-3.14.0/requirements/0000775000175000017500000000000014452776613020471 5ustar jriverojriveroflake8-comprehensions-3.14.0/requirements/compile.py0000775000175000017500000000212314452776613022474 0ustar jriverojrivero#!/usr/bin/env python from __future__ import annotations import os import subprocess import sys from pathlib import Path if __name__ == "__main__": os.chdir(Path(__file__).parent) os.environ["CUSTOM_COMPILE_COMMAND"] = "requirements/compile.py" os.environ["PIP_REQUIRE_VIRTUALENV"] = "0" common_args = [ "-m", "piptools", "compile", "--generate-hashes", "--allow-unsafe", ] + sys.argv[1:] subprocess.run( ["python3.8", *common_args, "-o", "py38.txt"], check=True, capture_output=True, ) subprocess.run( ["python3.9", *common_args, "-o", "py39.txt"], check=True, capture_output=True, ) subprocess.run( ["python3.10", *common_args, "-o", "py310.txt"], check=True, capture_output=True, ) subprocess.run( ["python3.11", *common_args, "-o", "py311.txt"], check=True, capture_output=True, ) subprocess.run( ["python3.12", *common_args, "-o", "py312.txt"], check=True, capture_output=True, ) flake8-comprehensions-3.14.0/requirements/py312.txt0000664000175000017500000000434414452776613022115 0ustar jriverojrivero# # This file is autogenerated by pip-compile with Python 3.12 # by the following command: # # requirements/compile.py # flake8==6.0.0 \ --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via pytest-flake8-path iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via flake8 packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest pycodestyle==2.10.0 \ --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via flake8 pyflakes==3.0.1 \ --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 pytest==7.3.2 \ --hash=sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295 \ --hash=sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.4.0 \ --hash=sha256:b1c21f599158c90b68a16d301d1ffa19d25c297ef22f0ca4560160065a852e8b \ --hash=sha256:c873d169e4ea5ab889413212bc33eb13d653c0e7f6c630fce6dc982076a228b7 # via -r requirements.in pytest-randomly==3.12.0 \ --hash=sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2 \ --hash=sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd # via -r requirements.in flake8-comprehensions-3.14.0/requirements/py311.txt0000664000175000017500000000434414452776613022114 0ustar jriverojrivero# # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # requirements/compile.py # flake8==6.0.0 \ --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via pytest-flake8-path iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via flake8 packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest pycodestyle==2.10.0 \ --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via flake8 pyflakes==3.0.1 \ --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 pytest==7.3.2 \ --hash=sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295 \ --hash=sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.4.0 \ --hash=sha256:b1c21f599158c90b68a16d301d1ffa19d25c297ef22f0ca4560160065a852e8b \ --hash=sha256:c873d169e4ea5ab889413212bc33eb13d653c0e7f6c630fce6dc982076a228b7 # via -r requirements.in pytest-randomly==3.12.0 \ --hash=sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2 \ --hash=sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd # via -r requirements.in flake8-comprehensions-3.14.0/requirements/requirements.in0000664000175000017500000000012614452776613023543 0ustar jriverojriveroimportlib-metadata ; python_version < "3.8" pytest pytest-flake8-path pytest-randomly flake8-comprehensions-3.14.0/requirements/py38.txt0000664000175000017500000000605614452776613022044 0ustar jriverojrivero# # This file is autogenerated by pip-compile with Python 3.8 # by the following command: # # requirements/compile.py # exceptiongroup==1.1.1 \ --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \ --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785 # via pytest flake8==6.0.0 \ --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via pytest-flake8-path importlib-metadata==6.6.0 \ --hash=sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed \ --hash=sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705 # via pytest-randomly iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via flake8 packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest pycodestyle==2.10.0 \ --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via flake8 pyflakes==3.0.1 \ --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 pytest==7.3.2 \ --hash=sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295 \ --hash=sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.4.0 \ --hash=sha256:b1c21f599158c90b68a16d301d1ffa19d25c297ef22f0ca4560160065a852e8b \ --hash=sha256:c873d169e4ea5ab889413212bc33eb13d653c0e7f6c630fce6dc982076a228b7 # via -r requirements.in pytest-randomly==3.12.0 \ --hash=sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2 \ --hash=sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via pytest zipp==3.15.0 \ --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 # via importlib-metadata flake8-comprehensions-3.14.0/requirements/py310.txt0000664000175000017500000000517514452776613022116 0ustar jriverojrivero# # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # requirements/compile.py # exceptiongroup==1.1.1 \ --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \ --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785 # via pytest flake8==6.0.0 \ --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via pytest-flake8-path iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via flake8 packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest pycodestyle==2.10.0 \ --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via flake8 pyflakes==3.0.1 \ --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 pytest==7.3.2 \ --hash=sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295 \ --hash=sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.4.0 \ --hash=sha256:b1c21f599158c90b68a16d301d1ffa19d25c297ef22f0ca4560160065a852e8b \ --hash=sha256:c873d169e4ea5ab889413212bc33eb13d653c0e7f6c630fce6dc982076a228b7 # via -r requirements.in pytest-randomly==3.12.0 \ --hash=sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2 \ --hash=sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via pytest flake8-comprehensions-3.14.0/requirements/py39.txt0000664000175000017500000000605614452776613022045 0ustar jriverojrivero# # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # # requirements/compile.py # exceptiongroup==1.1.1 \ --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \ --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785 # via pytest flake8==6.0.0 \ --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via pytest-flake8-path importlib-metadata==6.6.0 \ --hash=sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed \ --hash=sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705 # via pytest-randomly iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via flake8 packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest pycodestyle==2.10.0 \ --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via flake8 pyflakes==3.0.1 \ --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 pytest==7.3.2 \ --hash=sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295 \ --hash=sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.4.0 \ --hash=sha256:b1c21f599158c90b68a16d301d1ffa19d25c297ef22f0ca4560160065a852e8b \ --hash=sha256:c873d169e4ea5ab889413212bc33eb13d653c0e7f6c630fce6dc982076a228b7 # via -r requirements.in pytest-randomly==3.12.0 \ --hash=sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2 \ --hash=sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via pytest zipp==3.15.0 \ --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 # via importlib-metadata flake8-comprehensions-3.14.0/README.rst0000664000175000017500000002426414452776613017445 0ustar jriverojrivero===================== flake8-comprehensions ===================== .. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-comprehensions/main.yml?branch=main&style=for-the-badge :target: https://github.com/adamchainz/flake8-comprehensions/actions?workflow=CI .. image:: https://img.shields.io/pypi/v/flake8-comprehensions.svg?style=for-the-badge :target: https://pypi.org/project/flake8-comprehensions/ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge :target: https://github.com/psf/black .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge :target: https://github.com/pre-commit/pre-commit :alt: pre-commit A `flake8 `_ plugin that helps you write better list/set/dict comprehensions. Requirements ============ Python 3.8 to 3.12 supported. Installation ============ First, install with ``pip``: .. code-block:: sh python -m pip install flake8-comprehensions Second, if you define Flake8’s ``select`` setting, add the ``C4`` prefix to it. Otherwise, the plugin should be active by default. ---- **Linting a Django project?** Check out my book `Boost Your Django DX `__ which covers Flake8 and many other code quality tools. ---- Rules ===== C400-402: Unnecessary generator - rewrite as a ```` comprehension. --------------------------------------------------------------------------------- Rules: * C400 Unnecessary generator - rewrite as a list comprehension. * C401 Unnecessary generator - rewrite as a set comprehension. * C402 Unnecessary generator - rewrite as a dict comprehension. It's unnecessary to use ``list``, ``set``, or ``dict`` around a generator expression, since there are equivalent comprehensions for these types. For example: * Rewrite ``list(f(x) for x in foo)`` as ``[f(x) for x in foo]`` * Rewrite ``set(f(x) for x in foo)`` as ``{f(x) for x in foo}`` * Rewrite ``dict((x, f(x)) for x in foo)`` as ``{x: f(x) for x in foo}`` C403-404: Unnecessary list comprehension - rewrite as a ```` comprehension. ------------------------------------------------------------------------------------- Rules: * C403 Unnecessary list comprehension - rewrite as a set comprehension. * C404 Unnecessary list comprehension - rewrite as a dict comprehension. It's unnecessary to use a list comprehension inside a call to ``set`` or ``dict``, since there are equivalent comprehensions for these types. For example: * Rewrite ``set([f(x) for x in foo])`` as ``{f(x) for x in foo}`` * Rewrite ``dict([(x, f(x)) for x in foo])`` as ``{x: f(x) for x in foo}`` C405-406: Unnecessary ```` literal - rewrite as a ```` literal. ------------------------------------------------------------------------------------- * C405 Unnecessary ```` literal - rewrite as a set literal. * C406 Unnecessary ```` literal - rewrite as a dict literal. It's unnecessary to use a list or tuple literal within a call to ``set`` or ``dict``. For example: * Rewrite ``set([1, 2])`` as ``{1, 2}`` * Rewrite ``set((1, 2))`` as ``{1, 2}`` * Rewrite ``set([])`` as ``set()`` * Rewrite ``dict([(1, 2)])`` as ``{1: 2}`` * Rewrite ``dict(((1, 2),))`` as ``{1: 2}`` * Rewrite ``dict([])`` as ``{}`` C407: Unnecessary ```` comprehension - ```` can take a generator ------------------------------------------------------------------------------------ This rule was dropped in version 3.4.0, because it promoted an increase in laziness which could lead to bugs. C408: Unnecessary ```` call - rewrite as a literal. -------------------------------------------------------------------- It's slower to call e.g. ``dict()`` than using the empty literal, because the name ``dict`` must be looked up in the global scope in case it has been rebound. Same for the other two basic types here. For example: * Rewrite ``dict()`` as ``{}`` * Rewrite ``dict(a=1, b=2)`` as ``{"a": 1, "b": 2}`` * Rewrite ``list()`` as ``[]`` * Rewrite ``tuple()`` as ``()`` C409-410: Unnecessary ```` passed to ````\() - ````. ------------------------------------------------------------------------------------ Rules: * C409 Unnecessary ```` passed to tuple() - ````. * C410 Unnecessary list passed to list() - ````. Where ```` is either: * remove the outer call to ````\() * rewrite as a ```` literal It's unnecessary to use a list or tuple literal within a call to ``list`` or ``tuple``, since there is literal syntax for these types. For example: * Rewrite ``tuple([1, 2])`` as ``(1, 2)`` * Rewrite ``tuple((1, 2))`` as ``(1, 2)`` * Rewrite ``tuple([])`` as ``()`` * Rewrite ``list([1, 2])`` as ``[1, 2]`` * Rewrite ``list((1, 2))`` as ``[1, 2]`` * Rewrite ``list([])`` as ``[]`` C411: Unnecessary list call - remove the outer call to list(). -------------------------------------------------------------- It's unnecessary to use a ``list`` around a list comprehension, since it is equivalent without it. For example: * Rewrite ``list([f(x) for x in foo])`` as ``[f(x) for x in foo]`` C412: Unnecessary ```` comprehension - 'in' can take a generator. -------------------------------------------------------------------------------- This rule was dropped in version 3.4.0, because it promoted an increase in laziness which could lead to bugs. C413: Unnecessary ```` call around sorted(). ----------------------------------------------------------- It's unnecessary to use ``list()`` around ``sorted()`` as it already returns a list. It is also unnecessary to use ``reversed()`` around ``sorted()`` as the latter has a ``reverse`` argument. For example: * Rewrite ``list(sorted([2, 3, 1]))`` as ``sorted([2, 3, 1])`` * Rewrite ``reversed(sorted([2, 3, 1]))`` as ``sorted([2, 3, 1], reverse=True)`` * Rewrite ``reversed(sorted([2, 3, 1], reverse=True))`` as ``sorted([2, 3, 1])`` C414: Unnecessary ```` call within ````\(). -------------------------------------------------------------------------------------------------- It's unnecessary to double-cast or double-process iterables by wrapping the listed functions within ``list``/``set``/``sorted``/``tuple``. For example: * Rewrite ``list(list(iterable))`` as ``list(iterable)`` * Rewrite ``list(tuple(iterable))`` as ``list(iterable)`` * Rewrite ``tuple(list(iterable))`` as ``tuple(iterable)`` * Rewrite ``tuple(tuple(iterable))`` as ``tuple(iterable)`` * Rewrite ``set(set(iterable))`` as ``set(iterable)`` * Rewrite ``set(list(iterable))`` as ``set(iterable)`` * Rewrite ``set(tuple(iterable))`` as ``set(iterable)`` * Rewrite ``set(sorted(iterable))`` as ``set(iterable)`` * Rewrite ``set(reversed(iterable))`` as ``set(iterable)`` * Rewrite ``sorted(list(iterable))`` as ``sorted(iterable)`` * Rewrite ``sorted(tuple(iterable))`` as ``sorted(iterable)`` * Rewrite ``sorted(sorted(iterable))`` as ``sorted(iterable)`` * Rewrite ``sorted(reversed(iterable))`` as ``sorted(iterable)`` C415: Unnecessary subscript reversal of iterable within ````\(). ------------------------------------------------------------------------------------- It's unnecessary to reverse the order of an iterable when passing it into one of the listed functions will change the order again. For example: * Rewrite ``set(iterable[::-1])`` as ``set(iterable)`` * Rewrite ``sorted(iterable)[::-1]`` as ``sorted(iterable, reverse=True)`` * Rewrite ``reversed(iterable[::-1])`` as ``iterable`` C416: Unnecessary ```` comprehension - rewrite using ````\(). ------------------------------------------------------------------------------------------- It's unnecessary to use a dict/list/set comprehension to build a data structure if the elements are unchanged. Wrap the iterable with ``dict()``, ``list()``, or ``set()`` instead. For example: * Rewrite ``{a: b for a, b in iterable}`` as ``dict(iterable)`` * Rewrite ``[x for x in iterable]`` as ``list(iterable)`` * Rewrite ``{x for x in iterable}`` as ``set(iterable)`` C417: Unnecessary ``map`` usage - rewrite using a generator expression/```` comprehension. --------------------------------------------------------------------------------------------------------- ``map(func, iterable)`` has great performance when ``func`` is a built-in function, and it makes sense if your function already has a name. But if your func is a ``lambda``, it’s faster to use a generator expression or a comprehension, as it avoids the function call overhead. For example: * Rewrite ``map(lambda x: x + 1, iterable)`` to ``(x + 1 for x in iterable)`` * Rewrite ``map(lambda item: get_id(item), items)`` to ``(get_id(item) for item in items)`` * Rewrite ``list(map(lambda num: num * 2, nums))`` to ``[num * 2 for num in nums]`` * Rewrite ``set(map(lambda num: num % 2 == 0, nums))`` to ``{num % 2 == 0 for num in nums}`` * Rewrite ``dict(map(lambda v: (v, v ** 2), values))`` to ``{v : v ** 2 for v in values}`` C418: Unnecessary ```` passed to dict() - remove the outer call to dict() -------------------------------------------------------------------------------------------------- It's unnecessary to use a ``dict`` around a dict literal or dict comprehension, since either syntax already constructs a dict. For example: * Rewrite ``dict({})`` as ``{}`` * Rewrite ``dict({"a": 1})`` as ``{"a": 1}`` C419 Unnecessary list comprehension in ````\() prevents short-circuiting - rewrite as a generator. ----------------------------------------------------------------------------------------------------------- Using a list comprehension inside a call to ``any()``/``all()`` prevents short-circuiting when a ``True`` / ``False`` value is found. The whole list will be constructed before calling ``any()``/``all()``, potentially wasting work.part-way. Rewrite to use a generator expression, which can stop part way. For example: * Rewrite ``all([condition(x) for x in iterable])`` as ``all(condition(x) for x in iterable)`` * Rewrite ``any([condition(x) for x in iterable])`` as ``any(condition(x) for x in iterable)`` flake8-comprehensions-3.14.0/pyproject.toml0000664000175000017500000000070014452776613020657 0ustar jriverojrivero[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools", ] [tool.black] target-version = ['py38'] [tool.pytest.ini_options] addopts = """\ --strict-config --strict-markers """ [tool.mypy] mypy_path = "src/" namespace_packages = false show_error_codes = true strict = true warn_unreachable = true [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [tool.rstcheck] report_level = "ERROR" flake8-comprehensions-3.14.0/.gitignore0000664000175000017500000000007714452776613017742 0ustar jriverojrivero*.egg-info/ *.pyc /.coverage /.coverage.* /.tox /build/ /dist/ flake8-comprehensions-3.14.0/.github/0000775000175000017500000000000014452776613017306 5ustar jriverojriveroflake8-comprehensions-3.14.0/.github/CODE_OF_CONDUCT.md0000664000175000017500000000013114452776613022100 0ustar jriverojriveroThis project follows [Django's Code of Conduct](https://www.djangoproject.com/conduct/). flake8-comprehensions-3.14.0/.github/ISSUE_TEMPLATE/0000775000175000017500000000000014452776613021471 5ustar jriverojriveroflake8-comprehensions-3.14.0/.github/ISSUE_TEMPLATE/issue.yml0000664000175000017500000000152614452776613023350 0ustar jriverojriveroname: Issue description: File an issue body: - type: input id: python_version attributes: label: Python Version description: Which version of Python were you using? placeholder: 3.9.0 validations: required: false - type: input id: flake8_version attributes: label: flake8 Version description: Which version of flake8 were you using? placeholder: 3.9.2 validations: required: false - type: input id: package_version attributes: label: Package Version description: Which version of this package were you using? If not the latest version, please check this issue has not since been resolved. placeholder: 1.0.0 validations: required: false - type: textarea id: description attributes: label: Description description: Please describe your issue. validations: required: true flake8-comprehensions-3.14.0/.github/ISSUE_TEMPLATE/config.yml0000664000175000017500000000003414452776613023456 0ustar jriverojriveroblank_issues_enabled: false flake8-comprehensions-3.14.0/.github/ISSUE_TEMPLATE/feature-request.yml0000664000175000017500000000041114452776613025331 0ustar jriverojriveroname: Feature Request description: Request an enhancement or new feature. body: - type: textarea id: description attributes: label: Description description: Please describe your feature request with appropriate detail. validations: required: true flake8-comprehensions-3.14.0/.github/workflows/0000775000175000017500000000000014452776613021343 5ustar jriverojriveroflake8-comprehensions-3.14.0/.github/workflows/main.yml0000664000175000017500000000162714452776613023020 0ustar jriverojriveroname: CI on: push: branches: - main pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-22.04 strategy: matrix: python-version: - 3.8 - 3.9 - '3.10' - '3.11' - '3.12' steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} allow-prereleases: true cache: pip cache-dependency-path: 'requirements/*.txt' - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade 'tox>=4.0.0rc3' - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) flake8-comprehensions-3.14.0/.github/dependabot.yml0000664000175000017500000000015214452776613022134 0ustar jriverojriveroversion: 2 updates: - package-ecosystem: github-actions directory: "/" schedule: interval: weekly flake8-comprehensions-3.14.0/.github/SECURITY.md0000664000175000017500000000010114452776613021067 0ustar jriverojriveroPlease report security issues directly over email to me@adamj.eu flake8-comprehensions-3.14.0/CHANGELOG.rst0000664000175000017500000001761514452776613020001 0ustar jriverojrivero========= Changelog ========= 3.14.0 (2023-07-10) ------------------- * Drop Python 3.7 support. 3.13.0 (2023-06-15) ------------------- * Support Python 3.12. 3.12.0 (2023-04-13) ------------------- * Add rule C418 to check for calls passing a dict literal or dict comprehension to ``dict()``. * Add rule C419 to check for calls passing a list comprehension to ``any()``/``all()``. 3.11.1 (2023-03-21) ------------------- * Fix false positives in C406 “unnecessary dict literal”. Fixes `Issue #260 `__. 3.11.0 (2023-03-18) ------------------- * Expand C416 to ``dict`` comprehensions. Thanks to Aaron Gokaslan in `PR #490 `__. 3.10.1 (2022-10-29) ------------------- * Fix false positive in rules C402 and C404 for ``dict()`` calls with keyword arguments. Thanks to Anders Kaseorg for the report in `Issue #457 `__. 3.10.0 (2022-05-19) ------------------- * Add rule C417 which recommends rewriting use of ``map()`` with ``lambda`` to an equivalent generator expression or comprehension. Thanks to Tushar Sadhwani in `PR #409 `__. 3.9.0 (2022-05-11) ------------------ * Support Python 3.11. 3.8.0 (2022-01-10) ------------------ * Drop Python 3.6 support. * Remove upper bound on Flake8 version. 3.7.0 (2021-10-11) ------------------ * Support Flake8 4. 3.6.1 (2021-08-16) ------------------ * Fix type hint for ``tree`` argument. Thanks to kasium for the report in `Issue #352 `__. 3.6.0 (2021-08-13) ------------------ * Add type hints. 3.5.0 (2021-05-10) ------------------ * Support Python 3.10. * Stop distributing tests to reduce package size. Tests are not intended to be run outside of the tox setup in the repository. Repackagers can use GitHub's tarballs per tag. 3.4.0 (2021-03-18) ------------------ * Remove rules C407 (Unnecessary ```` comprehension - ```` can take a generator) and C412 (Unnecessary ```` comprehension - 'in' can take a generator). Both rules recommended increasing laziness, which is not always desirable and can lead to subtle bugs. Also, a fully exhausted generator is slower than an equivalent comprehension, so the advice did not always improve performance. Thanks to David Smith, Dylan Young, and Leonidas Loucas for the report in `Issue #247 `__. 3.3.1 (2020-12-19) ------------------ * Drop Python 3.5 support. * Improved installation instructions in README. 3.3.0 (2020-10-23) ------------------ * Support Python 3.9. * Move license from ISC to MIT License. * Partially reverted the change to ``C408`` to make it apply again to when ``dict`` is called with keyword arguments, e.g. ``dict(a=1, b=2)`` will be flagged to be rewritten in the literal form ``{"a": 1, "b": 2}`` 3.2.3 (2020-06-06) ------------------ * Made ``C408`` only apply when no arguments are passed to ``dict``/``list``/``tuple``. 3.2.2 (2020-01-20) ------------------ * Remove check for dict comprehensions in rule C407 as it would also change the results for certain builtins such as ``sum()``. 3.2.1 (2020-01-20) ------------------ * Remove check for set comprehensions in rule C407 as it would change the results for certain builtins such as ``sum()``. 3.2.0 (2020-01-20) ------------------ * Add ``filter`` and ``map`` to rule C407. * Check for dict and set comprehensions in rules C407 and C412. 3.1.4 (2019-11-20) ------------------ * Remove the tuple/unpacking check from C416 to prevent false positives where the type of the iterable is changed from some iterable to a tuple. 3.1.3 (2019-11-19) ------------------ * Ensure the fix for false positives in ``C416`` rule for asynchronous comprehensions runs on Python 3.6 too. 3.1.2 (2019-11-18) ------------------ * Fix false positives in ``C416`` rule for list comprehensions returning tuples. 3.1.1 (2019-11-16) ------------------ * Fix false positives in ``C416`` rule for asynchronous comprehensions. 3.1.0 (2019-11-15) ------------------ * Update Python support to 3.5-3.8. * Fix false positives for C404 for list comprehensions not directly creating tuples. * Add ``C413`` rule that checks for unnecessary use of ``list()`` or ``reversed()`` around ``sorted()``. * Add ``C414`` rule that checks for unnecessary use of the following: * ``list()``, ``reversed()``, ``sorted()``, or ``tuple()`` within ``set`` or ``sorted()`` * ``list()`` or ``tuple()`` within ``list()`` or ``tuple()`` * ``set()`` within ``set`` * Add ``C415`` rule that checks for unnecessary reversal of an iterable via subscript within ``reversed()``, ``set()``, or ``sorted()``. * Add ``C416`` rule that checks for unnecessary list or set comprehensions that can be rewritten using ``list()`` or ``set()``. 3.0.1 (2019-10-28) ------------------ * Fix version display on ``flake8 --version`` (removing dependency on ``cached-property``). Thanks to Jon Dufresne. 3.0.0 (2019-10-25) ------------------ * Update Flake8 support to 3.0+ only. 3.0.0 was released in 2016 and the plugin hasn't been tested with it since. 2.3.0 (2019-10-25) ------------------ * Converted setuptools metadata to configuration file. This meant removing the ``__version__`` attribute from the package. If you want to inspect the installed version, use ``importlib.metadata.version("flake8-comprehensions")`` (`docs `__ / `backport `__). * Add dependencies on ``cached-property`` and ``importlib-metadata``. * Fix false negatives in ``C407`` for cases when ``enumerate`` and ``sum()`` are passed more than one argument. 2.2.0 (2019-08-12) ------------------ * Update Python support to 3.5-3.7, as 3.4 has reached its end of life. * ``C412`` rule that complains about using list comprehension with ``in``. 2.1.0 (2019-03-01) ------------------ * Add missing builtin ``enumerate`` to ``C407``. 2.0.0 (2019-02-02) ------------------ * Drop Python 2 support, only Python 3.4+ is supported now. 1.4.1 (2017-05-17) ------------------ * Fix false positives in ``C408`` for calls using ``*args`` or ``**kwargs``. 1.4.0 (2017-05-14) ------------------ * Plugin now reserves the full ``C4XX`` code space rather than just ``C40X`` * ``C408`` rule that complains about using ``tuple()``, ``list()``, or ``dict()`` instead of a literal. * ``C409`` and ``C410`` rules that complain about an unnecessary list or tuple that could be rewritten as a literal. * ``C411`` rule that complains about using list comprehension inside a ``list()`` call. 1.3.0 (2017-05-01) ------------------ * Don't allow installation with Flake8 3.2.0 which doesn't enable the plugin. This bug was fixed in Flake8 3.2.1. * Prevent false positives of ``C402`` from generators of expressions that aren't two-tuples. * ``C405`` and ``C406`` now also complain about unnecessary tuple literals. 1.2.1 (2016-06-27) ------------------ * ``C407`` rule that complains about unnecessary list comprehensions inside builtins that can work on generators. 1.2.0 (2016-07-11) ------------------ * Split all rule codes by type. This allows granular selection of the rules in flake8 configuration. 1.1.1 (2016-04-06) ------------------ * Fix crash on method calls 1.1.0 (2016-04-06) ------------------ * ``C401`` rule that complains about unnecessary list comprehensions inside calls to ``set()`` or ``dict()``. * ``C402`` rule that complains about unnecessary list literals inside calls to ``set()`` or ``dict()``. 1.0.0 (2016-04-05) ------------------ * ``C400`` rule that complains about an unnecessary usage of a generator when a list/set/dict comprehension would do. flake8-comprehensions-3.14.0/setup.cfg0000664000175000017500000000276514452776613017601 0ustar jriverojrivero[metadata] name = flake8_comprehensions version = 3.14.0 description = A flake8 plugin to help you write better list/set/dict comprehensions. long_description = file: README.rst long_description_content_type = text/x-rst url = https://github.com/adamchainz/flake8-comprehensions author = Adam Johnson author_email = me@adamj.eu license = MIT license_files = LICENSE classifiers = Development Status :: 5 - Production/Stable Framework :: Flake8 Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Typing :: Typed keywords = flake8, comprehensions, list comprehension, set comprehension, dict comprehension project_urls = Changelog = https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst Mastodon = https://fosstodon.org/@adamchainz Twitter = https://twitter.com/adamchainz [options] packages = find: install_requires = flake8!=3.2.0,>=3.0 python_requires = >=3.8 include_package_data = True package_dir = =src zip_safe = False [options.packages.find] where = src [options.entry_points] flake8.extension = C4 = flake8_comprehensions:ComprehensionChecker [flake8] max-line-length = 88 extend-ignore = E203 flake8-comprehensions-3.14.0/LICENSE0000664000175000017500000000205514452776613016755 0ustar jriverojriveroMIT License Copyright (c) 2017 Adam Johnson 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. flake8-comprehensions-3.14.0/.pre-commit-config.yaml0000664000175000017500000000325714452776613022236 0ustar jriverojriverodefault_language_version: python: python3.11 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-added-large-files - id: check-case-conflict - id: check-json - id: check-merge-conflict - id: check-symlinks - id: check-toml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/tox-dev/pyproject-fmt rev: 0.12.1 hooks: - id: pyproject-fmt - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.4.0 hooks: - id: setup-cfg-fmt args: - --include-version-classifiers - --max-py-version - '3.12' - repo: https://github.com/tox-dev/tox-ini-fmt rev: 1.3.1 hooks: - id: tox-ini-fmt - repo: https://github.com/rstcheck/rstcheck rev: v6.1.2 hooks: - id: rstcheck additional_dependencies: - tomli==2.0.1 - repo: https://github.com/asottile/pyupgrade rev: v3.8.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black - repo: https://github.com/adamchainz/blacken-docs rev: 1.14.0 hooks: - id: blacken-docs additional_dependencies: - black==23.1.0 - repo: https://github.com/asottile/reorder-python-imports rev: v3.10.0 hooks: - id: reorder-python-imports args: - --py38-plus - --application-directories - .:example:src - --add-import - 'from __future__ import annotations' - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: - id: flake8 additional_dependencies: - flake8-bugbear - flake8-comprehensions - flake8-tidy-imports - flake8-typing-imports - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.4.1 hooks: - id: mypy flake8-comprehensions-3.14.0/tox.ini0000664000175000017500000000052614452776613017264 0ustar jriverojrivero[tox] requires = tox>=4.2 env_list = py{312, 311, 310, 39, 38} [testenv] package = wheel deps = -r requirements/{envname}.txt set_env = PYTHONDEVMODE = 1 commands = python \ -W error::ResourceWarning \ -W error::DeprecationWarning \ -W error::PendingDeprecationWarning \ -m pytest {posargs:tests} flake8-comprehensions-3.14.0/.editorconfig0000664000175000017500000000034514452776613020425 0ustar jriverojrivero# http://editorconfig.org root = true [*] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.py] indent_size = 4 [Makefile] indent_style = tab flake8-comprehensions-3.14.0/MANIFEST.in0000664000175000017500000000016314452776613017504 0ustar jriverojriveroprune tests include CHANGELOG.rst include LICENSE include pyproject.toml include README.rst include src/*/py.typed flake8-comprehensions-3.14.0/HISTORY.rst0000664000175000017500000000012014452776613017632 0ustar jriverojriveroSee https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst flake8-comprehensions-3.14.0/src/0000775000175000017500000000000014452776613016535 5ustar jriverojriveroflake8-comprehensions-3.14.0/src/flake8_comprehensions/0000775000175000017500000000000014452776613023023 5ustar jriverojriveroflake8-comprehensions-3.14.0/src/flake8_comprehensions/__init__.py0000664000175000017500000003701614452776613025143 0ustar jriverojriverofrom __future__ import annotations import ast from importlib.metadata import version from typing import Any from typing import Generator class ComprehensionChecker: """ Flake8 plugin to help you write better list/set/dict comprehensions. """ name = "flake8-comprehensions" version = version("flake8-comprehensions") __slots__ = ("tree",) def __init__(self, tree: ast.AST) -> None: self.tree = tree messages = { "C400": "C400 Unnecessary generator - rewrite as a list comprehension.", "C401": "C401 Unnecessary generator - rewrite as a set comprehension.", "C402": "C402 Unnecessary generator - rewrite as a dict comprehension.", "C403": "C403 Unnecessary list comprehension - rewrite as a set comprehension.", "C404": ( "C404 Unnecessary list comprehension - rewrite as a dict comprehension." ), "C405": "C405 Unnecessary {type} literal - ", "C406": "C406 Unnecessary {type} literal - ", "C408": "C408 Unnecessary {type} call - rewrite as a literal.", "C409": "C409 Unnecessary {type} passed to tuple() - ", "C410": "C410 Unnecessary {type} passed to list() - ", "C411": "C411 Unnecessary list call - remove the outer call to list().", "C413": "C413 Unnecessary {outer} call around {inner}(){remediation}.", "C414": "C414 Unnecessary {inner} call within {outer}().", "C415": "C415 Unnecessary subscript reversal of iterable within {func}().", "C416": "C416 Unnecessary {type} comprehension - rewrite using {type}().", "C417": "C417 Unnecessary use of map - use a {comp} instead.", "C418": ( "C418 Unnecessary {type} passed to dict() - " + "remove the outer call to dict()." ), "C419": ( "C419 Unnecessary list comprehension passed to {func}() prevents " + "short-circuiting - rewrite as a generator." ), } def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]: # Stores previously seen map() nodes, to avoid raising C417 on it twice. visited_map_calls: set[ast.Call] = set() for node in ast.walk(self.tree): if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): num_positional_args = len(node.args) num_keyword_args = len(node.keywords) if ( num_positional_args == 1 and isinstance(node.args[0], ast.GeneratorExp) and node.func.id in ("list", "set") ): msg_key = {"list": "C400", "set": "C401"}[node.func.id] yield ( node.lineno, node.col_offset, self.messages[msg_key], type(self), ) elif ( num_positional_args == 1 and node.func.id == "dict" and len(node.keywords) == 0 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) and isinstance(node.args[0].elt, ast.Tuple) and len(node.args[0].elt.elts) == 2 ): if isinstance(node.args[0], ast.GeneratorExp): msg = "C402" else: msg = "C404" yield ( node.lineno, node.col_offset, self.messages[msg], type(self), ) elif ( num_positional_args == 1 and isinstance(node.args[0], ast.ListComp) and node.func.id in ("list", "set", "any", "all") ): msg_key = { "list": "C411", "set": "C403", "any": "C419", "all": "C419", }[node.func.id] msg = self.messages[msg_key].format(func=node.func.id) yield ( node.lineno, node.col_offset, msg, type(self), ) elif num_positional_args == 1 and ( isinstance(node.args[0], ast.Tuple) and node.func.id == "tuple" or isinstance(node.args[0], ast.List) and node.func.id == "list" ): suffix = "remove the outer call to {func}()." msg_key = {"tuple": "C409", "list": "C410"}[node.func.id] msg = self.messages[msg_key] + suffix yield ( node.lineno, node.col_offset, msg.format( type=type(node.args[0]).__name__.lower(), func=node.func.id ), type(self), ) elif ( num_positional_args == 1 and num_keyword_args == 0 and isinstance(node.args[0], (ast.Dict, ast.DictComp)) and node.func.id == "dict" ): if isinstance(node.args[0], ast.Dict): type_ = "dict" else: type_ = "dict comprehension" yield ( node.lineno, node.col_offset, self.messages["C418"].format(type=type_), type(self), ) elif ( num_positional_args == 1 and isinstance(node.args[0], (ast.Tuple, ast.List)) and ( node.func.id in ("tuple", "list", "set") or ( node.func.id == "dict" and all( isinstance(i, ast.Tuple) and len(i.elts) == 2 for i in node.args[0].elts ) ) ) ): suffix = "rewrite as a {func} literal." msg_key = { "tuple": "C409", "list": "C410", "set": "C405", "dict": "C406", }[node.func.id] msg = self.messages[msg_key] + suffix yield ( node.lineno, node.col_offset, msg.format( type=type(node.args[0]).__name__.lower(), func=node.func.id ), type(self), ) elif ( num_positional_args == 0 and not has_star_args(node) and not has_double_star_args(node) and node.func.id == "dict" ): yield ( node.lineno, node.col_offset, self.messages["C408"].format(type=node.func.id), type(self), ) elif ( num_positional_args == 0 and num_keyword_args == 0 and node.func.id in ("tuple", "list") ): yield ( node.lineno, node.col_offset, self.messages["C408"].format(type=node.func.id), type(self), ) elif ( node.func.id in {"list", "reversed"} and num_positional_args > 0 and isinstance(node.args[0], ast.Call) and isinstance(node.args[0].func, ast.Name) and node.args[0].func.id == "sorted" ): remediation = "" if node.func.id == "reversed": reverse_flag_value: bool | None = False for keyword in node.args[0].keywords: if keyword.arg != "reverse": continue if isinstance(keyword.value, ast.NameConstant): reverse_flag_value = keyword.value.value elif isinstance(keyword.value, ast.Num): reverse_flag_value = bool(keyword.value.n) else: # Complex value reverse_flag_value = None if reverse_flag_value is None: remediation = " - toggle reverse argument to sorted()" else: remediation = " - use sorted(..., reverse={!r})".format( not reverse_flag_value ) msg = self.messages["C413"].format( inner=node.args[0].func.id, outer=node.func.id, remediation=remediation, ) yield ( node.lineno, node.col_offset, msg, type(self), ) elif ( num_positional_args > 0 and isinstance(node.args[0], ast.Call) and isinstance(node.args[0].func, ast.Name) and ( ( node.func.id in {"set", "sorted"} and node.args[0].func.id in {"list", "reversed", "sorted", "tuple"} ) or ( node.func.id in {"list", "tuple"} and node.args[0].func.id in {"list", "tuple"} ) or (node.func.id == "set" and node.args[0].func.id == "set") ) ): yield ( node.lineno, node.col_offset, self.messages["C414"].format( inner=node.args[0].func.id, outer=node.func.id ), type(self), ) elif ( node.func.id in {"reversed", "set", "sorted"} and num_positional_args > 0 and isinstance(node.args[0], ast.Subscript) and isinstance(node.args[0].slice, ast.Slice) and node.args[0].slice.lower is None and node.args[0].slice.upper is None and isinstance(node.args[0].slice.step, ast.UnaryOp) and isinstance(node.args[0].slice.step.op, ast.USub) and isinstance(node.args[0].slice.step.operand, ast.Num) and node.args[0].slice.step.operand.n == 1 ): yield ( node.lineno, node.col_offset, self.messages["C415"].format(func=node.func.id), type(self), ) elif ( node.func.id == "map" and node not in visited_map_calls and len(node.args) == 2 and isinstance(node.args[0], ast.Lambda) ): yield ( node.lineno, node.col_offset, self.messages["C417"].format(comp="generator expression"), type(self), ) elif ( node.func.id in ("list", "set", "dict") and len(node.args) == 1 and isinstance(node.args[0], ast.Call) and isinstance(node.args[0].func, ast.Name) and node.args[0].func.id == "map" and len(node.args[0].args) == 2 and isinstance(node.args[0].args[0], ast.Lambda) ): # To avoid raising C417 on the map() call inside the list/set/dict. map_call = node.args[0] visited_map_calls.add(map_call) rewriteable = True if node.func.id == "dict": # For the generator expression to be rewriteable as a # dict comprehension, its lambda must return a 2-tuple. lambda_node = node.args[0].args[0] if ( not isinstance(lambda_node.body, (ast.List, ast.Tuple)) or len(lambda_node.body.elts) != 2 ): rewriteable = False if rewriteable: comprehension_type = f"{node.func.id} comprehension" yield ( node.lineno, node.col_offset, self.messages["C417"].format(comp=comprehension_type), type(self), ) elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)): if ( len(node.generators) == 1 and not node.generators[0].ifs and not node.generators[0].is_async and ( ( isinstance(node, (ast.ListComp, ast.SetComp)) and isinstance(node.elt, ast.Name) and isinstance(node.generators[0].target, ast.Name) and node.elt.id == node.generators[0].target.id ) or ( isinstance(node, ast.DictComp) and isinstance(node.key, ast.Name) and isinstance(node.value, ast.Name) and isinstance(node.generators[0].target, ast.Tuple) and len(node.generators[0].target.elts) == 2 and isinstance(node.generators[0].target.elts[0], ast.Name) and node.generators[0].target.elts[0].id == node.key.id and isinstance(node.generators[0].target.elts[1], ast.Name) and node.generators[0].target.elts[1].id == node.value.id ) ) ): yield ( node.lineno, node.col_offset, self.messages["C416"].format(type=comp_type[node.__class__]), type(self), ) def has_star_args(call_node: ast.Call) -> bool: return any(isinstance(a, ast.Starred) for a in call_node.args) def has_double_star_args(call_node: ast.Call) -> bool: return any(k.arg is None for k in call_node.keywords) comp_type = { ast.DictComp: "dict", ast.ListComp: "list", ast.SetComp: "set", } flake8-comprehensions-3.14.0/src/flake8_comprehensions/py.typed0000664000175000017500000000000014452776613024510 0ustar jriverojrivero