pax_global_header00006660000000000000000000000064147075326030014521gustar00rootroot0000000000000052 comment=bf5861b1b3d1eb1b661a34ae71db1a2bf80ced5d flake8-comprehensions-3.16.0/000077500000000000000000000000001470753260300160145ustar00rootroot00000000000000flake8-comprehensions-3.16.0/.editorconfig000066400000000000000000000003451470753260300204730ustar00rootroot00000000000000# 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.16.0/.github/000077500000000000000000000000001470753260300173545ustar00rootroot00000000000000flake8-comprehensions-3.16.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000001311470753260300221460ustar00rootroot00000000000000This project follows [Django's Code of Conduct](https://www.djangoproject.com/conduct/). flake8-comprehensions-3.16.0/.github/FUNDING.yml000066400000000000000000000001111470753260300211620ustar00rootroot00000000000000tidelift: pypi/flake8-comprehensions custom: - "https://adamj.eu/books/" flake8-comprehensions-3.16.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001470753260300215375ustar00rootroot00000000000000flake8-comprehensions-3.16.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000000341470753260300235240ustar00rootroot00000000000000blank_issues_enabled: false flake8-comprehensions-3.16.0/.github/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000004111470753260300253770ustar00rootroot00000000000000name: 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.16.0/.github/ISSUE_TEMPLATE/issue.yml000066400000000000000000000015261470753260300234160ustar00rootroot00000000000000name: 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.16.0/.github/SECURITY.md000066400000000000000000000001011470753260300211350ustar00rootroot00000000000000Please report security issues directly over email to me@adamj.eu flake8-comprehensions-3.16.0/.github/dependabot.yml000066400000000000000000000002471470753260300222070ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: "/" groups: "GitHub Actions": patterns: - "*" schedule: interval: monthly flake8-comprehensions-3.16.0/.github/workflows/000077500000000000000000000000001470753260300214115ustar00rootroot00000000000000flake8-comprehensions-3.16.0/.github/workflows/main.yml000066400000000000000000000024521470753260300230630ustar00rootroot00000000000000name: CI on: push: branches: - main tags: - '**' pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-24.04 strategy: matrix: python-version: - 3.9 - '3.10' - '3.11' - '3.12' - '3.13' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install uv uses: astral-sh/setup-uv@v3 with: enable-cache: true cache-dependency-glob: tests/requirements/*.txt - name: Install dependencies run: uv pip install --system tox tox-uv - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) release: needs: [tests] if: success() && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-24.04 environment: release permissions: contents: read id-token: write steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v3 - name: Build run: uv build - uses: pypa/gh-action-pypi-publish@release/v1 flake8-comprehensions-3.16.0/.gitignore000066400000000000000000000000771470753260300200100ustar00rootroot00000000000000*.egg-info/ *.pyc /.coverage /.coverage.* /.tox /build/ /dist/ flake8-comprehensions-3.16.0/.pre-commit-config.yaml000066400000000000000000000026271470753260300223040ustar00rootroot00000000000000ci: autoupdate_schedule: monthly default_language_version: python: python3.12 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.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: 2.2.4 hooks: - id: pyproject-fmt - repo: https://github.com/tox-dev/tox-ini-fmt rev: 1.4.1 hooks: - id: tox-ini-fmt - repo: https://github.com/rstcheck/rstcheck rev: v6.2.4 hooks: - id: rstcheck additional_dependencies: - tomli==2.0.1 - repo: https://github.com/asottile/pyupgrade rev: v3.17.0 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.8.0 hooks: - id: black - repo: https://github.com/adamchainz/blacken-docs rev: 1.18.0 hooks: - id: blacken-docs additional_dependencies: - black==23.1.0 - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: - id: isort name: isort (python) - repo: https://github.com/PyCQA/flake8 rev: 7.1.1 hooks: - id: flake8 additional_dependencies: - flake8-bugbear - flake8-comprehensions - flake8-logging - flake8-tidy-imports - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.11.2 hooks: - id: mypy flake8-comprehensions-3.16.0/CHANGELOG.rst000066400000000000000000000203631470753260300200410ustar00rootroot00000000000000========= Changelog ========= 3.16.0 (2024-10-27) ------------------- * Drop Python 3.8 support. * Support Python 3.13. 3.15.0 (2024-06-29) ------------------- * Add rule C420 to check for dict comprehensions with constant values, encouraging replacement with ``dict.fromkeys()``. Thanks to Tom Kuson in `PR #553 `__. 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.16.0/HISTORY.rst000066400000000000000000000001201470753260300177000ustar00rootroot00000000000000See https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst flake8-comprehensions-3.16.0/LICENSE000066400000000000000000000020551470753260300170230ustar00rootroot00000000000000MIT 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.16.0/MANIFEST.in000066400000000000000000000001631470753260300175520ustar00rootroot00000000000000prune tests include CHANGELOG.rst include LICENSE include pyproject.toml include README.rst include src/*/py.typed flake8-comprehensions-3.16.0/README.rst000066400000000000000000000251661470753260300175150ustar00rootroot00000000000000===================== flake8-comprehensions ===================== .. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-comprehensions/main.yml.svg?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. ---- **Linting a Django project?** Check out my book `Boost Your Django DX `__ which covers Flake8 and many other code quality tools. ---- Requirements ============ Python 3.9 to 3.13 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. 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)`` C420: Unnecessary dict comprehension - rewrite using dict.fromkeys(). ---------------------------------------------------------------------- It's unnecessary to use a dict comprehension to build a dict with all values set to the same constant. Use ``dict.fromkeys()`` instead, which is faster. For example: * Rewrite ``{x: 1 for x in iterable}`` as ``dict.fromkeys(iterable, 1)`` * Rewrite ``{x: None for x in iterable}`` as ``dict.fromkeys(iterable)`` flake8-comprehensions-3.16.0/pyproject.toml000066400000000000000000000035041470753260300207320ustar00rootroot00000000000000[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools", ] [project] name = "flake8-comprehensions" version = "3.16.0" description = "A flake8 plugin to help you write better list/set/dict comprehensions." readme = "README.rst" keywords = [ "comprehensions", "dict comprehension", "flake8", "list comprehension", "set comprehension", ] authors = [ { name = "Adam Johnson", email = "me@adamj.eu" }, ] requires-python = ">=3.9" classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: Flake8", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Typing :: Typed", ] dependencies = [ "flake8!=3.2,>=3", ] urls = { Changelog = "https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst", Funding = "https://adamj.eu/books/", Repository = "https://github.com/adamchainz/flake8-comprehensions" } entry-points."flake8.extension".C4 = "flake8_comprehensions:ComprehensionChecker" [tool.isort] add_imports = [ "from __future__ import annotations", ] force_single_line = true profile = "black" [tool.pyproject-fmt] max_supported_python = "3.13" [tool.pytest.ini_options] addopts = """\ --strict-config --strict-markers """ xfail_strict = true [tool.mypy] enable_error_code = [ "ignore-without-code", "redundant-expr", "truthy-bool", ] mypy_path = "src/" namespace_packages = false strict = true warn_unreachable = true [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [tool.rstcheck] report_level = "ERROR" flake8-comprehensions-3.16.0/src/000077500000000000000000000000001470753260300166035ustar00rootroot00000000000000flake8-comprehensions-3.16.0/src/flake8_comprehensions/000077500000000000000000000000001470753260300230715ustar00rootroot00000000000000flake8-comprehensions-3.16.0/src/flake8_comprehensions/__init__.py000066400000000000000000000404311470753260300252040ustar00rootroot00000000000000from __future__ import annotations import ast from collections.abc import Generator from importlib.metadata import version from typing import Any 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." ), "C420": ( "C420 Unnecessary {type} comprehension - rewrite using dict.fromkeys()." ), } def run(self) -> Generator[tuple[int, int, str, type[Any]]]: # 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 ): if ( 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), ) elif ( isinstance(node, ast.DictComp) and isinstance(node.key, ast.Name) and isinstance(node.value, ast.Constant) and isinstance(node.generators[0].target, ast.Name) and node.key.id == node.generators[0].target.id ): yield ( node.lineno, node.col_offset, self.messages["C420"].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.16.0/src/flake8_comprehensions/py.typed000066400000000000000000000000001470753260300245560ustar00rootroot00000000000000flake8-comprehensions-3.16.0/tests/000077500000000000000000000000001470753260300171565ustar00rootroot00000000000000flake8-comprehensions-3.16.0/tests/__init__.py000066400000000000000000000000001470753260300212550ustar00rootroot00000000000000flake8-comprehensions-3.16.0/tests/requirements/000077500000000000000000000000001470753260300217015ustar00rootroot00000000000000flake8-comprehensions-3.16.0/tests/requirements/compile.py000077500000000000000000000014421470753260300237070ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import annotations import os import subprocess import sys from functools import partial from pathlib import Path if __name__ == "__main__": os.chdir(Path(__file__).parent) common_args = [ "uv", "pip", "compile", "--quiet", "--generate-hashes", "requirements.in", *sys.argv[1:], ] run = partial(subprocess.run, check=True) run([*common_args, "--python", "3.9", "--output-file", "py39.txt"]) run([*common_args, "--python", "3.10", "--output-file", "py310.txt"]) run([*common_args, "--python", "3.11", "--output-file", "py311.txt"]) run([*common_args, "--python", "3.12", "--output-file", "py312.txt"]) run([*common_args, "--python", "3.13", "--output-file", "py313.txt"]) flake8-comprehensions-3.16.0/tests/requirements/py310.txt000066400000000000000000000052341470753260300233220ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --generate-hashes requirements.in --python 3.10 --output-file py310.txt exceptiongroup==1.2.2 \ --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \ --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc # via pytest flake8==7.1.1 \ --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # 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==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via pytest pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via pytest pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ --hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521 # via flake8 pyflakes==3.2.0 \ --hash=sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f \ --hash=sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a # via flake8 pytest==8.3.2 \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.5.0 \ --hash=sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e \ --hash=sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9 # via -r requirements.in pytest-randomly==3.15.0 \ --hash=sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6 \ --hash=sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047 # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via pytest flake8-comprehensions-3.16.0/tests/requirements/py311.txt000066400000000000000000000044031470753260300233200ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --generate-hashes requirements.in --python 3.11 --output-file py311.txt flake8==7.1.1 \ --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # 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==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via pytest pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via pytest pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ --hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521 # via flake8 pyflakes==3.2.0 \ --hash=sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f \ --hash=sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a # via flake8 pytest==8.3.2 \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.5.0 \ --hash=sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e \ --hash=sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9 # via -r requirements.in pytest-randomly==3.15.0 \ --hash=sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6 \ --hash=sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047 # via -r requirements.in flake8-comprehensions-3.16.0/tests/requirements/py312.txt000066400000000000000000000044031470753260300233210ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --generate-hashes requirements.in --python 3.12 --output-file py312.txt flake8==7.1.1 \ --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # 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==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via pytest pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via pytest pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ --hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521 # via flake8 pyflakes==3.2.0 \ --hash=sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f \ --hash=sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a # via flake8 pytest==8.3.2 \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.5.0 \ --hash=sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e \ --hash=sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9 # via -r requirements.in pytest-randomly==3.15.0 \ --hash=sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6 \ --hash=sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047 # via -r requirements.in flake8-comprehensions-3.16.0/tests/requirements/py313.txt000066400000000000000000000044031470753260300233220ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --generate-hashes requirements.in --python 3.13 --output-file py313.txt flake8==7.1.1 \ --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # 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==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via pytest pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via pytest pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ --hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521 # via flake8 pyflakes==3.2.0 \ --hash=sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f \ --hash=sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a # via flake8 pytest==8.3.2 \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.5.0 \ --hash=sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e \ --hash=sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9 # via -r requirements.in pytest-randomly==3.15.0 \ --hash=sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6 \ --hash=sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047 # via -r requirements.in flake8-comprehensions-3.16.0/tests/requirements/py39.txt000066400000000000000000000061141470753260300232500ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --generate-hashes requirements.in --python 3.9 --output-file py39.txt exceptiongroup==1.2.2 \ --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \ --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc # via pytest flake8==7.1.1 \ --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # via pytest-flake8-path importlib-metadata==8.4.0 \ --hash=sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1 \ --hash=sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5 # 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==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via pytest pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via pytest pycodestyle==2.12.1 \ --hash=sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3 \ --hash=sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521 # via flake8 pyflakes==3.2.0 \ --hash=sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f \ --hash=sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a # via flake8 pytest==8.3.2 \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via # -r requirements.in # pytest-flake8-path # pytest-randomly pytest-flake8-path==1.5.0 \ --hash=sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e \ --hash=sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9 # via -r requirements.in pytest-randomly==3.15.0 \ --hash=sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6 \ --hash=sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047 # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via pytest zipp==3.20.1 \ --hash=sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064 \ --hash=sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b # via importlib-metadata flake8-comprehensions-3.16.0/tests/requirements/requirements.in000066400000000000000000000000521470753260300247510ustar00rootroot00000000000000pytest pytest-flake8-path pytest-randomly flake8-comprehensions-3.16.0/tests/test_flake8_comprehensions.py000066400000000000000000000664101470753260300250640ustar00rootroot00000000000000from __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: [] 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 @pytest.mark.parametrize( "code", [ "{elt: elt * 2 for elt in range(5)}", "{elt: [] for elt in foo}", "{elt: {1, 2, 3} for elt in ['a', 'b', 'c']}", "{elt: some_func() for elt in ['a', 'b', 'c']}", "{elt: SomeClass() for elt in ['a', 'b', 'c']}", ], ) def test_C420_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", [ ( "{elt: None for elt in range(5)}", [ "./example.py:1:1: C420 Unnecessary dict comprehension - " + "rewrite using dict.fromkeys()." ], ), ( "{elt: 1 for elt in foo}", [ "./example.py:1:1: C420 Unnecessary dict comprehension - " + "rewrite using dict.fromkeys()." ], ), ( "{elt: 'value' for elt in ['a', 'b', 'c']}", [ "./example.py:1:1: C420 Unnecessary dict comprehension - " + "rewrite using dict.fromkeys()." ], ), ( "{elt: True for elt in some_func()}", [ "./example.py:1:1: C420 Unnecessary dict comprehension - " + "rewrite using dict.fromkeys()." ], ), ], ) def test_C420_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.16.0/tox.ini000066400000000000000000000006551470753260300173350ustar00rootroot00000000000000[tox] requires = tox>=4.2 env_list = py{313, 312, 311, 310, 39} [testenv] package = wheel wheel_build_env = .pkg deps = -r tests/requirements/{envname}.txt set_env = PYTHONDEVMODE = 1 commands = python \ -W error::ResourceWarning \ -W error::DeprecationWarning \ -W error::PendingDeprecationWarning \ -m pytest {posargs:tests} [flake8] max-line-length = 88 extend-ignore = E203,E501